From 1ed3235e84a083240bb76bb545331fa27f7c8ae4 Mon Sep 17 00:00:00 2001 From: Shrief Gabr Date: Thu, 14 Mar 2024 14:22:46 +0200 Subject: [PATCH 001/202] EffectComposer: Toggle "Assign to.." button based on component selection Task-number: QDS-12148 Change-Id: I1f44df7c6c027c36c09f7c4d74e4dd609542c5b8 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../EffectComposerTopBar.qml | 3 ++- .../effectcomposer/effectcomposermodel.cpp | 13 +++++++++++++ src/plugins/effectcomposer/effectcomposermodel.h | 6 ++++++ src/plugins/effectcomposer/effectcomposerview.cpp | 15 +++++++++++++++ src/plugins/effectcomposer/effectcomposerview.h | 3 +++ 5 files changed, 39 insertions(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml index d5ac4461c48..1912592f2fc 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml @@ -58,7 +58,8 @@ Rectangle { style: StudioTheme.Values.viewBarButtonStyle buttonIcon: StudioTheme.Constants.assignTo_medium tooltip: qsTr("Assign current composition to selected item") - enabled: root.backendModel ? root.backendModel.isEnabled + enabled: root.backendModel ? root.backendModel.hasValidTarget + && root.backendModel.isEnabled && root.backendModel.currentComposition !== "" : false diff --git a/src/plugins/effectcomposer/effectcomposermodel.cpp b/src/plugins/effectcomposer/effectcomposermodel.cpp index 5a5ad5718c2..092df2b3e4a 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.cpp +++ b/src/plugins/effectcomposer/effectcomposermodel.cpp @@ -1729,6 +1729,19 @@ void EffectComposerModel::setIsEnabled(bool enabled) emit isEnabledChanged(); } +bool EffectComposerModel::hasValidTarget() const +{ + return m_hasValidTarget; +} + +void EffectComposerModel::setHasValidTarget(bool validTarget) +{ + if (m_hasValidTarget == validTarget) + return; + m_hasValidTarget = validTarget; + emit hasValidTargetChanged(); +} + QString EffectComposerModel::getQmlImagesString(bool localFiles) { QString imagesString; diff --git a/src/plugins/effectcomposer/effectcomposermodel.h b/src/plugins/effectcomposer/effectcomposermodel.h index bd4040efc27..3dca41abf9e 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.h +++ b/src/plugins/effectcomposer/effectcomposermodel.h @@ -48,6 +48,7 @@ class EffectComposerModel : public QAbstractListModel Q_PROPERTY(bool hasUnsavedChanges MEMBER m_hasUnsavedChanges WRITE setHasUnsavedChanges NOTIFY hasUnsavedChangesChanged) Q_PROPERTY(bool shadersUpToDate READ shadersUpToDate WRITE setShadersUpToDate NOTIFY shadersUpToDateChanged) Q_PROPERTY(bool isEnabled READ isEnabled WRITE setIsEnabled NOTIFY isEnabledChanged) + Q_PROPERTY(bool hasValidTarget READ hasValidTarget WRITE setHasValidTarget NOTIFY hasValidTargetChanged) Q_PROPERTY(QString currentComposition READ currentComposition WRITE setCurrentComposition NOTIFY currentCompositionChanged) public: @@ -77,6 +78,9 @@ public: bool isEnabled() const; void setIsEnabled(bool enabled); + bool hasValidTarget() const; + void setHasValidTarget(bool validTarget); + QString fragmentShader() const; void setFragmentShader(const QString &newFragmentShader); @@ -110,6 +114,7 @@ signals: void effectErrorChanged(); void shadersUpToDateChanged(); void isEnabledChanged(); + void hasValidTargetChanged(); void shadersBaked(); void currentCompositionChanged(); void nodesChanged(); @@ -210,6 +215,7 @@ private: QString m_qmlComponentString; bool m_loadComponentImages = true; bool m_isEnabled = true; + bool m_hasValidTarget = false; QString m_currentComposition; QTimer m_rebakeTimer; diff --git a/src/plugins/effectcomposer/effectcomposerview.cpp b/src/plugins/effectcomposer/effectcomposerview.cpp index e7a879bd8b0..ed50c389a83 100644 --- a/src/plugins/effectcomposer/effectcomposerview.cpp +++ b/src/plugins/effectcomposer/effectcomposerview.cpp @@ -105,4 +105,19 @@ void EffectComposerView::modelAboutToBeDetached(QmlDesigner::Model *model) AbstractView::modelAboutToBeDetached(model); } +void EffectComposerView::selectedNodesChanged(const QList & selectedNodeList, + const QList & /*lastSelectedNodeList*/) +{ + bool hasValidTarget = false; + + for (const QmlDesigner::ModelNode &node : selectedNodeList) { + if (node.metaInfo().isQtQuickItem()) { + hasValidTarget = true; + break; + } + } + + m_widget->effectComposerModel()->setHasValidTarget(hasValidTarget); +} + } // namespace EffectComposer diff --git a/src/plugins/effectcomposer/effectcomposerview.h b/src/plugins/effectcomposer/effectcomposerview.h index c7a381cb7db..49a7b32621b 100644 --- a/src/plugins/effectcomposer/effectcomposerview.h +++ b/src/plugins/effectcomposer/effectcomposerview.h @@ -4,6 +4,7 @@ #pragma once #include "abstractview.h" +#include "modelnode.h" #include @@ -35,6 +36,8 @@ public: // AbstractView void modelAttached(QmlDesigner::Model *model) override; void modelAboutToBeDetached(QmlDesigner::Model *model) override; + void selectedNodesChanged(const QList &selectedNodeList, + const QList &lastSelectedNodeList) override; private: void customNotification(const AbstractView *view, const QString &identifier, From 3031bbfd07a5188221a469a8e9263693f08c5c91 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 15 Mar 2024 14:48:03 +0200 Subject: [PATCH 002/202] QmlDesigner: Fix assert Url properties should be set as urls to avoid asserts. Change-Id: I990e731f4555943ce35443226ab053286c58bd58 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- src/plugins/qmldesigner/components/createtexture.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/createtexture.cpp b/src/plugins/qmldesigner/components/createtexture.cpp index b6e99ae972c..56056e3fd2f 100644 --- a/src/plugins/qmldesigner/components/createtexture.cpp +++ b/src/plugins/qmldesigner/components/createtexture.cpp @@ -17,6 +17,7 @@ #include #include +#include namespace QmlDesigner { @@ -94,7 +95,7 @@ ModelNode CreateTexture::createTextureFromImage(const Utils::FilePath &assetPat newTexNode.setIdWithoutRefactoring(m_view->model()->generateNewId(assetPath.baseName())); VariantProperty sourceProp = newTexNode.variantProperty("source"); - sourceProp.setValue(textureSource); + sourceProp.setValue(QUrl(textureSource)); matLib.defaultNodeListProperty().reparentHere(newTexNode); } From 244752f6af93fa7cf317642270061e23309f3ade Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Fri, 15 Mar 2024 16:20:26 +0200 Subject: [PATCH 003/202] QmlDesigner: Hide Flags combobox's popup when opening the cog menu Otherwise the cog menu appears below the popup Change-Id: If3b541bd98b2d771687373c9259ad017349d4215 Reviewed-by: Miikka Heikkinen --- .../imports/HelperWidgets/FlagsComboBox.qml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlagsComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlagsComboBox.qml index 46c98f25c50..edf17374e22 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlagsComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlagsComboBox.qml @@ -132,6 +132,8 @@ StudioControls.CustomComboBox { ExtendedFunctionLogic { id: extFuncLogic backendValue: root.backendValue + + onMenuVisibleChanged: root.popup.visible = false } actionIndicator.icon.color: extFuncLogic.color From 6a5056dc221f79f72abdadbeeb7a2cca52026fa9 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 18 Mar 2024 10:48:46 +0100 Subject: [PATCH 004/202] QmlDesigner: Hotfix keep qml cache enabled Task-number: QDS-12264 Change-Id: I4fb58331141c72eedd74b3d7ac8fd8462ae3158f Reviewed-by: Tim Jenssen --- src/plugins/qmldesigner/puppetenvironmentbuilder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp b/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp index 6e8a2b60245..7819c6b49d6 100644 --- a/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp +++ b/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp @@ -126,7 +126,7 @@ void PuppetEnvironmentBuilder::addRendering() const { m_environment.set("QML_BAD_GUI_RENDER_LOOP", "true"); m_environment.set("QML_PUPPET_MODE", "true"); - m_environment.set("QML_DISABLE_DISK_CACHE", "true"); + //m_environment.set("QML_DISABLE_DISK_CACHE", "true"); m_environment.set("QMLPUPPET_RENDER_EFFECTS", "true"); if (!m_environment.hasKey("QT_SCREEN_SCALE_FACTORS") && !m_environment.hasKey("QT_SCALE_FACTOR")) m_environment.set("QT_AUTO_SCREEN_SCALE_FACTOR", "1"); From 5159d7ebe441bbb658270f228ab9f99fc1952d9b Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 14 Mar 2024 21:27:31 +0100 Subject: [PATCH 005/202] Nanotrace: Support enumeration arguments Change-Id: I75f7576f28231fc35269cbb3bb46654739cf7f18 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen Reviewed-by: --- src/libs/nanotrace/nanotracehr.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index 7856ea3582f..0698c61e059 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -153,6 +153,19 @@ void convertToString(String &string, double number) string.append(Utils::SmallString::number(number)); } +template +constexpr std::underlying_type_t to_underlying(Enumeration enumeration) noexcept +{ + static_assert(std::is_enum_v, "to_underlying expect an enumeration"); + return static_cast>(enumeration); +} + +template>> +void convertToString(String &string, Enumeration enumeration) +{ + string.append(Utils::SmallString::number(to_underlying(enumeration))); +} + template void convertToString(String &string, const QString &text) { From 462f46db30eade04b477110b99190cd922d81ca2 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 12 Mar 2024 11:57:22 +0100 Subject: [PATCH 006/202] NanoTrace: Add missing category constructor The constructor for the category was missing. Otherwise the CTAD is not working. Change-Id: I4d4c2aaba8915fbbade0b8ce4f1310046d748cd1 Reviewed-by: Thomas Hartmann Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- src/libs/nanotrace/nanotracehr.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index 0698c61e059..23e5896df84 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -1332,6 +1332,8 @@ private: Internal::appendArguments(traceEvent.arguments, std::forward(arguments)...); } + CategoryFunctionPointer self() { return m_self; } + private: StringType m_name; EnabledEventQueue &m_eventQueue; @@ -1446,6 +1448,11 @@ public: } } + template + [[nodiscard]] Tracer(ArgumentType name, Category &category, Arguments &&...arguments) + : Tracer(std::move(name), category.self(), std::forward(arguments)...) + {} + Tracer(const Tracer &) = delete; Tracer &operator=(const Tracer &) = delete; Tracer(Tracer &&other) noexcept = delete; @@ -1479,7 +1486,7 @@ private: void sendTrace(Arguments &&...arguments) { if (m_name.size()) { - auto category = m_category(); + auto &category = m_category(); if (category.isEnabled == IsEnabled::Yes) { auto duration = Clock::now() - m_start; auto &traceEvent = getTraceEvent(category.eventQueue()); From 27cfd2e240322fcdd60257ab52f1a737d6fe2809 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 12 Mar 2024 12:00:55 +0100 Subject: [PATCH 007/202] Sqlite: Add convertToString to Sqlite id Change-Id: I52d9b6860d24d715fdd6b584ffc2f8345f6b99c4 Reviewed-by: Tim Jenssen Reviewed-by: Reviewed-by: Qt CI Patch Build Bot --- src/libs/nanotrace/nanotracehr.cpp | 5 +---- src/libs/nanotrace/nanotracehr.h | 4 ++-- src/libs/sqlite/CMakeLists.txt | 2 +- src/libs/sqlite/sqliteids.h | 8 ++++++++ 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/libs/nanotrace/nanotracehr.cpp b/src/libs/nanotrace/nanotracehr.cpp index 07d2ff95ac1..2dd1806aaf8 100644 --- a/src/libs/nanotrace/nanotracehr.cpp +++ b/src/libs/nanotrace/nanotracehr.cpp @@ -103,7 +103,6 @@ std::string getThreadName() } // namespace -namespace Internal { template void convertToString(String &string, const QImage &image) { @@ -132,14 +131,12 @@ void convertToString(String &string, const QImage &image) return "alpha premultiplied"sv; })))); - Internal::convertToString(string, dict); + convertToString(string, dict); } template NANOTRACE_EXPORT void convertToString(std::string &string, const QImage &image); template NANOTRACE_EXPORT void convertToString(ArgumentsString &string, const QImage &image); -} // namespace Internal - template void flushEvents(const Utils::span events, std::thread::id threadId, diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index 23e5896df84..9d16b1bb635 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -80,8 +80,6 @@ struct IsDictonary inline constexpr IsDictonary isDictonary; -namespace Internal { - template void convertToString(String &string, std::string_view text) { @@ -251,6 +249,8 @@ void convertToString(String &string, const Container &container) string.append("]"); } +namespace Internal { + template String toArguments(Arguments &&...arguments) { diff --git a/src/libs/sqlite/CMakeLists.txt b/src/libs/sqlite/CMakeLists.txt index 2c7e1ebbf31..2943d1e77ac 100644 --- a/src/libs/sqlite/CMakeLists.txt +++ b/src/libs/sqlite/CMakeLists.txt @@ -31,7 +31,7 @@ endif() add_qtc_library(Sqlite PROPERTIES AUTOMOC OFF AUTOUIC OFF - DEPENDS Qt::Core Threads::Threads ${CMAKE_DL_LIBS} SqliteInternal + DEPENDS Qt::Core Threads::Threads ${CMAKE_DL_LIBS} SqliteInternal Nanotrace INCLUDES ../3rdparty/sqlite PUBLIC_INCLUDES diff --git a/src/libs/sqlite/sqliteids.h b/src/libs/sqlite/sqliteids.h index d64e4d9645a..75490ecccd9 100644 --- a/src/libs/sqlite/sqliteids.h +++ b/src/libs/sqlite/sqliteids.h @@ -5,6 +5,7 @@ #include +#include #include #include @@ -77,6 +78,12 @@ auto toIntegers(const Container &container) return Utils::span{data, container.size()}; } +template +void convertToString(String &string, BasicId id) +{ + NanotraceHR::convertToString(string, id.internalId()); +} + } // namespace Sqlite namespace std { @@ -88,4 +95,5 @@ struct hash> return std::hash{}(id.internalId()); } }; + } // namespace std From d632a7c82e251abc35b0313e69861b7c85531698 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 12 Mar 2024 12:14:23 +0100 Subject: [PATCH 008/202] Nanotrace: If the process is terminated flush the event queues That terminate handler is only installed if any event queue is activated. That is only the case if you compile with a special option. Change-Id: I4d8661c0fa11d8bb14b49b7b963471f626b3b950 Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- src/libs/nanotrace/nanotracehr.h | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index 9d16b1bb635..f9ff55419ee 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -436,7 +437,12 @@ class EventQueueTracker using Queue = EventQueue; public: - EventQueueTracker() = default; + EventQueueTracker() + { + terminateHandler = std::get_terminate(); + + std::set_terminate([]() { EventQueueTracker::get().terminate(); }); + } EventQueueTracker(const EventQueueTracker &) = delete; EventQueueTracker(EventQueueTracker &&) = delete; EventQueueTracker &operator=(const EventQueueTracker &) = delete; @@ -469,9 +475,26 @@ public: return tracker; } +private: + void terminate() + { + flushAll(); + if (terminateHandler) + terminateHandler(); + } + + void flushAll() + { + std::lock_guard lock{mutex}; + + for (auto queue : queues) + queue->flush(); + } + private: std::mutex mutex; std::vector queues; + std::terminate_handler terminateHandler = nullptr; }; } // namespace Internal From f8d74b5a052a988621f6af5fe72a0f61ffe82450 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 12 Mar 2024 17:45:12 +0100 Subject: [PATCH 009/202] QmlDesigner: Avoid dangling pointer to the stack Pointer to temporaries create dangling pointer. So we have to copy the tuples. The leafes still only contain references. Change-Id: I37900be569283324178e223829f913fb4ccba16e Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- src/libs/nanotrace/nanotracehr.h | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index f9ff55419ee..47823748d25 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -99,7 +99,7 @@ template void convertToString(String &string, const char (&text)[size]) { string.append(R"(")"); - string.append(text); + string.append(std::string_view{text, size - 1}); string.append(R"(")"); } @@ -230,7 +230,7 @@ void convertDictonaryToString(String &string, const IsDictonary &, Entries &...e } template -void convertToString(String &string, const std::tuple &dictonary) +void convertToString(String &string, const std::tuple &dictonary) { std::apply([&](auto &&...entries) { convertDictonaryToString(string, entries...); }, dictonary); } @@ -296,6 +296,12 @@ template } // namespace Internal +template +void convertToString(Key &&key, const std::tuple &value) +{ + return std::make_tuple(std::forward(key), value); +} + template auto keyValue(Key &&key, Value &&value) { @@ -305,13 +311,13 @@ auto keyValue(Key &&key, Value &&value) template auto dictonary(Entries &&...entries) { - return std::forward_as_tuple(isDictonary, std::forward(entries)...); + return std::make_tuple(isDictonary, std::forward(entries)...); } template auto array(Entries &&...entries) { - return std::forward_as_tuple(isArray, std::forward(entries)...); + return std::make_tuple(isArray, std::forward(entries)...); } enum class IsFlow : std::size_t { No = 0, Out = 1 << 0, In = 1 << 1, InOut = In | Out }; @@ -443,6 +449,7 @@ public: std::set_terminate([]() { EventQueueTracker::get().terminate(); }); } + EventQueueTracker(const EventQueueTracker &) = delete; EventQueueTracker(EventQueueTracker &&) = delete; EventQueueTracker &operator=(const EventQueueTracker &) = delete; From 0cd74fe22e00bff01bcd610af6bdf8c814b3959b Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 13 Mar 2024 13:38:00 +0100 Subject: [PATCH 010/202] QmlDesigner: avoid extra widgetInfo() call widgetInfo() should be cheap but people make expensive stuff inside the call. So we should try to avoid to many calls. Change-Id: I66b0a5431471732dca934fc03efb307e2617fd65 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann Reviewed-by: Tim Jenssen --- src/plugins/qmldesigner/designercore/model/abstractview.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp index 061ab8ae2b0..125c7195a8f 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp @@ -567,8 +567,10 @@ void AbstractView::disableWidget() void AbstractView::enableWidget() { - if (hasWidget() && widgetInfo().widgetFlags == DesignerWidgetFlags::DisableOnError) - widgetInfo().widget->setEnabled(true); + if (hasWidget()) { + if (auto info = widgetInfo(); info.widgetFlags == DesignerWidgetFlags::DisableOnError) + info.widget->setEnabled(true); + } } QString AbstractView::contextHelpId() const From 15d0ab5358c764355c0c9104f67346ab8dcb50c1 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 13 Mar 2024 18:27:12 +0100 Subject: [PATCH 011/202] QmlDesigner: Remove unneeded break Change-Id: I051bf442870ce1ec429565bcbf15068d22eb16a9 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- .../designercore/projectstorage/projectstorageupdater.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp index 62fcf310f65..72ddf3e37bf 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp @@ -602,7 +602,6 @@ auto ProjectStorageUpdater::parseTypeInfo(const Storage::Synchronization::Projec } case FileState::NotExists: throw CannotParseQmlTypesFile{}; - break; } return state; From 244904303d182e84bf01fd072a6b1491b614be67 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 13 Mar 2024 18:29:48 +0100 Subject: [PATCH 012/202] QmlDesigner: Add lite designer to qmldesigner project management Task-number: QDS-12102 Change-Id: I3abea3a5b32128db984a9b0c49b3044a42d9f72d Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- src/plugins/qmldesigner/CMakeLists.txt | 5 +- .../qmldesigner/qmldesignerprojectmanager.cpp | 65 ++++++++++++++----- 2 files changed, 52 insertions(+), 18 deletions(-) diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index b5b64bebbc8..ae954872d9d 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -8,7 +8,6 @@ if (APPLE) set(QmlDesignerPluginInstallPrefix "${IDE_PLUGIN_PATH}/QmlDesigner") endif() -add_compile_options("$<$:-Wno-error=maybe-uninitialized>") set(BUILD_NOT_DESIGNSTUDIO NOT ${BUILD_NOT_DESIGNSTUDIO}) option(QTC_USE_QML_DESIGNER_LITE "Use Qml Designer Lite" ${BUILD_NOT_DESIGNSTUDIO}) @@ -46,6 +45,10 @@ add_qtc_library(QmlDesignerUtils STATIC qmldesignerutils_global.h ) + +target_compile_options(QmlDesignerUtils PUBLIC $<$:-Wno-error=maybe-uninitialized>) +target_compile_options(QmlDesignerUtils PUBLIC $<$:-Wno-unneeded-internal-declaration>) + extend_qtc_library(QmlDesignerUtils CONDITION ENABLE_COMPILE_WARNING_AS_ERROR PROPERTIES COMPILE_WARNING_AS_ERROR ON diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp index 7e28849fbb3..f21e0f93fa1 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp @@ -41,6 +41,7 @@ #include #include +#include #include using namespace std::chrono; @@ -401,17 +402,16 @@ void collectQmldirPaths(const QString &path, QStringList &qmldirPaths) collectQmldirPaths(qmlPath(target).toString(), qmldirPaths); } -[[maybe_unused]] void qtQmldirPathsForLiteDesigner(::ProjectExplorer::Target *target, - QStringList &qmldirPaths) +[[maybe_unused]] void qtQmldirPathsForLiteDesigner(QStringList &qmldirPaths) { if constexpr (useProjectStorage()) { - auto qmlRootPath = qmlPath(target).toString(); + auto qmlRootPath = QLibraryInfo::path(QLibraryInfo::QmlImportsPath); collectQmldirPaths(qmlRootPath + "/QtQml", qmldirPaths); collectQmldirPaths(qmlRootPath + "/QtQuick", qmldirPaths); } } -QStringList directories(::ProjectExplorer::Target *target) +[[maybe_unused]] QStringList directories(::ProjectExplorer::Target *target) { if (!target) return {}; @@ -419,12 +419,8 @@ QStringList directories(::ProjectExplorer::Target *target) QStringList qmldirPaths; qmldirPaths.reserve(100); - if constexpr (isUsingQmlDesignerLite()) { - qtQmldirPathsForLiteDesigner(target, qmldirPaths); - } else { - qtQmldirPaths(target, qmldirPaths); - projectQmldirPaths(target, qmldirPaths); - } + qtQmldirPaths(target, qmldirPaths); + projectQmldirPaths(target, qmldirPaths); std::sort(qmldirPaths.begin(), qmldirPaths.end()); qmldirPaths.erase(std::unique(qmldirPaths.begin(), qmldirPaths.end()), qmldirPaths.end()); @@ -432,7 +428,20 @@ QStringList directories(::ProjectExplorer::Target *target) return qmldirPaths; } -QStringList qmlTypes(::ProjectExplorer::Target *target) +[[maybe_unused]] QStringList directoriesForLiteDesigner() +{ + QStringList qmldirPaths; + qmldirPaths.reserve(100); + + qtQmldirPathsForLiteDesigner(qmldirPaths); + + std::sort(qmldirPaths.begin(), qmldirPaths.end()); + qmldirPaths.erase(std::unique(qmldirPaths.begin(), qmldirPaths.end()), qmldirPaths.end()); + + return qmldirPaths; +} + +[[maybe_unused]] QStringList qmlTypes(::ProjectExplorer::Target *target) { if (!target) return {}; @@ -440,10 +449,26 @@ QStringList qmlTypes(::ProjectExplorer::Target *target) QStringList qmldirPaths; qmldirPaths.reserve(2); - const QString installDirectory = qmlPath(target).toString(); + const QString qmlRootPath = qmlPath(target).toString(); - qmldirPaths.append(installDirectory + "/builtins.qmltypes"); - qmldirPaths.append(installDirectory + "/jsroot.qmltypes"); + qmldirPaths.append(qmlRootPath + "/builtins.qmltypes"); + qmldirPaths.append(qmlRootPath + "/jsroot.qmltypes"); + + qmldirPaths.append( + Core::ICore::resourcePath("qmldesigner/projectstorage/fake.qmltypes").toString()); + + return qmldirPaths; +} + +[[maybe_unused]] QStringList qmlTypesForLiteDesigner() +{ + QStringList qmldirPaths; + qmldirPaths.reserve(2); + + const auto qmlRootPath = QLibraryInfo::path(QLibraryInfo::QmlImportsPath); + + qmldirPaths.append(qmlRootPath + "/builtins.qmltypes"); + qmldirPaths.append(qmlRootPath + "/jsroot.qmltypes"); qmldirPaths.append( Core::ICore::resourcePath("qmldesigner/projectstorage/fake.qmltypes").toString()); @@ -594,9 +619,15 @@ void QmlDesignerProjectManager::update() if (!m_projectData || !m_projectData->projectStorageData) return; - m_projectData->projectStorageData->updater.update(directories(m_projectData->activeTarget), - qmlTypes(m_projectData->activeTarget), - propertyEditorResourcesPath()); + if constexpr (isUsingQmlDesignerLite()) { + m_projectData->projectStorageData->updater.update(directoriesForLiteDesigner(), + qmlTypesForLiteDesigner(), + propertyEditorResourcesPath()); + } else { + m_projectData->projectStorageData->updater.update(directories(m_projectData->activeTarget), + qmlTypes(m_projectData->activeTarget), + propertyEditorResourcesPath()); + } } } // namespace QmlDesigner From 82b4d34c9c37bff4cdd8e01add13989a36b9d1b6 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 13 Mar 2024 19:14:45 +0100 Subject: [PATCH 013/202] Nanotrace: Fix dangling tuple The tuple are constructed on the stack, so we cannot hold references to it. Change-Id: Ia02bf21516c97eb67eca8fd56e68dbd10878ca9e Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- src/libs/nanotrace/nanotracehr.h | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index 47823748d25..99eb43d49a6 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -296,16 +296,13 @@ template } // namespace Internal -template -void convertToString(Key &&key, const std::tuple &value) -{ - return std::make_tuple(std::forward(key), value); -} - template -auto keyValue(Key &&key, Value &&value) +auto keyValue(const Key &key, Value &&value) { - return std::forward_as_tuple(std::forward(key), std::forward(value)); + if constexpr (std::is_lvalue_reference_v) + return std::tuple(key, value); + else + return std::tuple>(key, value); } template From 04cbe492675950831e0c06190d8cb72f1e470d93 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 13 Mar 2024 19:18:13 +0100 Subject: [PATCH 014/202] Nanotrace: Fix dangling reference As EventQueueData was derived from EventQueue the order of initialization was inverted. So we got dangling pointer. Merging EventQueueData into EventQueue fixes the dangling references. Change-Id: I3d9cee492304132fa612d6d19324656df597ec99 Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot Reviewed-by: --- src/libs/nanotrace/nanotracehr.cpp | 35 ++++++++-------- src/libs/nanotrace/nanotracehr.h | 42 ++++--------------- src/libs/sqlite/sqlitebasestatement.cpp | 8 ++-- .../projectstorage/projectstorage.cpp | 8 ++-- .../projectstorage/projectstorage.h | 3 +- .../tracing/qmldesignertracing.cpp | 15 ++++--- 6 files changed, 45 insertions(+), 66 deletions(-) diff --git a/src/libs/nanotrace/nanotracehr.cpp b/src/libs/nanotrace/nanotracehr.cpp index 2dd1806aaf8..083c9da69ca 100644 --- a/src/libs/nanotrace/nanotracehr.cpp +++ b/src/libs/nanotrace/nanotracehr.cpp @@ -73,17 +73,17 @@ void printEvent(std::ostream &out, const TraceEvent &event, qint64 processId, st out << "}"; } -void writeMetaEvent(TraceFile *file, std::string_view key, std::string_view value) +void writeMetaEvent(TraceFile &file, std::string_view key, std::string_view value) { - std::lock_guard lock{file->fileMutex}; - auto &out = file->out; + std::lock_guard lock{file.fileMutex}; + auto &out = file.out; if (out.is_open()) { - file->out << R"({"name":")" << key << R"(","ph":"M", "pid":)" - << getUnsignedIntegerHash(QCoreApplication::applicationPid()) << R"(,"tid":)" - << getUnsignedIntegerHash(std::this_thread::get_id()) << R"(,"args":{"name":")" - << value << R"("}})" - << ",\n"; + file.out << R"({"name":")" << key << R"(","ph":"M", "pid":)" + << getUnsignedIntegerHash(QCoreApplication::applicationPid()) << R"(,"tid":)" + << getUnsignedIntegerHash(std::this_thread::get_id()) << R"(,"args":{"name":")" + << value << R"("}})" + << ",\n"; } } @@ -145,8 +145,8 @@ void flushEvents(const Utils::span events, if (events.empty()) return; - std::lock_guard lock{eventQueue.file->fileMutex}; - auto &out = eventQueue.file->out; + std::lock_guard lock{eventQueue.file.fileMutex}; + auto &out = eventQueue.file.out; if (out.is_open()) { auto processId = QCoreApplication::applicationPid(); @@ -197,17 +197,17 @@ void finalizeFile(EnabledTraceFile &file) template void flushInThread(EnabledEventQueue &eventQueue) { - if (eventQueue.file->processing.valid()) - eventQueue.file->processing.wait(); + if (eventQueue.file.processing.valid()) + eventQueue.file.processing.wait(); auto flush = [&](const Utils::span &events, std::thread::id threadId) { flushEvents(events, threadId, eventQueue); }; - eventQueue.file->processing = std::async(std::launch::async, - flush, - eventQueue.currentEvents.subspan(0, eventQueue.eventsIndex), - eventQueue.threadId); + eventQueue.file.processing = std::async(std::launch::async, + flush, + eventQueue.currentEvents.subspan(0, eventQueue.eventsIndex), + eventQueue.threadId); eventQueue.currentEvents = eventQueue.currentEvents.data() == eventQueue.eventsOne.data() ? eventQueue.eventsTwo : eventQueue.eventsOne; @@ -220,10 +220,11 @@ template NANOTRACE_EXPORT void flushInThread( EnabledEventQueue &eventQueue); template -EventQueue::EventQueue(EnabledTraceFile *file) +EventQueue::EventQueue(EnabledTraceFile &file) : file{file} , threadId{std::this_thread::get_id()} { + setEventsSpans(*eventArrayOne.get(), *eventArrayTwo.get()); Internal::EventQueueTracker::get().addQueue(this); if (auto thread = QThread::currentThread()) { auto name = getThreadName(); diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index 99eb43d49a6..eee9bf2daee 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -430,6 +430,8 @@ class EventQueue { public: using IsActive = std::false_type; + + template EventQueue(TraceFile &) {} }; namespace Internal { @@ -506,11 +508,12 @@ template class EventQueue { using TraceEventsSpan = Utils::span; + using TraceEvents = std::array; public: using IsActive = std::true_type; - EventQueue(EnabledTraceFile *file); + EventQueue(EnabledTraceFile &file); ~EventQueue(); @@ -523,7 +526,9 @@ public: EventQueue &operator=(const EventQueue &) = delete; EventQueue &operator=(EventQueue &&) = delete; - EnabledTraceFile *file = nullptr; + EnabledTraceFile &file; + std::unique_ptr eventArrayOne = std::make_unique(); + std::unique_ptr eventArrayTwo = std::make_unique(); TraceEventsSpan eventsOne; TraceEventsSpan eventsTwo; TraceEventsSpan currentEvents; @@ -545,35 +550,6 @@ extern template class NANOTRACE_EXPORT_EXTERN_TEMPLATE EventQueue; -template -class EventQueueData : public EventQueue -{ -public: - using IsActive = std::true_type; - - EventQueueData(TraceFile &) {} -}; - -template -class EventQueueData - : public EventQueue -{ - using TraceEvents = std::array; - using Base = EventQueue; - -public: - using IsActive = std::true_type; - - EventQueueData(EnabledTraceFile &file) - : Base{&file} - { - Base::setEventsSpans(*eventsOne.get(), *eventsTwo.get()); - } - - std::unique_ptr eventsOne = std::make_unique(); - std::unique_ptr eventsTwo = std::make_unique(); -}; - template TraceEvent &getTraceEvent(EnabledEventQueue &eventQueue) { @@ -1517,7 +1493,7 @@ private: if (category.isEnabled == IsEnabled::Yes) { auto duration = Clock::now() - m_start; auto &traceEvent = getTraceEvent(category.eventQueue()); - traceEvent.name = m_name; + traceEvent.name = std::move(m_name); traceEvent.category = category.name(); traceEvent.time = m_start; traceEvent.duration = duration; @@ -1530,7 +1506,7 @@ private: std::forward( arguments)...); } else { - traceEvent.arguments = m_arguments; + traceEvent.arguments = std::move(m_arguments); } } } diff --git a/src/libs/sqlite/sqlitebasestatement.cpp b/src/libs/sqlite/sqlitebasestatement.cpp index 23466c0cea1..c340c52a0ed 100644 --- a/src/libs/sqlite/sqlitebasestatement.cpp +++ b/src/libs/sqlite/sqlitebasestatement.cpp @@ -31,13 +31,13 @@ using TraceFile = NanotraceHR::TraceFile; TraceFile traceFile{"sqlite.json"}; -thread_local NanotraceHR::EventQueueData - eventQueueData(traceFile); +thread_local NanotraceHR::EventQueue eventQueue( + traceFile); NanotraceHR::StringViewCategory &sqliteLowLevelCategory(); thread_local NanotraceHR::StringViewCategory sqliteLowLevelCategory_{ - "sqlite low level"_t, eventQueueData, sqliteLowLevelCategory}; + "sqlite low level"_t, eventQueue, sqliteLowLevelCategory}; NanotraceHR::StringViewCategory &sqliteLowLevelCategory() { @@ -45,7 +45,7 @@ NanotraceHR::StringViewCategory &sqliteLowLevelCategory() } thread_local NanotraceHR::StringViewCategory sqliteHighLevelCategory_{ - "sqlite high level"_t, eventQueueData, sqliteHighLevelCategory}; + "sqlite high level"_t, eventQueue, sqliteHighLevelCategory}; } // namespace NanotraceHR::StringViewCategory &sqliteHighLevelCategory() diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp index 3e493e87724..25588efd9c7 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp @@ -9,10 +9,12 @@ namespace QmlDesigner { -NanotraceHR::StringViewCategory &projectStorageCategory() +NanotraceHR::StringViewWithStringArgumentsCategory &projectStorageCategory() { - thread_local NanotraceHR::StringViewCategory - projectStorageCategory_{"project storage"_t, Tracing::eventQueue(), projectStorageCategory}; + thread_local NanotraceHR::StringViewWithStringArgumentsCategory + projectStorageCategory_{"project storage"_t, + Tracing::eventQueueWithStringArguments(), + projectStorageCategory}; return projectStorageCategory_; } diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index a770577a658..a43f26d38c6 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -37,7 +37,8 @@ constexpr NanotraceHR::Tracing projectStorageTracingStatus() #endif } -[[gnu::pure]] NanotraceHR::StringViewCategory &projectStorageCategory(); +[[gnu::pure]] NanotraceHR::StringViewWithStringArgumentsCategory & +projectStorageCategory(); template class ProjectStorage final : public ProjectStorageInterface diff --git a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp index d49c6156a65..c12f1daf682 100644 --- a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp +++ b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp @@ -19,24 +19,23 @@ TraceFile &traceFile() EventQueue &eventQueue() { - thread_local NanotraceHR::EventQueueData - stringViewEventQueueData(traceFile()); + thread_local NanotraceHR::EventQueue + stringViewEventQueue(traceFile()); - return stringViewEventQueueData; + return stringViewEventQueue; } EventQueueWithStringArguments &eventQueueWithStringArguments() { - thread_local NanotraceHR:: - EventQueueData - stringViewWithStringArgumentsEventQueueData(traceFile()); + thread_local NanotraceHR::EventQueue + stringViewWithStringArgumentsEventQueue(traceFile()); - return stringViewWithStringArgumentsEventQueueData; + return stringViewWithStringArgumentsEventQueue; } StringEventQueue &stringEventQueue() { - thread_local NanotraceHR::EventQueueData eventQueue( + thread_local NanotraceHR::EventQueue eventQueue( traceFile()); return eventQueue; From 402ac58d05bbb5e2427292db704959ef62e9f544 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 14 Mar 2024 10:02:37 +0100 Subject: [PATCH 015/202] QmlDesigner: Fix missing BreakTemplateDeclarations Change-Id: I00c042e5e0a04705e3c2773dca5499d9bfa32949 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen Reviewed-by: --- src/plugins/qmldesigner/.clang-format | 2 ++ tests/unit/.clang-format | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/plugins/qmldesigner/.clang-format b/src/plugins/qmldesigner/.clang-format index d3695ac2982..366f82f76f2 100644 --- a/src/plugins/qmldesigner/.clang-format +++ b/src/plugins/qmldesigner/.clang-format @@ -2,6 +2,7 @@ Language: Cpp AccessModifierOffset: -4 AlignEscapedNewlines: DontAlign AllowShortFunctionsOnASingleLine: Inline +AlwaysBreakTemplateDeclarations: true # use with clang 19 BinPackArguments: false BinPackParameters: false BraceWrapping: @@ -15,6 +16,7 @@ BreakBeforeBinaryOperators: All BreakBeforeBraces: Custom BreakConstructorInitializers: BeforeComma BreakInheritanceList: AfterComma +# BreakTemplateDeclarations: Yes # use with clang 19 ColumnLimit: 100 IncludeCategories: - Regex: 'Q.*' diff --git a/tests/unit/.clang-format b/tests/unit/.clang-format index d3695ac2982..366f82f76f2 100644 --- a/tests/unit/.clang-format +++ b/tests/unit/.clang-format @@ -2,6 +2,7 @@ Language: Cpp AccessModifierOffset: -4 AlignEscapedNewlines: DontAlign AllowShortFunctionsOnASingleLine: Inline +AlwaysBreakTemplateDeclarations: true # use with clang 19 BinPackArguments: false BinPackParameters: false BraceWrapping: @@ -15,6 +16,7 @@ BreakBeforeBinaryOperators: All BreakBeforeBraces: Custom BreakConstructorInitializers: BeforeComma BreakInheritanceList: AfterComma +# BreakTemplateDeclarations: Yes # use with clang 19 ColumnLimit: 100 IncludeCategories: - Regex: 'Q.*' From a08837e8bdb512d7f78beee8bdb39ceb772f4fc5 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 14 Mar 2024 11:00:06 +0100 Subject: [PATCH 016/202] Nanotrace: Use begin and end slices instead of duraction slice If an exception is thrown or the program is abortet there is still the begin of a slice. Because a duration slice is only wirtten at the end we get no slice. Change-Id: I8b05e9ae045517a503c2a6f00936b05715b812ca Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- src/libs/nanotrace/nanotracehr.h | 51 ++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index eee9bf2daee..458b2873055 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -1421,11 +1421,8 @@ class Tracer , flow{flow} , m_category{category} { - if (category().isEnabled == IsEnabled::Yes) { - Internal::appendArguments(m_arguments, - std::forward(arguments)...); - m_start = Clock::now(); - } + if (category().isEnabled == IsEnabled::Yes) + sendBeginTrace(std::forward(arguments)...); } public: @@ -1444,11 +1441,8 @@ public: : m_name{name} , m_category{category} { - if (category().isEnabled == IsEnabled::Yes) { - Internal::appendArguments(m_arguments, - std::forward(arguments)...); - m_start = Clock::now(); - } + if (category().isEnabled == IsEnabled::Yes) + sendBeginTrace(std::forward(arguments)...); } template @@ -1463,7 +1457,7 @@ public: TokenType createToken() { return {0, m_category}; } - ~Tracer() { sendTrace(); } + ~Tracer() { sendEndTrace(); } template Tracer beginDuration(ArgumentType name, Arguments &&...arguments) @@ -1480,42 +1474,55 @@ public: template void end(Arguments &&...arguments) { - sendTrace(std::forward(arguments)...); + sendEndTrace(std::forward(arguments)...); m_name = {}; } private: template - void sendTrace(Arguments &&...arguments) + void sendBeginTrace(Arguments &&...arguments) + { + auto &category = m_category(); + if (category.isEnabled == IsEnabled::Yes) { + auto &traceEvent = getTraceEvent(category.eventQueue()); + traceEvent.name = m_name; + traceEvent.category = category.name(); + traceEvent.bindId = m_bindId; + traceEvent.flow = flow; + traceEvent.type = 'B'; + if (sizeof...(arguments)) { + Internal::appendArguments(traceEvent.arguments, + std::forward(arguments)...); + } + traceEvent.time = Clock::now(); + } + } + + template + void sendEndTrace(Arguments &&...arguments) { if (m_name.size()) { auto &category = m_category(); if (category.isEnabled == IsEnabled::Yes) { - auto duration = Clock::now() - m_start; + auto end = Clock::now(); auto &traceEvent = getTraceEvent(category.eventQueue()); traceEvent.name = std::move(m_name); traceEvent.category = category.name(); - traceEvent.time = m_start; - traceEvent.duration = duration; + traceEvent.time = end; traceEvent.bindId = m_bindId; traceEvent.flow = flow; - traceEvent.type = 'X'; + traceEvent.type = 'E'; if (sizeof...(arguments)) { - m_arguments.clear(); Internal::appendArguments(traceEvent.arguments, std::forward( arguments)...); - } else { - traceEvent.arguments = std::move(m_arguments); } } } } private: - TimePoint m_start; StringType m_name; - ArgumentsStringType m_arguments; std::size_t m_bindId; IsFlow flow; CategoryFunctionPointer m_category; From 7448d44dc5e118d3930214874e0e6b4af2e3155a Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 14 Mar 2024 15:29:38 +0100 Subject: [PATCH 017/202] Nanotrace: Fix unsed arguments The arguments must be set because we otherwise access old data. Change-Id: I18e83297b80558b112af266bd50779bef8da9e94 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen Reviewed-by: --- src/libs/nanotrace/nanotracehr.h | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index 458b2873055..ac29bce9285 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -273,19 +273,19 @@ inline std::string_view toArguments(std::string_view arguments) } template -void appendArguments(String &eventArguments) +void setArguments(String &eventArguments) { eventArguments = {}; } template -void appendArguments(String &eventArguments, TracerLiteral arguments) +void setArguments(String &eventArguments, TracerLiteral arguments) { eventArguments = arguments; } template -[[maybe_unused]] void appendArguments(String &eventArguments, Arguments &&...arguments) +[[maybe_unused]] void setArguments(String &eventArguments, Arguments &&...arguments) { static_assert( !std::is_same_v, @@ -1286,7 +1286,7 @@ private: traceEvent.id = id; traceEvent.bindId = bindId; traceEvent.flow = flow; - Internal::appendArguments(traceEvent.arguments, std::forward(arguments)...); + Internal::setArguments(traceEvent.arguments, std::forward(arguments)...); traceEvent.time = Clock::now(); } @@ -1312,7 +1312,7 @@ private: traceEvent.id = id; traceEvent.bindId = bindId; traceEvent.flow = flow; - Internal::appendArguments(traceEvent.arguments, std::forward(arguments)...); + Internal::setArguments(traceEvent.arguments, std::forward(arguments)...); } template @@ -1332,7 +1332,7 @@ private: traceEvent.id = id; traceEvent.bindId = 0; traceEvent.flow = IsFlow::No; - Internal::appendArguments(traceEvent.arguments, std::forward(arguments)...); + Internal::setArguments(traceEvent.arguments, std::forward(arguments)...); } CategoryFunctionPointer self() { return m_self; } @@ -1490,10 +1490,8 @@ private: traceEvent.bindId = m_bindId; traceEvent.flow = flow; traceEvent.type = 'B'; - if (sizeof...(arguments)) { - Internal::appendArguments(traceEvent.arguments, - std::forward(arguments)...); - } + Internal::setArguments(traceEvent.arguments, + std::forward(arguments)...); traceEvent.time = Clock::now(); } } @@ -1512,11 +1510,8 @@ private: traceEvent.bindId = m_bindId; traceEvent.flow = flow; traceEvent.type = 'E'; - if (sizeof...(arguments)) { - Internal::appendArguments(traceEvent.arguments, - std::forward( - arguments)...); - } + Internal::setArguments(traceEvent.arguments, + std::forward(arguments)...); } } } From 44fe5a9c5c23d43ca97418de040ffaaa3c350755 Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Mon, 18 Mar 2024 10:47:55 +0100 Subject: [PATCH 018/202] Doc: Auto-generate navigation links based on "All Topics" This does not change what you see as values of \previouspage and \nextpage in the source files. However, when you build the docs, QDoc replaces those values with links to the next and previous page from the list in qtdesignstudio-toc.qdoc because that page contains "\title All Topics". So, with this change, you need to keep the list in "All Topics" up to date. Change-Id: I0996e383cbd60b7f4297524e4141a2d7af6fae13 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Mats Honkamaa --- doc/qtdesignstudio/config/qtdesignstudio.qdocconf | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/qtdesignstudio/config/qtdesignstudio.qdocconf b/doc/qtdesignstudio/config/qtdesignstudio.qdocconf index 72b38940f7c..95d1475cf15 100644 --- a/doc/qtdesignstudio/config/qtdesignstudio.qdocconf +++ b/doc/qtdesignstudio/config/qtdesignstudio.qdocconf @@ -120,4 +120,9 @@ macro.function = "\\fn" macro.QMLD = "Qt Design Studio" navigation.landingpage = "$IDE_DISPLAY_NAME Manual" + +# Auto-generate navigation linking based on "All Topics": +navigation.toctitles = "All Topics" +navigation.toctitles.inclusive = false + buildversion = "$IDE_DISPLAY_NAME Manual $QTC_VERSION" From dd3188664955e52ec07bc7a06c182f7491d12833 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 18 Mar 2024 16:37:00 +0200 Subject: [PATCH 019/202] QmlDesigner: Fix fly mode camera rotation direction Flip x-rotation direction when camera is upside down to keep camera rotation natural. Fixes: QDS-12186 Change-Id: Ia9f166cafa27a09a4dd5a72dbb8ec28f00239564 Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../qml2puppet/qml2puppet/editor3d/generalhelper.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp index 6886f7e41c5..0fb708c6ff3 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp @@ -187,8 +187,14 @@ QVector3D GeneralHelper::rotateCamera(QQuick3DCamera *camera, const QPointF &ang if (qAbs(angles.y()) > 0.001f) camera->rotate(angles.y(), QVector3D(1.f, 0.f, 0.f), QQuick3DNode::LocalSpace); // Rotation around Y-axis is done in scene space to keep horizon level - if (qAbs(angles.x()) > 0.001f) - camera->rotate(angles.x(), QVector3D(0.f, 1.f, 0.f), QQuick3DNode::SceneSpace); + if (qAbs(angles.x()) > 0.001f) { + // Since we are rotating in scene space, adjust direction according to camera up-vector + float angle = angles.x(); + if (camera->up().y() <= 0) + angle = -angle; + + camera->rotate(angle, QVector3D(0.f, 1.f, 0.f), QQuick3DNode::SceneSpace); + } QMatrix4x4 m = camera->sceneTransform(); const float *dataPtr(m.data()); From d114074afd22026592541fe5c80453e6556ad33c Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 19 Mar 2024 16:35:50 +0100 Subject: [PATCH 020/202] QmlDesigner: Workaround issues with QJSValue in Qt 6.7.x This forks code from Qt to fix an issue with JSValue. We must skip QJSValues when iterating properties since copying or constructing the QVariant crashes. Task-number: QDS-12263 Change-Id: I8db701cee94dccd7fe254c9473dc25b87f8a04f8 Reviewed-by: Tim Jenssen --- .../qmlprivategate/qmlprivategate.cpp | 499 +++++++++++++++++- 1 file changed, 490 insertions(+), 9 deletions(-) diff --git a/src/tools/qml2puppet/qmlprivategate/qmlprivategate.cpp b/src/tools/qml2puppet/qmlprivategate/qmlprivategate.cpp index 8abfed59160..9d390714bb9 100644 --- a/src/tools/qml2puppet/qmlprivategate/qmlprivategate.cpp +++ b/src/tools/qml2puppet/qmlprivategate/qmlprivategate.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -35,16 +36,484 @@ #include #include #include +#include #ifdef QUICK3D_MODULE #include #include #endif + + namespace QmlDesigner { namespace Internal { +static void addToPropertyNameListIfNotBlackListed(QQuickDesignerSupport::PropertyNameList *propertyNameList, + const QQuickDesignerSupport::PropertyName &propertyName) +{ + if (!QQuickDesignerSupportProperties::isPropertyBlackListed(propertyName)) + propertyNameList->append(propertyName); +} + +static QQuickDesignerSupport::PropertyNameList propertyNameListForWritablePropertiesInternal(QObject *object, + const QQuickDesignerSupport::PropertyName &baseName, + QObjectList *inspectedObjects, + int depth = 0) +{ + QQuickDesignerSupport::PropertyNameList propertyNameList; + + if (depth > 2) + return propertyNameList; + + if (!inspectedObjects->contains(object)) + inspectedObjects->append(object); + + const QMetaObject *metaObject = object->metaObject(); + for (int index = 0; index < metaObject->propertyCount(); ++index) { + QMetaProperty metaProperty = metaObject->property(index); + QQmlProperty declarativeProperty(object, QString::fromUtf8(metaProperty.name())); + if (declarativeProperty.isValid() && !declarativeProperty.isWritable() && declarativeProperty.propertyTypeCategory() == QQmlProperty::Object) { + if (declarativeProperty.name() != QLatin1String("parent")) { + QObject *childObject = QQmlMetaType::toQObject(declarativeProperty.read()); + if (childObject) + propertyNameList.append(propertyNameListForWritablePropertiesInternal(childObject, + baseName + QQuickDesignerSupport::PropertyName(metaProperty.name()) + + '.', inspectedObjects, + depth + 1)); + } + } else if (QQmlGadgetPtrWrapper *valueType + = QQmlGadgetPtrWrapper::instance(qmlEngine(object), metaProperty.metaType())) { + + + const QVariant value = metaProperty.read(object); + QMetaType jsType = QMetaType::fromType(); + int userType = value.userType(); + + //qDebug() << jsType << jsType.id(); + //qDebug() << "tp" << value.typeName(); + //qDebug() << "ut" << userType; + + if (userType == jsType.id()) { + qDebug() << "js value found"; + //QJSValue jsValue = value.value(); //crashes + //qDebug() << jsValue.isObject(); + //qDebug() << jsValue.isQObject(); + } else { + + + + valueType->setValue(value); + propertyNameList.append(propertyNameListForWritablePropertiesInternal(valueType, + baseName + QQuickDesignerSupport::PropertyName(metaProperty.name()) + + '.', inspectedObjects, + depth + 1)); + } + + } + + if (metaProperty.isReadable() && metaProperty.isWritable()) { + addToPropertyNameListIfNotBlackListed(&propertyNameList, + baseName + QQuickDesignerSupport::PropertyName(metaProperty.name())); + } + } + + return propertyNameList; +} + +static QQuickDesignerSupport::PropertyNameList propertyNameListForWritablePropertiesFork(QObject *object) +{ + QObjectList localObjectList; + return propertyNameListForWritablePropertiesInternal(object, {}, &localObjectList); +} + +static QQuickDesignerSupport::PropertyNameList allPropertyNamesFork(QObject *object, + const QQuickDesignerSupport::PropertyName &baseName = QQuickDesignerSupport::PropertyName(), + QObjectList *inspectedObjects = nullptr, + int depth = 0) +{ + QQuickDesignerSupport::PropertyNameList propertyNameList; + + QObjectList localObjectList; + + if (inspectedObjects == nullptr) + inspectedObjects = &localObjectList; + + if (depth > 2) + return propertyNameList; + + if (!inspectedObjects->contains(object)) + inspectedObjects->append(object); + + const QMetaObject *metaObject = object->metaObject(); + + QStringList deferredPropertyNames; + const int namesIndex = metaObject->indexOfClassInfo("DeferredPropertyNames"); + if (namesIndex != -1) { + QMetaClassInfo classInfo = metaObject->classInfo(namesIndex); + deferredPropertyNames = QString::fromUtf8(classInfo.value()).split(QLatin1Char(',')); + } + + for (int index = 0; index < metaObject->propertyCount(); ++index) { + QMetaProperty metaProperty = metaObject->property(index); + QQmlProperty declarativeProperty(object, QString::fromUtf8(metaProperty.name())); + if (declarativeProperty.isValid() && declarativeProperty.propertyTypeCategory() == QQmlProperty::Object) { + if (declarativeProperty.name() != QLatin1String("parent") + && !deferredPropertyNames.contains(declarativeProperty.name())) { + QObject *childObject = QQmlMetaType::toQObject(declarativeProperty.read()); + if (childObject) + propertyNameList.append(allPropertyNamesFork(childObject, + baseName + + QQuickDesignerSupport::PropertyName(metaProperty.name()) + + '.', inspectedObjects, + depth + 1)); + } + } else if (QQmlGadgetPtrWrapper *valueType + = QQmlGadgetPtrWrapper::instance(qmlEngine(object), metaProperty.metaType())) { + + const QVariant value = metaProperty.read(object); + propertyNameList.append(baseName + QQuickDesignerSupport::PropertyName(metaProperty.name())); + const QJsonValue jsonValue = value.toJsonValue(); + + if (!jsonValue.isNull()) { + qDebug() << "llokhere"; + qDebug() << "name" << metaProperty.name(); + qDebug() << "value" << value; + qDebug() << jsonValue; + } + + if (value.isValid() && jsonValue.isNull()) { + //qDebug() << "llokhere crash"; + //qDebug() << "name" << metaProperty.name(); + //qDebug() << "value" << value; + //qDebug() << jsonValue; + + + QMetaType jsType = QMetaType::fromType(); + + int userType = value.userType(); + + //qDebug() << jsType << jsType.id(); + //qDebug() << "tp" << value.typeName(); + //qDebug() << "ut" << userType; + + if (userType == jsType.id()) { + qDebug() << "js value found"; + //QJSValue jsValue = value.value(); //crashes + //qDebug() << jsValue.isObject(); + //qDebug() << jsValue.isQObject(); + } else { + + + valueType->setValue(value); + propertyNameList.append(allPropertyNamesFork(valueType, + baseName + + QQuickDesignerSupport::PropertyName(metaProperty.name()) + + '.', inspectedObjects, + depth + 1)); + } + + + } + } else { + addToPropertyNameListIfNotBlackListed(&propertyNameList, + baseName + QQuickDesignerSupport::PropertyName(metaProperty.name())); + } + } + + return propertyNameList; +} + +class DesignerCustomObjectDataFork +{ +public: + static void registerData(QObject *object); + static DesignerCustomObjectDataFork *get(QObject *object); + static QVariant getResetValue(QObject *object, const QQuickDesignerSupport::PropertyName &propertyName); + static void doResetProperty(QObject *object, QQmlContext *context, const QQuickDesignerSupport::PropertyName &propertyName); + static bool hasValidResetBinding(QObject *object, const QQuickDesignerSupport::PropertyName &propertyName); + static bool hasBindingForProperty(QObject *object, + QQmlContext *context, + const QQuickDesignerSupport::PropertyName &propertyName, + bool *hasChanged); + static void setPropertyBinding(QObject *object, + QQmlContext *context, + const QQuickDesignerSupport::PropertyName &propertyName, + const QString &expression); + static void keepBindingFromGettingDeleted(QObject *object, + QQmlContext *context, + const QQuickDesignerSupport::PropertyName &propertyName); + void handleDestroyed(); + +private: + DesignerCustomObjectDataFork(QObject *object); + void populateResetHashes(); + QObject *object() const; + QVariant getResetValue(const QQuickDesignerSupport::PropertyName &propertyName) const; + void doResetProperty(QQmlContext *context, const QQuickDesignerSupport::PropertyName &propertyName); + bool hasValidResetBinding(const QQuickDesignerSupport::PropertyName &propertyName) const; + QQmlAnyBinding getResetBinding(const QQuickDesignerSupport::PropertyName &propertyName) const; + bool hasBindingForProperty(QQmlContext *context, const QQuickDesignerSupport::PropertyName &propertyName, bool *hasChanged) const; + void setPropertyBinding(QQmlContext *context, const QQuickDesignerSupport::PropertyName &propertyName, const QString &expression); + void keepBindingFromGettingDeleted(QQmlContext *context, const QQuickDesignerSupport::PropertyName &propertyName); + + QObject *m_object; + QHash m_resetValueHash; + QHash m_resetBindingHash; + mutable QHash m_hasBindingHash; +}; + +typedef QHash CustomObjectDataHash; +Q_GLOBAL_STATIC(CustomObjectDataHash, s_designerObjectToDataHash) + +struct HandleDestroyedFunctor { + DesignerCustomObjectDataFork *data; + void operator()() { data->handleDestroyed(); } +}; + +using namespace Qt::StringLiterals; + +DesignerCustomObjectDataFork::DesignerCustomObjectDataFork(QObject *object) + : m_object(object) +{ + if (object) { + populateResetHashes(); + s_designerObjectToDataHash()->insert(object, this); + + HandleDestroyedFunctor functor; + functor.data = this; + QObject::connect(object, &QObject::destroyed, functor); + } +} + +void DesignerCustomObjectDataFork::registerData(QObject *object) +{ + new DesignerCustomObjectDataFork(object); +} + +DesignerCustomObjectDataFork *DesignerCustomObjectDataFork::get(QObject *object) +{ + return s_designerObjectToDataHash()->value(object); +} + +QVariant DesignerCustomObjectDataFork::getResetValue(QObject *object, const QQuickDesignerSupport::PropertyName &propertyName) +{ + DesignerCustomObjectDataFork* data = get(object); + + if (data) + return data->getResetValue(propertyName); + + return QVariant(); +} + +void DesignerCustomObjectDataFork::doResetProperty(QObject *object, QQmlContext *context, const QQuickDesignerSupport::PropertyName &propertyName) +{ + DesignerCustomObjectDataFork* data = get(object); + + if (data) + data->doResetProperty(context, propertyName); +} + +bool DesignerCustomObjectDataFork::hasValidResetBinding(QObject *object, const QQuickDesignerSupport::PropertyName &propertyName) +{ + DesignerCustomObjectDataFork* data = get(object); + + if (data) + return data->hasValidResetBinding(propertyName); + + return false; +} + +bool DesignerCustomObjectDataFork::hasBindingForProperty(QObject *object, + QQmlContext *context, + const QQuickDesignerSupport::PropertyName &propertyName, + bool *hasChanged) +{ + DesignerCustomObjectDataFork* data = get(object); + + if (data) + return data->hasBindingForProperty(context, propertyName, hasChanged); + + return false; +} + +void DesignerCustomObjectDataFork::setPropertyBinding(QObject *object, + QQmlContext *context, + const QQuickDesignerSupport::PropertyName &propertyName, + const QString &expression) +{ + DesignerCustomObjectDataFork* data = get(object); + + if (data) + data->setPropertyBinding(context, propertyName, expression); +} + +void DesignerCustomObjectDataFork::keepBindingFromGettingDeleted(QObject *object, + QQmlContext *context, + const QQuickDesignerSupport::PropertyName &propertyName) +{ + DesignerCustomObjectDataFork* data = get(object); + + if (data) + data->keepBindingFromGettingDeleted(context, propertyName); +} + +void DesignerCustomObjectDataFork::populateResetHashes() +{ + const QQuickDesignerSupport::PropertyNameList propertyNameList = + propertyNameListForWritablePropertiesFork(object()); + + const QMetaObject *mo = object()->metaObject(); + QByteArrayList deferredPropertyNames; + const int namesIndex = mo->indexOfClassInfo("DeferredPropertyNames"); + if (namesIndex != -1) { + QMetaClassInfo classInfo = mo->classInfo(namesIndex); + deferredPropertyNames = QByteArray(classInfo.value()).split(','); + } + + for (const QQuickDesignerSupport::PropertyName &propertyName : propertyNameList) { + + if (deferredPropertyNames.contains(propertyName)) + continue; + + QQmlProperty property(object(), QString::fromUtf8(propertyName), QQmlEngine::contextForObject(object())); + + auto binding = QQmlAnyBinding::ofProperty(property); + + if (binding) { + m_resetBindingHash.insert(propertyName, binding); + } else if (property.isWritable()) { + m_resetValueHash.insert(propertyName, property.read()); + } + } +} + +QObject *DesignerCustomObjectDataFork::object() const +{ + return m_object; +} + +QVariant DesignerCustomObjectDataFork::getResetValue(const QQuickDesignerSupport::PropertyName &propertyName) const +{ + return m_resetValueHash.value(propertyName); +} + +void DesignerCustomObjectDataFork::doResetProperty(QQmlContext *context, const QQuickDesignerSupport::PropertyName &propertyName) +{ + QQmlProperty property(object(), QString::fromUtf8(propertyName), context); + + if (!property.isValid()) + return; + + // remove existing binding + QQmlAnyBinding::takeFrom(property); + + + if (hasValidResetBinding(propertyName)) { + QQmlAnyBinding binding = getResetBinding(propertyName); + binding.installOn(property); + + if (binding.isAbstractPropertyBinding()) { + // for new style properties, we will evaluate during setBinding anyway + static_cast(binding.asAbstractBinding())->update(); + } + + } else if (property.isResettable()) { + property.reset(); + } else if (property.propertyTypeCategory() == QQmlProperty::List) { + QQmlListReference list = qvariant_cast(property.read()); + + if (!QQuickDesignerSupportProperties::hasFullImplementedListInterface(list)) { + qWarning() << "Property list interface not fully implemented for Class " << property.property().typeName() << " in property " << property.name() << "!"; + return; + } + + list.clear(); + } else if (property.isWritable()) { + if (property.read() == getResetValue(propertyName)) + return; + + property.write(getResetValue(propertyName)); + } +} + +bool DesignerCustomObjectDataFork::hasValidResetBinding(const QQuickDesignerSupport::PropertyName &propertyName) const +{ + return m_resetBindingHash.contains(propertyName) && m_resetBindingHash.value(propertyName); +} + +QQmlAnyBinding DesignerCustomObjectDataFork::getResetBinding(const QQuickDesignerSupport::PropertyName &propertyName) const +{ + return m_resetBindingHash.value(propertyName); +} + +bool DesignerCustomObjectDataFork::hasBindingForProperty(QQmlContext *context, + const QQuickDesignerSupport::PropertyName &propertyName, + bool *hasChanged) const +{ + if (QQuickDesignerSupportProperties::isPropertyBlackListed(propertyName)) + return false; + + QQmlProperty property(object(), QString::fromUtf8(propertyName), context); + + bool hasBinding = QQmlAnyBinding::ofProperty(property); + + if (hasChanged) { + *hasChanged = hasBinding != m_hasBindingHash.value(propertyName, false); + if (*hasChanged) + m_hasBindingHash.insert(propertyName, hasBinding); + } + + return hasBinding; +} + +void DesignerCustomObjectDataFork::setPropertyBinding(QQmlContext *context, + const QQuickDesignerSupport::PropertyName &propertyName, + const QString &expression) +{ + QQmlProperty property(object(), QString::fromUtf8(propertyName), context); + + if (!property.isValid()) + return; + + if (property.isProperty()) { + QString url = u"@designer"_s; + int lineNumber = 0; + QQmlAnyBinding binding = QQmlAnyBinding::createFromCodeString(property, + expression, object(), QQmlContextData::get(context), url, lineNumber); + + binding.installOn(property); + if (binding.isAbstractPropertyBinding()) { + // for new style properties, we will evaluate during setBinding anyway + static_cast(binding.asAbstractBinding())->update(); + } + + if (binding.hasError()) { + if (property.property().userType() == QMetaType::QString) + property.write(QVariant(QLatin1Char('#') + expression + QLatin1Char('#'))); + } + + } else { + qWarning() << Q_FUNC_INFO << ": Cannot set binding for property" << propertyName << ": property is unknown for type"; + } +} + +void DesignerCustomObjectDataFork::keepBindingFromGettingDeleted(QQmlContext *context, + const QQuickDesignerSupport::PropertyName &propertyName) +{ + //Refcounting is taking care + Q_UNUSED(context); + Q_UNUSED(propertyName); +} + +void DesignerCustomObjectDataFork::handleDestroyed() +{ + s_designerObjectToDataHash()->remove(m_object); + delete this; +} + + + namespace QmlPrivateGate { bool isPropertyBlackListed(const QmlDesigner::PropertyName &propertyName) @@ -54,12 +523,14 @@ bool isPropertyBlackListed(const QmlDesigner::PropertyName &propertyName) PropertyNameList allPropertyNames(QObject *object) { - return QQuickDesignerSupportProperties::allPropertyNames(object); + return allPropertyNamesFork(object); + //return QQuickDesignerSupportProperties::allPropertyNames(object); } PropertyNameList propertyNameListForWritableProperties(QObject *object) { - return QQuickDesignerSupportProperties::propertyNameListForWritableProperties(object); + return propertyNameListForWritablePropertiesFork(object); + //return QQuickDesignerSupportProperties::propertyNameListForWritableProperties(object); } void tweakObjects(QObject *object) @@ -187,7 +658,8 @@ bool hasFullImplementedListInterface(const QQmlListReference &list) void registerCustomData(QObject *object) { - QQuickDesignerSupportProperties::registerCustomData(object); + DesignerCustomObjectDataFork::registerData(object); + //QQuickDesignerSupportProperties::registerCustomData(object); } QVariant getResetValue(QObject *object, const PropertyName &propertyName) @@ -201,7 +673,8 @@ QVariant getResetValue(QObject *object, const PropertyName &propertyName) else if (propertyName == "Layout.fillWidth") return false; else - return QQuickDesignerSupportProperties::getResetValue(object, propertyName); + return DesignerCustomObjectDataFork::getResetValue(object, propertyName); + //return QQuickDesignerSupportProperties::getResetValue(object, propertyName); } static void setProperty(QObject *object, QQmlContext *context, const PropertyName &propertyName, const QVariant &value) @@ -221,7 +694,9 @@ void doResetProperty(QObject *object, QQmlContext *context, const PropertyName & else if (propertyName == "Layout.fillWidth") setProperty(object, context, propertyName, getResetValue(object, propertyName)); else - QQuickDesignerSupportProperties::doResetProperty(object, context, propertyName); + DesignerCustomObjectDataFork::doResetProperty(object, context, propertyName); + + //QQuickDesignerSupportProperties::doResetProperty(object, context, propertyName); } bool hasValidResetBinding(QObject *object, const PropertyName &propertyName) @@ -234,17 +709,22 @@ bool hasValidResetBinding(QObject *object, const PropertyName &propertyName) return true; else if (propertyName == "Layout.fillWidth") return true; - return QQuickDesignerSupportProperties::hasValidResetBinding(object, propertyName); + return + DesignerCustomObjectDataFork::hasValidResetBinding(object, propertyName); + //QQuickDesignerSupportProperties::hasValidResetBinding(object, propertyName); } bool hasBindingForProperty(QObject *object, QQmlContext *context, const PropertyName &propertyName, bool *hasChanged) { - return QQuickDesignerSupportProperties::hasBindingForProperty(object, context, propertyName, hasChanged); + return DesignerCustomObjectDataFork::hasBindingForProperty(object, context, propertyName, hasChanged); + //return QQuickDesignerSupportProperties::hasBindingForProperty(object, context, propertyName, hasChanged); } void setPropertyBinding(QObject *object, QQmlContext *context, const PropertyName &propertyName, const QString &expression) { - QQuickDesignerSupportProperties::setPropertyBinding(object, context, propertyName, expression); + DesignerCustomObjectDataFork::setPropertyBinding(object, context, propertyName, expression); + + //QQuickDesignerSupportProperties::setPropertyBinding(object, context, propertyName, expression); } void emitComponentComplete(QObject *item) @@ -317,7 +797,8 @@ void doComponentCompleteRecursive(QObject *object, NodeInstanceServer *nodeInsta void keepBindingFromGettingDeleted(QObject *object, QQmlContext *context, const PropertyName &propertyName) { - QQuickDesignerSupportProperties::keepBindingFromGettingDeleted(object, context, propertyName); + DesignerCustomObjectDataFork::keepBindingFromGettingDeleted(object, context, propertyName); + //QQuickDesignerSupportProperties::keepBindingFromGettingDeleted(object, context, propertyName); } bool objectWasDeleted(QObject *object) From 8f2349815dcfa30861173745442c6ed60687c7c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esa=20T=C3=B6rm=C3=A4nen?= Date: Mon, 18 Mar 2024 14:50:17 +0200 Subject: [PATCH 021/202] Doc: Update QDS version compatibility with Qt for MCUs SDKs Update the QDS version compatibility with Qt for MCUs SDKs page so that it includes the latest/upcoming QDS (4.4, 4.5) and Qt for MCUs (2.7, 2.8) releases. Task-number: QDS-12270 Change-Id: I0380de777b9440b3bd01a984711833ff3d8d2890 Reviewed-by: Mats Honkamaa --- .../qtdesignstudio-compatibility-with-mcu-sdks.qdoc | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/doc/qtdesignstudio/src/mcus/qtdesignstudio-compatibility-with-mcu-sdks.qdoc b/doc/qtdesignstudio/src/mcus/qtdesignstudio-compatibility-with-mcu-sdks.qdoc index de94f59d805..0e833cead10 100644 --- a/doc/qtdesignstudio/src/mcus/qtdesignstudio-compatibility-with-mcu-sdks.qdoc +++ b/doc/qtdesignstudio/src/mcus/qtdesignstudio-compatibility-with-mcu-sdks.qdoc @@ -16,10 +16,16 @@ \li \QDS Version \li \QMCU SDK Version \row - \li 4.3 or later - \li 2.6 or later + \li 4.5 or later + \li 2.8 or later \row - \li 4.2 or later + \li 4.4 + \li 2.7 + \row + \li 4.3 + \li 2.6 + \row + \li 4.2 \li 2.5 \row \li 4.0 up to 4.1 From bfaef2cbb7231abdc8c94e266a157899da884230 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 12 Mar 2024 12:02:47 +0100 Subject: [PATCH 022/202] QmlDesigner: Add tracers for type debugging It is really hard to debug the project storage because it gets so much data. With the tracer is should be a little bit easier. Change-Id: I79d60c6fbc32ba3fd7bdcc78916c1aa48f73e125 Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- .../projectstorage/projectstorage.h | 133 ++++++++++++++++-- .../projectstorageexceptions.cpp | 7 +- .../projectstorage/projectstorageexceptions.h | 4 +- .../projectstorage/projectstoragetypes.h | 62 ++++++++ .../projectstorage/projectstorageupdater.cpp | 6 +- 5 files changed, 196 insertions(+), 16 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index a43f26d38c6..344afae756f 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -1659,6 +1659,10 @@ private: }; auto insert = [&](const Storage::Synchronization::ExportedType &type) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert exported type"_t, + projectStorageCategory(), + keyValue("exported type", NanotraceHR::value(type))}; if (!type.moduleId) throw QmlDesigner::ModuleDoesNotExists{}; @@ -1688,6 +1692,11 @@ private: auto update = [&](const Storage::Synchronization::ExportedTypeView &view, const Storage::Synchronization::ExportedType &type) { if (view.typeId != type.typeId) { + NanotraceHR::Tracer tracer{"update exported type"_t, + projectStorageCategory(), + keyValue("exported type", NanotraceHR::value(type)), + keyValue("exported type view", NanotraceHR::value(view))}; + handlePropertyDeclarationWithPropertyType(view.typeId, relinkablePropertyDeclarations); handleAliasPropertyDeclarationsWithPropertyType(view.typeId, relinkableAliasPropertyDeclarations); @@ -1700,6 +1709,10 @@ private: }; auto remove = [&](const Storage::Synchronization::ExportedTypeView &view) { + NanotraceHR::Tracer tracer{"remove exported type"_t, + projectStorageCategory(), + keyValue("exported type view", NanotraceHR::value(view))}; + handlePropertyDeclarationWithPropertyType(view.typeId, relinkablePropertyDeclarations); handleAliasPropertyDeclarationsWithPropertyType(view.typeId, relinkableAliasPropertyDeclarations); @@ -1737,7 +1750,7 @@ private: auto propertyTypeId = fetchTypeId(propertyImportedTypeNameId); if (!propertyTypeId) - throw TypeNameDoesNotExists{fetchImportedTypeName(propertyImportedTypeNameId)}; + throw TypeNameDoesNotExists{fetchImportedTypeName(propertyImportedTypeNameId), sourceId}; auto propertyDeclarationId = insertPropertyDeclarationStatement.template value( typeId, value.name, propertyTypeId, value.traits, propertyImportedTypeNameId); @@ -1779,7 +1792,7 @@ private: auto propertyTypeId = fetchTypeId(propertyImportedTypeNameId); if (!propertyTypeId) - throw TypeNameDoesNotExists{fetchImportedTypeName(propertyImportedTypeNameId)}; + throw TypeNameDoesNotExists{fetchImportedTypeName(propertyImportedTypeNameId), sourceId}; if (view.traits == value.traits && propertyTypeId == view.typeId && propertyImportedTypeNameId == view.typeNameId) @@ -1996,6 +2009,13 @@ private: }; auto insert = [&](const Storage::Import &import) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert import"_t, + projectStorageCategory(), + keyValue("import", NanotraceHR::value(import)), + keyValue("import kind", importKind), + keyValue("source id", import.sourceId)}; + auto importId = insertDocumentImport(import, importKind, import.moduleId, ImportId{}); auto callback = [&](ModuleId exportedModuleId, int majorVersion, int minorVersion) { Storage::Import additionImport{exportedModuleId, @@ -2006,13 +2026,24 @@ private: ? Storage::Synchronization::ImportKind::ModuleExportedImport : Storage::Synchronization::ImportKind::ModuleExportedModuleDependency; - insertDocumentImport(additionImport, exportedImportKind, import.moduleId, importId); + NanotraceHR::Tracer tracer{"insert indirect import"_t, + projectStorageCategory(), + keyValue("import", NanotraceHR::value(import)), + keyValue("import kind", exportedImportKind)}; + + auto indirectImportId = insertDocumentImport(additionImport, + exportedImportKind, + import.moduleId, + importId); + + tracer.end(keyValue("import id", indirectImportId)); }; selectModuleExportedImportsForModuleIdStatement.readCallback(callback, import.moduleId, import.version.major.value, import.version.minor.value); + tracer.end(keyValue("import id", importId)); }; auto update = [](const Storage::Synchronization::ImportView &, const Storage::Import &) { @@ -2020,6 +2051,12 @@ private: }; auto remove = [&](const Storage::Synchronization::ImportView &view) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"remove import"_t, + projectStorageCategory(), + keyValue("import id", view.importId), + keyValue("source id", view.sourceId)}; + deleteDocumentImportStatement.write(view.importId); deleteDocumentImportsWithParentImportIdStatement.write(view.sourceId, view.importId); }; @@ -2321,9 +2358,17 @@ private: TypeId declareType(Storage::Synchronization::Type &type) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"declare type"_t, + projectStorageCategory(), + keyValue("source id", type.sourceId), + keyValue("type name", type.typeName)}; + if (type.typeName.isEmpty()) { type.typeId = selectTypeIdBySourceIdStatement.template value(type.sourceId); + tracer.end(keyValue("type id", type.typeId)); + return type.typeId; } @@ -2333,6 +2378,8 @@ private: type.typeId = selectTypeIdBySourceIdAndNameStatement.template value(type.sourceId, type.typeName); + tracer.end(keyValue("type id", type.typeId)); + return type.typeId; } @@ -2510,6 +2557,12 @@ private: std::pair fetchImportedTypeNameIdAndTypeId( const Storage::Synchronization::ImportedTypeName &typeName, SourceId sourceId) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch imported type name id and type id"_t, + projectStorageCategory(), + keyValue("impoted type name", NanotraceHR::value(typeName)), + keyValue("source id", sourceId)}; + TypeId typeId; ImportedTypeNameId typeNameId; if (!std::visit([](auto &&typeName_) -> bool { return typeName_.name.isEmpty(); }, typeName)) { @@ -2517,8 +2570,10 @@ private: typeId = fetchTypeId(typeNameId); + tracer.end(keyValue("type id", typeId), keyValue("type name id", typeNameId)); + if (!typeId) - throw TypeNameDoesNotExists{fetchImportedTypeName(typeNameId)}; + throw TypeNameDoesNotExists{fetchImportedTypeName(typeNameId), sourceId}; } return {typeId, typeNameId}; @@ -2529,6 +2584,14 @@ private: if (type.changeLevel == Storage::Synchronization::ChangeLevel::Minimal) return; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"synchronize prototype and extension"_t, + projectStorageCategory(), + keyValue("prototype", NanotraceHR::value(type.prototype)), + keyValue("extension", NanotraceHR::value(type.extension)), + keyValue("type id", type.typeId), + keyValue("source id", type.sourceId)}; + auto [prototypeId, prototypeTypeNameId] = fetchImportedTypeNameIdAndTypeId(type.prototype, type.sourceId); auto [extensionId, extensionTypeNameId] = fetchImportedTypeNameIdAndTypeId(type.extension, @@ -2544,13 +2607,19 @@ private: checkForPrototypeChainCycle(type.typeId); typeIds.push_back(type.typeId); + + tracer.end(keyValue("prototype id", prototypeId), + keyValue("prototype type name id", prototypeTypeNameId), + keyValue("extension id", extensionId), + keyValue("extension type name id", extensionTypeNameId)); } void syncPrototypesAndExtensions(Storage::Synchronization::Types &types, Prototypes &relinkablePrototypes, Prototypes &relinkableExtensions) { - NanotraceHR::Tracer tracer{"synchronize prototypes"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"synchronize prototypes and extensions"_t, + projectStorageCategory()}; TypeIds typeIds; typeIds.reserve(types.size()); @@ -2585,6 +2654,13 @@ private: { auto operator()(const Storage::Synchronization::ImportedType &importedType) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch imported type name id"_t, + projectStorageCategory(), + keyValue("imported type name", importedType.name), + keyValue("source id", sourceId), + keyValue("type name kind", "exported"sv)}; + return storage.fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::Exported, sourceId, importedType.name); @@ -2592,11 +2668,24 @@ private: auto operator()(const Storage::Synchronization::QualifiedImportedType &importedType) { + using NanotraceHR::keyValue; + + NanotraceHR::Tracer tracer{"fetch imported type name id"_t, + projectStorageCategory(), + keyValue("imported type name", importedType.name), + keyValue("import", NanotraceHR::value(importedType.import)), + keyValue("type name kind", "qualified exported"sv)}; + ImportId importId = storage.fetchImportId(sourceId, importedType.import); - return storage.fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::QualifiedExported, - importId, - importedType.name); + auto importedTypeNameId = storage.fetchImportedTypeNameId( + Storage::Synchronization::TypeNameKind::QualifiedExported, + importId, + importedType.name); + + tracer.end(keyValue("import id", importId), keyValue("source id", sourceId)); + + return importedTypeNameId; } ProjectStorage &storage; @@ -2622,10 +2711,19 @@ private: TypeId fetchTypeId(ImportedTypeNameId typeNameId) const { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch type id with type name kind"_t, + projectStorageCategory(), + keyValue("type name id", typeNameId)}; + auto kind = selectKindFromImportedTypeNamesStatement .template value(typeNameId); - return fetchTypeId(typeNameId, kind); + auto typeId = fetchTypeId(typeNameId, kind); + + tracer.end(keyValue("type id", typeId), keyValue("type name kind", to_underlying(kind))); + + return typeId; } Utils::SmallString fetchImportedTypeName(ImportedTypeNameId typeNameId) const @@ -2635,12 +2733,23 @@ private: TypeId fetchTypeId(ImportedTypeNameId typeNameId, Storage::Synchronization::TypeNameKind kind) const { - if (kind == Storage::Synchronization::TypeNameKind::QualifiedExported) { - return selectTypeIdForQualifiedImportedTypeNameNamesStatement.template value( + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch type id"_t, + projectStorageCategory(), + keyValue("type name id", typeNameId), + keyValue("type name kind", to_underlying(kind))}; + + TypeId typeId; + if (kind == Storage::Synchronization::TypeNameKind::Exported) { + typeId = selectTypeIdForImportedTypeNameNamesStatement.template value(typeNameId); + } else { + typeId = selectTypeIdForQualifiedImportedTypeNameNamesStatement.template value( typeNameId); } - return selectTypeIdForImportedTypeNameNamesStatement.template value(typeNameId); + tracer.end(keyValue("type id", typeId)); + + return typeId; } class FetchPropertyDeclarationResult diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp index efe9bc58f5d..0f70ccf4075 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp @@ -40,8 +40,11 @@ const char *ModuleAlreadyExists::what() const noexcept return "The module does already exist!"; } -TypeNameDoesNotExists::TypeNameDoesNotExists(std::string_view errorMessage) - : ProjectStorageErrorWithMessage{"TypeNameDoesNotExists"sv, errorMessage} +TypeNameDoesNotExists::TypeNameDoesNotExists(std::string_view typeName, SourceId sourceId) + : ProjectStorageErrorWithMessage{ + "TypeNameDoesNotExists"sv, + Utils::SmallString::join( + {"type: ", typeName, ", source id: ", Utils::SmallString::number(sourceId.internalId())})} {} const char *PropertyNameDoesNotExists::what() const noexcept diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h index 412dd4a9ff9..e19421dbf45 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h @@ -5,6 +5,8 @@ #include "../include/qmldesignercorelib_global.h" +#include "projectstorageids.h" + #include namespace QmlDesigner { @@ -79,7 +81,7 @@ public: class QMLDESIGNERCORE_EXPORT TypeNameDoesNotExists : public ProjectStorageErrorWithMessage { public: - TypeNameDoesNotExists(std::string_view errorMessage); + TypeNameDoesNotExists(std::string_view typeName, SourceId sourceId = SourceId{}); }; class QMLDESIGNERCORE_EXPORT PropertyNameDoesNotExists : public ProjectStorageError diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h index 18c39312496..6199911497b 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h @@ -7,6 +7,7 @@ #include "projectstorageids.h" #include "projectstorageinfotypes.h" +#include #include #include @@ -878,3 +879,64 @@ public: } // namespace Synchronization } // namespace QmlDesigner::Storage + +namespace NanotraceHR { + +inline auto value(const QmlDesigner::Storage::Version &version) +{ + return dictonary(keyValue("major version", version.major.value), + keyValue("minor version", version.minor.value)); +} + +inline auto value(const QmlDesigner::Storage::Import &import) +{ + return dictonary(keyValue("module id", import.moduleId), + keyValue("source id", import.sourceId), + keyValue("version", value(import.version))); +} + +inline auto value(const QmlDesigner::Storage::Synchronization::ExportedType &exportedType) +{ + return dictonary(keyValue("name", exportedType.name), + keyValue("module id", exportedType.moduleId), + keyValue("type id", exportedType.typeId), + keyValue("version", value(exportedType.version))); +} + +inline auto value(const QmlDesigner::Storage::Synchronization::ExportedTypeView &exportedType) +{ + return dictonary(keyValue("name", exportedType.name), + keyValue("module id", exportedType.moduleId), + keyValue("type id", exportedType.typeId), + keyValue("version", value(exportedType.version)), + keyValue("version", exportedType.exportedTypeNameId)); +} + +inline auto value(const QmlDesigner::Storage::Synchronization::ImportedTypeName &typeName) +{ + struct Dispatcher + { + static const QmlDesigner::Storage::Import &nullImport() + { + static QmlDesigner::Storage::Import import; + + return import; + } + + auto operator()(const QmlDesigner::Storage::Synchronization::ImportedType &importedType) const + { + return dictonary(keyValue("name", importedType.name), + keyValue("import", value(nullImport()))); + } + + auto operator()( + const QmlDesigner::Storage::Synchronization::QualifiedImportedType &qualifiedImportedType) const + { + return dictonary(keyValue("name", qualifiedImportedType.name), + keyValue("import", value(qualifiedImportedType.import))); + } + }; + + return std::visit(Dispatcher{}, typeName); +} +} // namespace NanotraceHR diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp index 72ddf3e37bf..f197aad060d 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp @@ -198,7 +198,11 @@ void ProjectStorageUpdater::update(QStringList directories, std::move(package.updatedFileStatusSourceIds), std::move(notUpdatedSourceIds.fileStatusSourceIds)); - m_projectStorage.synchronize(std::move(package)); + try { + m_projectStorage.synchronize(std::move(package)); + } catch (...) { + qWarning() << "Project storage could not been updated!"; + } m_pathWatcher.updateIdPaths(createIdPaths(watchedSourceIds, m_projectPartId)); } From c9527e03419de8e5edd3527e4bc1be4497e0e3bd Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Fri, 15 Mar 2024 09:01:32 +0100 Subject: [PATCH 023/202] Sqlite: Improve tracing Change-Id: Id5d6ee448a7f2cf06cda2e2776f3dbbe7c8f66c4 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- src/libs/nanotrace/nanotracehr.h | 6 + src/libs/sqlite/sqlitebasestatement.cpp | 215 ++++++++++++++---- src/libs/sqlite/sqlitebasestatement.h | 63 ++++- src/libs/utils/smallstring.h | 15 +- .../tracing/qmldesignertracing.cpp | 12 +- tests/unit/tests/mocks/mocksqlitestatement.h | 2 + 6 files changed, 241 insertions(+), 72 deletions(-) diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index ac29bce9285..ded71375390 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -146,6 +146,12 @@ void convertToString(String &string, long long number) string.append(Utils::SmallString::number(number)); } +template +void convertToString(String &string, std::size_t number) +{ + string.append(Utils::SmallString::number(number)); +} + template void convertToString(String &string, double number) { diff --git a/src/libs/sqlite/sqlitebasestatement.cpp b/src/libs/sqlite/sqlitebasestatement.cpp index c340c52a0ed..9fa1638d382 100644 --- a/src/libs/sqlite/sqlitebasestatement.cpp +++ b/src/libs/sqlite/sqlitebasestatement.cpp @@ -26,30 +26,35 @@ extern "C" int sqlite3_carray_bind( namespace Sqlite { -namespace { -using TraceFile = NanotraceHR::TraceFile; - -TraceFile traceFile{"sqlite.json"}; - -thread_local NanotraceHR::EventQueue eventQueue( - traceFile); - -NanotraceHR::StringViewCategory &sqliteLowLevelCategory(); - -thread_local NanotraceHR::StringViewCategory sqliteLowLevelCategory_{ - "sqlite low level"_t, eventQueue, sqliteLowLevelCategory}; - -NanotraceHR::StringViewCategory &sqliteLowLevelCategory() +TraceFile &traceFile() { + static TraceFile traceFile{"tracing.json"}; + + return traceFile; +} + +namespace { + +thread_local NanotraceHR::EventQueue + eventQueue(traceFile()); + +NanotraceHR::StringViewWithStringArgumentsCategory &sqliteLowLevelCategory() +{ + thread_local NanotraceHR::StringViewWithStringArgumentsCategory + sqliteLowLevelCategory_{"sqlite low level"_t, eventQueue, sqliteLowLevelCategory}; return sqliteLowLevelCategory_; } -thread_local NanotraceHR::StringViewCategory sqliteHighLevelCategory_{ - "sqlite high level"_t, eventQueue, sqliteHighLevelCategory}; +using NanotraceHR::keyValue; + } // namespace -NanotraceHR::StringViewCategory &sqliteHighLevelCategory() +NanotraceHR::StringViewWithStringArgumentsCategory &sqliteHighLevelCategory() { + thread_local NanotraceHR::StringViewWithStringArgumentsCategory + sqliteHighLevelCategory_{"sqlite high level"_t, eventQueue, sqliteHighLevelCategory}; + return sqliteHighLevelCategory_; } @@ -107,14 +112,18 @@ void BaseStatement::waitForUnlockNotify() const void BaseStatement::reset() const noexcept { - NanotraceHR::Tracer tracer{"reset"_t, sqliteLowLevelCategory()}; + NanotraceHR::Tracer tracer{"reset"_t, + sqliteLowLevelCategory(), + keyValue("sqlite statement", handle())}; sqlite3_reset(m_compiledStatement.get()); } bool BaseStatement::next() const { - NanotraceHR::Tracer tracer{"next"_t, sqliteLowLevelCategory()}; + NanotraceHR::Tracer tracer{"next"_t, + sqliteLowLevelCategory(), + keyValue("sqlite statement", handle())}; int resultCode; do { @@ -141,7 +150,10 @@ void BaseStatement::step() const void BaseStatement::bindNull(int index) { - NanotraceHR::Tracer tracer{"bind null"_t, sqliteLowLevelCategory()}; + NanotraceHR::Tracer tracer{"bind null"_t, + sqliteLowLevelCategory(), + keyValue("sqlite statement", handle()), + keyValue("index", index)}; int resultCode = sqlite3_bind_null(m_compiledStatement.get(), index); if (resultCode != SQLITE_OK) @@ -155,7 +167,11 @@ void BaseStatement::bind(int index, NullValue) void BaseStatement::bind(int index, int value) { - NanotraceHR::Tracer tracer{"bind int"_t, sqliteLowLevelCategory()}; + NanotraceHR::Tracer tracer{"bind int"_t, + sqliteLowLevelCategory(), + keyValue("sqlite statement", handle()), + keyValue("index", index), + keyValue("value", value)}; int resultCode = sqlite3_bind_int(m_compiledStatement.get(), index, value); if (resultCode != SQLITE_OK) @@ -164,7 +180,11 @@ void BaseStatement::bind(int index, int value) void BaseStatement::bind(int index, long long value) { - NanotraceHR::Tracer tracer{"bind long long"_t, sqliteLowLevelCategory()}; + NanotraceHR::Tracer tracer{"bind long long"_t, + sqliteLowLevelCategory(), + keyValue("sqlite statement", handle()), + keyValue("index", index), + keyValue("value", value)}; int resultCode = sqlite3_bind_int64(m_compiledStatement.get(), index, value); if (resultCode != SQLITE_OK) @@ -173,7 +193,11 @@ void BaseStatement::bind(int index, long long value) void BaseStatement::bind(int index, double value) { - NanotraceHR::Tracer tracer{"bind double"_t, sqliteLowLevelCategory()}; + NanotraceHR::Tracer tracer{"bind double"_t, + sqliteLowLevelCategory(), + keyValue("sqlite statement", handle()), + keyValue("index", index), + keyValue("value", value)}; int resultCode = sqlite3_bind_double(m_compiledStatement.get(), index, value); if (resultCode != SQLITE_OK) @@ -182,7 +206,11 @@ void BaseStatement::bind(int index, double value) void BaseStatement::bind(int index, void *pointer) { - NanotraceHR::Tracer tracer{"bind pointer"_t, sqliteLowLevelCategory()}; + NanotraceHR::Tracer tracer{"bind pointer"_t, + sqliteLowLevelCategory(), + keyValue("sqlite statement", handle()), + keyValue("index", index), + keyValue("pointer", reinterpret_cast(pointer))}; int resultCode = sqlite3_bind_pointer(m_compiledStatement.get(), index, pointer, "carray", nullptr); if (resultCode != SQLITE_OK) @@ -191,7 +219,12 @@ void BaseStatement::bind(int index, void *pointer) void BaseStatement::bind(int index, Utils::span values) { - NanotraceHR::Tracer tracer{"bind int span"_t, sqliteLowLevelCategory()}; + NanotraceHR::Tracer tracer{"bind int span"_t, + sqliteLowLevelCategory(), + keyValue("sqlite statement", handle()), + keyValue("index", index), + keyValue("pointer", reinterpret_cast(values.data())), + keyValue("size", values.size())}; int resultCode = sqlite3_carray_bind(m_compiledStatement.get(), index, @@ -205,7 +238,12 @@ void BaseStatement::bind(int index, Utils::span values) void BaseStatement::bind(int index, Utils::span values) { - NanotraceHR::Tracer tracer{"bind long long span"_t, sqliteLowLevelCategory()}; + NanotraceHR::Tracer tracer{"bind long long span"_t, + sqliteLowLevelCategory(), + keyValue("sqlite statement", handle()), + keyValue("index", index), + keyValue("pointer", reinterpret_cast(values.data())), + keyValue("size", values.size())}; int resultCode = sqlite3_carray_bind(m_compiledStatement.get(), index, @@ -219,7 +257,12 @@ void BaseStatement::bind(int index, Utils::span values) void BaseStatement::bind(int index, Utils::span values) { - NanotraceHR::Tracer tracer{"bind double span"_t, sqliteLowLevelCategory()}; + NanotraceHR::Tracer tracer{"bind double span"_t, + sqliteLowLevelCategory(), + keyValue("sqlite statement", handle()), + keyValue("index", index), + keyValue("pointer", reinterpret_cast(values.data())), + keyValue("size", values.size())}; int resultCode = sqlite3_carray_bind(m_compiledStatement.get(), index, @@ -233,7 +276,12 @@ void BaseStatement::bind(int index, Utils::span values) void BaseStatement::bind(int index, Utils::span values) { - NanotraceHR::Tracer tracer{"bind const char* span"_t, sqliteLowLevelCategory()}; + NanotraceHR::Tracer tracer{"bind const char* span"_t, + sqliteLowLevelCategory(), + keyValue("sqlite statement", handle()), + keyValue("index", index), + keyValue("pointer", reinterpret_cast(values.data())), + keyValue("size", values.size())}; int resultCode = sqlite3_carray_bind(m_compiledStatement.get(), index, @@ -247,7 +295,11 @@ void BaseStatement::bind(int index, Utils::span values) void BaseStatement::bind(int index, Utils::SmallStringView text) { - NanotraceHR::Tracer tracer{"bind string"_t, sqliteLowLevelCategory()}; + NanotraceHR::Tracer tracer{"bind string"_t, + sqliteLowLevelCategory(), + keyValue("sqlite statement", handle()), + keyValue("index", index), + keyValue("text", text)}; int resultCode = sqlite3_bind_text(m_compiledStatement.get(), index, @@ -260,7 +312,12 @@ void BaseStatement::bind(int index, Utils::SmallStringView text) void BaseStatement::bind(int index, BlobView blobView) { - NanotraceHR::Tracer tracer{"bind blob"_t, sqliteLowLevelCategory()}; + NanotraceHR::Tracer tracer{"bind blob"_t, + sqliteLowLevelCategory(), + keyValue("sqlite statement", handle()), + keyValue("index", index), + keyValue("pointer", reinterpret_cast(blobView.data())), + keyValue("size", blobView.size())}; int resultCode = SQLITE_OK; @@ -280,7 +337,11 @@ void BaseStatement::bind(int index, BlobView blobView) void BaseStatement::bind(int index, const Value &value) { - NanotraceHR::Tracer tracer{"bind value"_t, sqliteLowLevelCategory()}; + NanotraceHR::Tracer tracer{ + "bind value"_t, + sqliteLowLevelCategory(), + keyValue("sqlite statement", handle()), + }; switch (value.type()) { case ValueType::Integer: @@ -303,7 +364,11 @@ void BaseStatement::bind(int index, const Value &value) void BaseStatement::bind(int index, ValueView value) { - NanotraceHR::Tracer tracer{"bind value"_t, sqliteLowLevelCategory()}; + NanotraceHR::Tracer tracer{ + "bind value"_t, + sqliteLowLevelCategory(), + keyValue("sqlite statement", handle()), + }; switch (value.type()) { case ValueType::Integer: @@ -326,7 +391,9 @@ void BaseStatement::bind(int index, ValueView value) void BaseStatement::prepare(Utils::SmallStringView sqlStatement) { - NanotraceHR::Tracer tracer{"prepare"_t, sqliteLowLevelCategory()}; + NanotraceHR::Tracer tracer{"prepare"_t, + sqliteLowLevelCategory(), + keyValue("sqlite statement", handle())}; if (!m_database.isLocked()) throw DatabaseIsNotLocked{}; @@ -342,14 +409,18 @@ void BaseStatement::prepare(Utils::SmallStringView sqlStatement) nullptr); m_compiledStatement.reset(sqliteStatement); - if (resultCode == SQLITE_LOCKED) + if (resultCode == SQLITE_LOCKED) { + tracer.tick("wait for unlock"_t); waitForUnlockNotify(); + } } while (resultCode == SQLITE_LOCKED); if (resultCode != SQLITE_OK) Sqlite::throwError(resultCode, sqliteDatabaseHandle()); + + tracer.end(keyValue("sql statement", handle())); } sqlite3 *BaseStatement::sqliteDatabaseHandle() const @@ -427,7 +498,10 @@ StringType convertToTextForColumn(sqlite3_stmt *sqlStatment, int column) Type BaseStatement::fetchType(int column) const { - NanotraceHR::Tracer tracer{"fetch type"_t, sqliteLowLevelCategory()}; + NanotraceHR::Tracer tracer{"fetch type"_t, + sqliteLowLevelCategory(), + keyValue("sqlite statement", handle()), + keyValue("column", column)}; auto dataType = sqlite3_column_type(m_compiledStatement.get(), column); @@ -449,9 +523,16 @@ Type BaseStatement::fetchType(int column) const int BaseStatement::fetchIntValue(int column) const { - NanotraceHR::Tracer tracer{"fetch int"_t, sqliteLowLevelCategory()}; + NanotraceHR::Tracer tracer{"fetch int"_t, + sqliteLowLevelCategory(), + keyValue("sqlite statement", handle()), + keyValue("column", column)}; - return sqlite3_column_int(m_compiledStatement.get(), column); + auto value = sqlite3_column_int(m_compiledStatement.get(), column); + + tracer.end(keyValue("value", value)); + + return value; } template<> @@ -473,9 +554,16 @@ long BaseStatement::fetchValue(int column) const long long BaseStatement::fetchLongLongValue(int column) const { - NanotraceHR::Tracer tracer{"fetch long long"_t, sqliteLowLevelCategory()}; + NanotraceHR::Tracer tracer{"fetch long long"_t, + sqliteLowLevelCategory(), + keyValue("sqlite statement", handle()), + keyValue("column", column)}; - return sqlite3_column_int64(m_compiledStatement.get(), column); + auto value = sqlite3_column_int64(m_compiledStatement.get(), column); + + tracer.end(keyValue("value", value)); + + return value; } template<> @@ -486,14 +574,24 @@ long long BaseStatement::fetchValue(int column) const double BaseStatement::fetchDoubleValue(int column) const { - NanotraceHR::Tracer tracer{"fetch double"_t, sqliteLowLevelCategory()}; + NanotraceHR::Tracer tracer{"fetch double"_t, + sqliteLowLevelCategory(), + keyValue("sqlite statement", handle()), + keyValue("column", column)}; - return sqlite3_column_double(m_compiledStatement.get(), column); + auto value = sqlite3_column_double(m_compiledStatement.get(), column); + + tracer.end(keyValue("value", value)); + + return value; } BlobView BaseStatement::fetchBlobValue(int column) const { - NanotraceHR::Tracer tracer{"fetch blob"_t, sqliteLowLevelCategory()}; + NanotraceHR::Tracer tracer{"fetch blob"_t, + sqliteLowLevelCategory(), + keyValue("sqlite statement", handle()), + keyValue("column", column)}; return convertToBlobForColumn(m_compiledStatement.get(), column); } @@ -507,7 +605,16 @@ double BaseStatement::fetchValue(int column) const template StringType BaseStatement::fetchValue(int column) const { - return convertToTextForColumn(m_compiledStatement.get(), column); + NanotraceHR::Tracer tracer{"fetch string value"_t, + sqliteLowLevelCategory(), + keyValue("sqlite statement", handle()), + keyValue("column", column)}; + + auto text = convertToTextForColumn(m_compiledStatement.get(), column); + + tracer.end(keyValue("text", text)); + + return text; } template SQLITE_EXPORT Utils::SmallStringView BaseStatement::fetchValue( @@ -519,11 +626,25 @@ template SQLITE_EXPORT Utils::PathString BaseStatement::fetchValue(column); + NanotraceHR::Tracer tracer{"fetch string view"_t, + sqliteLowLevelCategory(), + keyValue("sqlite statement", handle()), + keyValue("column", column)}; + + auto text = fetchValue(column); + + tracer.end(keyValue("text", text)); + + return text; } ValueView BaseStatement::fetchValueView(int column) const { + NanotraceHR::Tracer tracer{"fetch value view"_t, + sqliteLowLevelCategory(), + keyValue("sqlite statement", handle()), + keyValue("column", column)}; + int dataType = sqlite3_column_type(m_compiledStatement.get(), column); switch (dataType) { case SQLITE_NULL: @@ -543,7 +664,11 @@ ValueView BaseStatement::fetchValueView(int column) const void BaseStatement::Deleter::operator()(sqlite3_stmt *statement) { - NanotraceHR::Tracer tracer{"finalize"_t, sqliteLowLevelCategory()}; + NanotraceHR::Tracer tracer{ + "finalize"_t, + sqliteLowLevelCategory(), + keyValue("sqlite statement", reinterpret_cast(statement)), + }; sqlite3_finalize(statement); } diff --git a/src/libs/sqlite/sqlitebasestatement.h b/src/libs/sqlite/sqlitebasestatement.h index d2306c67a5e..9f81bfb9e26 100644 --- a/src/libs/sqlite/sqlitebasestatement.h +++ b/src/libs/sqlite/sqlitebasestatement.h @@ -47,13 +47,18 @@ constexpr static std::underlying_type_t to_underlying(Enumeration e constexpr NanotraceHR::Tracing sqliteTracingStatus() { #ifdef ENABLE_SQLITE_TRACING - return NanotraceHR::tracingStatus(); + return NanotraceHR::Tracing::IsEnabled; #else return NanotraceHR::Tracing::IsDisabled; #endif } -SQLITE_EXPORT NanotraceHR::StringViewCategory &sqliteHighLevelCategory(); +using TraceFile = NanotraceHR::TraceFile; + +SQLITE_EXPORT TraceFile &traceFile(); + +SQLITE_EXPORT NanotraceHR::StringViewWithStringArgumentsCategory & +sqliteHighLevelCategory(); class SQLITE_EXPORT BaseStatement { @@ -136,6 +141,11 @@ public: protected: ~BaseStatement() = default; + std::uintptr_t handle() const + { + return reinterpret_cast(m_compiledStatement.get()); + } + private: struct Deleter { @@ -166,7 +176,12 @@ public: void execute() { - NanotraceHR::Tracer tracer{"execute"_t, sqliteHighLevelCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{ + "execute"_t, + sqliteHighLevelCategory(), + keyValue("sqlite statement", BaseStatement::handle()), + }; Resetter resetter{this}; BaseStatement::next(); @@ -175,7 +190,10 @@ public: template void bindValues(const ValueType &...values) { - NanotraceHR::Tracer tracer{"bind"_t, sqliteHighLevelCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"bind"_t, + sqliteHighLevelCategory(), + keyValue("sqlite statement", BaseStatement::handle())}; static_assert(BindParameterCount == sizeof...(values), "Wrong binding parameter count!"); @@ -186,7 +204,10 @@ public: template void write(const ValueType&... values) { - NanotraceHR::Tracer tracer{"write"_t, sqliteHighLevelCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"write"_t, + sqliteHighLevelCategory(), + keyValue("sqlite statement", BaseStatement::handle())}; Resetter resetter{this}; bindValues(values...); @@ -212,7 +233,10 @@ public: typename... QueryTypes> auto values(const QueryTypes &...queryValues) { - NanotraceHR::Tracer tracer{"values"_t, sqliteHighLevelCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"values"_t, + sqliteHighLevelCategory(), + keyValue("sqlite statement", BaseStatement::handle())}; Resetter resetter{this}; Container resultValues; @@ -241,7 +265,10 @@ public: template auto value(const QueryTypes &...queryValues) { - NanotraceHR::Tracer tracer{"value"_t, sqliteHighLevelCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"value"_t, + sqliteHighLevelCategory(), + keyValue("sqlite statement", BaseStatement::handle())}; Resetter resetter{this}; ResultType resultValue{}; @@ -257,7 +284,10 @@ public: template auto optionalValue(const QueryTypes &...queryValues) { - NanotraceHR::Tracer tracer{"optionalValue"_t, sqliteHighLevelCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"optionalValue"_t, + sqliteHighLevelCategory(), + keyValue("sqlite statement", BaseStatement::handle())}; Resetter resetter{this}; std::optional resultValue; @@ -273,6 +303,7 @@ public: template static auto toValue(Utils::SmallStringView sqlStatement, Database &database) { + using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"toValue"_t, sqliteHighLevelCategory()}; StatementImplementation statement(sqlStatement, database); @@ -287,7 +318,10 @@ public: template void readCallback(Callable &&callable, const QueryTypes &...queryValues) { - NanotraceHR::Tracer tracer{"readCallback"_t, sqliteHighLevelCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"readCallback"_t, + sqliteHighLevelCategory(), + keyValue("sqlite statement", BaseStatement::handle())}; Resetter resetter{this}; @@ -304,7 +338,10 @@ public: template void readTo(Container &container, const QueryTypes &...queryValues) { - NanotraceHR::Tracer tracer{"readTo"_t, sqliteHighLevelCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"readTo"_t, + sqliteHighLevelCategory(), + keyValue("sqlite statement", BaseStatement::handle())}; Resetter resetter{this}; @@ -402,9 +439,11 @@ public: private: using TracerCategory = std::decay_t; - NanotraceHR::Tracer tracer{ - "range"_t, sqliteHighLevelCategory()}; StatementImplementation &m_statement; + NanotraceHR::Tracer tracer{ + "range"_t, + sqliteHighLevelCategory(), + NanotraceHR::keyValue("sqlite statement", m_statement.handle())}; }; template diff --git a/src/libs/utils/smallstring.h b/src/libs/utils/smallstring.h index a32b09986c4..1d5eeb2438e 100644 --- a/src/libs/utils/smallstring.h +++ b/src/libs/utils/smallstring.h @@ -582,20 +582,11 @@ public: return joinedString; } - static - BasicSmallString number(int number) + template>> + static BasicSmallString number(Integer number) { // 2 bytes for the sign and because digits10 returns the floor - char buffer[std::numeric_limits::digits10 + 2]; - auto result = std::to_chars(buffer, buffer + sizeof(buffer), number); - auto endOfConversionString = result.ptr; - return BasicSmallString(buffer, endOfConversionString); - } - - static BasicSmallString number(long long int number) noexcept - { - // 2 bytes for the sign and because digits10 returns the floor - char buffer[std::numeric_limits::digits10 + 2]; + char buffer[std::numeric_limits::digits10 + 2]; auto result = std::to_chars(buffer, buffer + sizeof(buffer), number); auto endOfConversionString = result.ptr; return BasicSmallString(buffer, endOfConversionString); diff --git a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp index c12f1daf682..bb7cb809bd1 100644 --- a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp +++ b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp @@ -3,6 +3,8 @@ #include "qmldesignertracing.h" +#include + namespace QmlDesigner { namespace Tracing { @@ -10,10 +12,14 @@ namespace { using TraceFile = NanotraceHR::TraceFile; -TraceFile &traceFile() +auto &traceFile() { - static TraceFile traceFile{"qml_designer.json"}; - return traceFile; + if constexpr (std::is_same_v) { + return Sqlite::traceFile(); + } else { + static TraceFile traceFile{"tracing.json"}; + return traceFile; + } } } // namespace diff --git a/tests/unit/tests/mocks/mocksqlitestatement.h b/tests/unit/tests/mocks/mocksqlitestatement.h index f34b13f6d0f..1e55d4c74f1 100644 --- a/tests/unit/tests/mocks/mocksqlitestatement.h +++ b/tests/unit/tests/mocks/mocksqlitestatement.h @@ -50,6 +50,8 @@ public: SqliteDatabaseMock &database() { return *m_databaseMock; } + MOCK_METHOD(std::uintptr_t, handle, (), (const)); + private: SqliteDatabaseMock *m_databaseMock = nullptr; }; From 2520d5ce9c2725cfd3410cd3b9da96b60bd35285 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Fri, 15 Mar 2024 10:31:23 +0100 Subject: [PATCH 024/202] NanoTrace: Remove unused std::string Change-Id: I7eddcf946b9519f11e61a71861977ad2714df4e5 Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- src/libs/nanotrace/nanotracehr.cpp | 1 - src/libs/nanotrace/nanotracehr.h | 1 - 2 files changed, 2 deletions(-) diff --git a/src/libs/nanotrace/nanotracehr.cpp b/src/libs/nanotrace/nanotracehr.cpp index 083c9da69ca..5cd6e1852c0 100644 --- a/src/libs/nanotrace/nanotracehr.cpp +++ b/src/libs/nanotrace/nanotracehr.cpp @@ -134,7 +134,6 @@ void convertToString(String &string, const QImage &image) convertToString(string, dict); } -template NANOTRACE_EXPORT void convertToString(std::string &string, const QImage &image); template NANOTRACE_EXPORT void convertToString(ArgumentsString &string, const QImage &image); template diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index ded71375390..d5d8b29b46d 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -92,7 +92,6 @@ void convertToString(String &string, std::string_view text) template void convertToString(String &string, const QImage &image); -extern template NANOTRACE_EXPORT void convertToString(std::string &string, const QImage &image); extern template NANOTRACE_EXPORT void convertToString(ArgumentsString &string, const QImage &image); template From 4fc1ca984f4e314ec8f7c769aec7f28c74a92427 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Fri, 15 Mar 2024 17:20:19 +0100 Subject: [PATCH 025/202] Utils: Add number Smallstring::append Saves some overhead. Change-Id: I7d46e41f29e66cd01f5a11f16e6f78ff7a4d0aa8 Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- src/libs/utils/smallstring.h | 62 ++++++++++++------- .../unittests/utils/smallstring-test.cpp | 20 ++++++ 2 files changed, 60 insertions(+), 22 deletions(-) diff --git a/src/libs/utils/smallstring.h b/src/libs/utils/smallstring.h index 1d5eeb2438e..a91b1665392 100644 --- a/src/libs/utils/smallstring.h +++ b/src/libs/utils/smallstring.h @@ -432,6 +432,33 @@ public: setSize(newSize); } + template>> + void append(Type number) + { +#if defined(__cpp_lib_to_chars) && (__cpp_lib_to_chars >= 201611L) + // 2 bytes for the sign and because digits10 returns the floor + char buffer[std::numeric_limits::digits10 + 2]; + auto result = std::to_chars(buffer, buffer + sizeof(buffer), number); + auto endOfConversionString = result.ptr; + + append({buffer, endOfConversionString}); +#else + if constexpr (std::is_floating_point_v) { + QLocale locale{QLocale::Language::C}; + append(locale.toString(number)); + return; + } else { + // 2 bytes for the sign and because digits10 returns the floor + char buffer[std::numeric_limits::digits10 + 2]; + auto result = std::to_chars(buffer, buffer + sizeof(buffer), number); + auto endOfConversionString = result.ptr; + + append({buffer, endOfConversionString}); + } + +#endif + } + void append(QStringView string) noexcept { QStringEncoder encoder{QStringEncoder::Utf8}; @@ -478,6 +505,14 @@ public: return *this; } + template>> + BasicSmallString &operator+=(Type number) noexcept + { + append(number); + + return *this; + } + BasicSmallString &operator+=(std::initializer_list list) noexcept { appendInitializerList(list, size()); @@ -582,28 +617,12 @@ public: return joinedString; } - template>> - static BasicSmallString number(Integer number) + template>> + static BasicSmallString number(Type number) { - // 2 bytes for the sign and because digits10 returns the floor - char buffer[std::numeric_limits::digits10 + 2]; - auto result = std::to_chars(buffer, buffer + sizeof(buffer), number); - auto endOfConversionString = result.ptr; - return BasicSmallString(buffer, endOfConversionString); - } - - static BasicSmallString number(double number) noexcept - { -#if defined(__cpp_lib_to_chars) && (__cpp_lib_to_chars >= 201611L) - // 2 bytes for the sign and because digits10 returns the floor - char buffer[std::numeric_limits::digits10 + 2]; - auto result = std::to_chars(buffer, buffer + sizeof(buffer), number); - auto endOfConversionString = result.ptr; - return BasicSmallString(buffer, endOfConversionString); -#else - QLocale locale{QLocale::Language::C}; - return BasicSmallString{locale.toString(number)}; -#endif + BasicSmallString string; + string.append(number); + return string; } char &operator[](std::size_t index) noexcept { return *(data() + index); } @@ -648,7 +667,6 @@ public: friend BasicSmallString operator+(const BasicSmallString &first, const char (&second)[ArraySize]) noexcept { - return operator+(first, SmallStringView(second)); } diff --git a/tests/unit/tests/unittests/utils/smallstring-test.cpp b/tests/unit/tests/unittests/utils/smallstring-test.cpp index bdcdb44019f..ac979ef65c3 100644 --- a/tests/unit/tests/unittests/utils/smallstring-test.cpp +++ b/tests/unit/tests/unittests/utils/smallstring-test.cpp @@ -941,6 +941,24 @@ TEST(SmallString, append_empty_initializer_list) ASSERT_THAT(text, Eq("some text")); } +TEST(SmallString, append_int) +{ + SmallString text("some text"); + + text += 123; + + ASSERT_THAT(text, Eq("some text123")); +} + +TEST(SmallString, append_float) +{ + SmallString text("some text"); + + text += 123.456; + + ASSERT_THAT(text, Eq("some text123.456")); +} + TEST(SmallString, to_byte_array) { SmallString text("some text"); @@ -1839,6 +1857,8 @@ TEST(SmallString, number_to_string) ASSERT_THAT(SmallString::number(std::numeric_limits::min()), "-9223372036854775808"); ASSERT_THAT(SmallString::number(1.2), "1.2"); ASSERT_THAT(SmallString::number(-1.2), "-1.2"); + ASSERT_THAT(SmallString::number(1.2f), "1.2"); + ASSERT_THAT(SmallString::number(-1.2f), "-1.2"); } TEST(SmallString, string_view_plus_operator) From 4fcff3adcaee0b0a0fd7a2ce22e2432565a17497 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Fri, 15 Mar 2024 10:31:46 +0100 Subject: [PATCH 026/202] Nanotrace: Use number append overload Change-Id: I9da305ef75dda69ec21d0ef032adea2fa89c1023 Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- src/libs/nanotrace/nanotracehr.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index d5d8b29b46d..99ef83ad761 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -136,25 +136,25 @@ void convertToString(String &string, Callable &&callable) template void convertToString(String &string, int number) { - string.append(Utils::SmallString::number(number)); + string.append(number); } template void convertToString(String &string, long long number) { - string.append(Utils::SmallString::number(number)); + string.append(number); } template void convertToString(String &string, std::size_t number) { - string.append(Utils::SmallString::number(number)); + string.append(number); } template void convertToString(String &string, double number) { - string.append(Utils::SmallString::number(number)); + string.append(number); } template @@ -167,7 +167,7 @@ constexpr std::underlying_type_t to_underlying(Enumeration enumerat template>> void convertToString(String &string, Enumeration enumeration) { - string.append(Utils::SmallString::number(to_underlying(enumeration))); + string.append(to_underlying(enumeration)); } template From d72f55bbd03db0eadd5a22a5e487f70353e9e456 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 14 Mar 2024 20:55:51 +0100 Subject: [PATCH 027/202] QmlDesigner: Fix issues with dynamic vectors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The conversion from Qt.vectorxd was not strict enough and expressions like Qt.vector2d(7 / Constants.width, 7 / Constants.height) were read as [7, 7]. Reflection was not handled properly and the [7,7] was written on selection. Fixes: * Avoid reflection * Improve Qt.vector parsing; Move code to C++ * Handle "real" expression case and show binding indicator * Fix setting an expression. We allow the binding editor/expression indicator only on the first component of the vector Task-number: QDS-12242 Change-Id: I6707bdac860852d96d416adbe911d43fe9128e8a Reviewed-by: Henning GrĂĽndl Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../DynamicPropertiesSection.qml | 91 ++++++++++++------- .../imports/HelperWidgets/SpinBox.qml | 3 + .../propertyeditor/propertyeditorvalue.cpp | 43 ++++++++- .../propertyeditor/propertyeditorvalue.h | 6 ++ 4 files changed, 107 insertions(+), 36 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DynamicPropertiesSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DynamicPropertiesSection.qml index 39176ca82a9..9b23842fe80 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DynamicPropertiesSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DynamicPropertiesSection.qml @@ -291,53 +291,64 @@ Section { property int vecSize: 0 property var proxyValues: [] property var spinBoxes: [boxX, boxY, boxZ, boxW] + property bool block: false signal remove - onVecSizeChanged: updateProxyValues() + onVecSizeChanged: layoutVector.updateProxyValues() - spacing: StudioTheme.Values.sectionRowSpacing / 2 + spacing: StudioTheme.Values.sectionRowSpacing function isValidValue(v) { return !(v === undefined || isNaN(v)) } - function updateExpression() { + function updateExpressionFromExpression() { + if (layoutVector.block) + return + + layoutVector.backendValue.expression = layoutVector.proxyValues[0].expression + // Only the first proxy value has an expression editor enabled + } + + function updateExpressionFromValue() { + if (layoutVector.block) + return + for (let i = 0; i < vecSize; ++i) { - if (!isValidValue(proxyValues[i].value)) + if (!layoutVector.isValidValue(layoutVector.proxyValues[i].value)) return } - let expStr = "Qt.vector" + vecSize + "d("+proxyValues[0].value - for (let j=1; j < vecSize; ++j) - expStr += ", " + proxyValues[j].value + let expStr = "Qt.vector" + layoutVector.vecSize + "d(" + layoutVector.proxyValues[0].value + for (let j=1; j < layoutVector.vecSize; ++j) + expStr += ", " + layoutVector.proxyValues[j].value expStr += ")" layoutVector.backendValue.expression = expStr } function updateProxyValues() { - if (!backendValue) + if (!layoutVector.backendValue) return; - const startIndex = backendValue.expression.indexOf('(') - const endIndex = backendValue.expression.indexOf(')') - if (startIndex === -1 || endIndex === -1 || endIndex < startIndex) - return - const numberStr = backendValue.expression.slice(startIndex + 1, endIndex) - const numbers = numberStr.split(",") - if (!Array.isArray(numbers) || numbers.length !== vecSize) - return + let vals = layoutVector.backendValue.getExpressionAsVector() - let vals = [] - for (let i = 0; i < vecSize; ++i) { - vals[i] = parseFloat(numbers[i]) - if (!isValidValue(vals[i])) - return + layoutVector.block = true + + if (layoutVector.vecSize === vals.length) { + for (let j = 0; j < layoutVector.vecSize; ++j) { + layoutVector.proxyValues[j].setForceBound(false) + layoutVector.proxyValues[j].value = vals[j] + } + } else { + for (let j = 0; j < layoutVector.vecSize; ++j) { + layoutVector.proxyValues[j].setForceBound(true) // Required since the backendValue is just proxied + layoutVector.proxyValues[j].expression = layoutVector.backendValue.expression + } } - for (let j = 0; j < vecSize; ++j) - proxyValues[j].value = vals[j] + layoutVector.block = false } SecondColumnLayout { @@ -357,15 +368,18 @@ Section { tooltip: "X" } - Spacer { implicitWidth: StudioTheme.Values.controlGap } + Spacer { + implicitWidth: StudioTheme.Values.controlGap + + StudioTheme.Values.actionIndicatorWidth + } SpinBox { id: boxY + actionIndicatorVisible: false minimumValue: -9999999 maximumValue: 9999999 decimals: 2 implicitWidth: StudioTheme.Values.twoControlColumnWidth - + StudioTheme.Values.actionIndicatorWidth } Spacer { implicitWidth: StudioTheme.Values.controlLabelGap } @@ -386,14 +400,16 @@ Section { } SecondColumnLayout { - visible: vecSize > 2 + visible: layoutVector.vecSize > 2 + Spacer { implicitWidth: StudioTheme.Values.actionIndicatorWidth } + SpinBox { id: boxZ + actionIndicatorVisible: false minimumValue: -9999999 maximumValue: 9999999 decimals: 2 implicitWidth: StudioTheme.Values.twoControlColumnWidth - + StudioTheme.Values.actionIndicatorWidth } Spacer { implicitWidth: StudioTheme.Values.controlLabelGap } @@ -401,19 +417,22 @@ Section { ControlLabel { text: "Z" tooltip: "Z" - visible: vecSize > 2 + visible: layoutVector.vecSize > 2 } - Spacer { implicitWidth: StudioTheme.Values.controlGap } + Spacer { + implicitWidth: StudioTheme.Values.controlGap + + StudioTheme.Values.actionIndicatorWidth + } SpinBox { id: boxW + actionIndicatorVisible: false minimumValue: -9999999 maximumValue: 9999999 decimals: 2 implicitWidth: StudioTheme.Values.twoControlColumnWidth - + StudioTheme.Values.actionIndicatorWidth - visible: vecSize > 3 + visible: layoutVector.vecSize > 3 } Spacer { implicitWidth: StudioTheme.Values.controlLabelGap } @@ -421,7 +440,7 @@ Section { ControlLabel { text: "W" tooltip: "W" - visible: vecSize > 3 + visible: layoutVector.vecSize > 3 } Spacer { implicitWidth: StudioTheme.Values.controlGap } @@ -430,7 +449,7 @@ Section { height: 10 implicitWidth: StudioTheme.Values.twoControlColumnWidth + StudioTheme.Values.actionIndicatorWidth - visible: vecSize === 2 // Placeholder for last spinbox + visible: layoutVector.vecSize === 2 // Placeholder for last spinbox } Spacer { implicitWidth: StudioTheme.Values.twoControlColumnGap } @@ -486,9 +505,12 @@ Section { model: root.propertiesModel row: index } + PropertyLabel { text: propertyName tooltip: propertyType + Layout.alignment: Qt.AlignTop + Layout.topMargin: 6 } Loader { @@ -540,7 +562,8 @@ Section { for (let i = 0; i < vecSize; ++i) { var newProxyValue = propertyRow.createProxyBackendValue() loader.item.proxyValues.push(newProxyValue) - newProxyValue.valueChangedQml.connect(loader.item.updateExpression) + newProxyValue.valueChangedQml.connect(loader.item.updateExpressionFromValue) + newProxyValue.expressionChangedQml.connect(loader.item.updateExpressionFromExpression) loader.item.spinBoxes[i].backendValue = newProxyValue } propertyRow.backendValue.expressionChanged.connect(loader.item.updateProxyValues) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/SpinBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/SpinBox.qml index f125c459c51..04cbb78f356 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/SpinBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/SpinBox.qml @@ -22,6 +22,9 @@ Item { property alias realDragRange: spinBox.realDragRange property alias pixelsPerUnit: spinBox.pixelsPerUnit + property alias actionIndicatorEnabled: spinBox.actionIndicator.enabled + property alias actionIndicatorVisible: spinBox.actionIndicatorVisible + width: 96 implicitHeight: spinBox.height diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp index 663ebafb659..c1d91f907a9 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp @@ -153,7 +153,8 @@ void PropertyEditorValue::setExpressionWithEmit(const QString &expression) if (m_expression != expression) { setExpression(expression); m_value.clear(); - emit expressionChanged(nameAsQString()); // Note that we set the name in this case + emit expressionChanged(nameAsQString()); + emit expressionChangedQml();// Note that we set the name in this case } } @@ -180,7 +181,7 @@ bool PropertyEditorValue::isInSubState() const bool PropertyEditorValue::isBound() const { const QmlObjectNode objectNode(modelNode()); - return objectNode.isValid() && objectNode.hasBindingProperty(name()); + return m_forceBound || (objectNode.isValid() && objectNode.hasBindingProperty(name())); } bool PropertyEditorValue::isInModel() const @@ -334,6 +335,7 @@ void PropertyEditorValue::resetValue() m_expression = QString(); emit valueChanged(nameAsQString(), QVariant()); emit expressionChanged({}); + emit expressionChangedQml(); } } @@ -425,6 +427,34 @@ QStringList PropertyEditorValue::getExpressionAsList() const return generateStringList(expression()); } +QVector PropertyEditorValue::getExpressionAsVector() const +{ + const QRegularExpression rx( + QRegularExpression::anchoredPattern("Qt.vector(2|3|4)d\\((.*?)\\)")); + const QRegularExpressionMatch match = rx.match(expression()); + if (!match.hasMatch()) + return {}; + + const QStringList floats = match.captured(2).split(',', Qt::SkipEmptyParts); + + bool ok; + + const int num = match.captured(1).toInt(); + + if (num != floats.count()) + return {}; + + QVector ret; + for (const QString &number : floats) { + ret.append(number.toDouble(&ok)); + + if (!ok) + return {}; + } + + return ret; +} + bool PropertyEditorValue::idListAdd(const QString &value) { const QmlObjectNode objectNode(modelNode()); @@ -507,6 +537,15 @@ void PropertyEditorValue::openMaterialEditor(int idx) m_modelNode.view()->emitCustomNotification("select_material", {}, {idx}); } +void PropertyEditorValue::setForceBound(bool b) +{ + if (m_forceBound == b) + return; + m_forceBound = b; + + emit isBoundChanged(); +} + QStringList PropertyEditorValue::generateStringList(const QString &string) const { QString copy = string; diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h index 59236c4fe21..f621c9a5008 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h @@ -126,12 +126,15 @@ public: bool isIdList() const; Q_INVOKABLE QStringList getExpressionAsList() const; + Q_INVOKABLE QVector getExpressionAsVector() const; Q_INVOKABLE bool idListAdd(const QString &value); Q_INVOKABLE bool idListRemove(int idx); Q_INVOKABLE bool idListReplace(int idx, const QString &value); Q_INVOKABLE void commitDrop(const QString &dropData); Q_INVOKABLE void openMaterialEditor(int idx); + Q_INVOKABLE void setForceBound(bool b); + public slots: void resetValue(); void setEnumeration(const QString &scope, const QString &name); @@ -143,6 +146,8 @@ signals: void expressionChanged(const QString &name); // HACK - We use the same notifer for the backend and frontend. // If name is empty the signal is used for QML. + void expressionChangedQml(); + void exportPropertyAsAliasRequested(const QString &name); void removeAliasExportRequested(const QString &name); @@ -168,6 +173,7 @@ private: bool m_hasActiveDrag = false; bool m_isValid = false; // if the property value belongs to a non-existing complexProperty it is invalid PropertyEditorNodeWrapper *m_complexNode; + bool m_forceBound = false; }; } // namespace QmlDesigner From b163510696ba36593d09cb81768a007143980956 Mon Sep 17 00:00:00 2001 From: Teea Poldsam Date: Tue, 19 Mar 2024 14:46:53 +0200 Subject: [PATCH 028/202] Doc: Update supported platforms Fixes: QDS-12231 Change-Id: I884e723073e2b922ec594d5cc703ca55a5ebe11f Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mats Honkamaa Reviewed-by: Tim Jenssen --- .../src/qtdesignstudio-platforms.qdoc | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/doc/qtdesignstudio/src/qtdesignstudio-platforms.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-platforms.qdoc index 5a50eb2bf40..501e30adc77 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-platforms.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-platforms.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -11,20 +11,20 @@ You can install and run \QDS on several operating systems to create applications for multiple desktop, embedded, and mobile device platforms. - \section1 Development Platforms + \section1 Host Platforms \QDS is available in binary packages for the following operating systems: \list - \li \macOS 11.0 - \li Linux: - \list - \li CentOS 8.1 - \li openSUSE Leap 15.1 - \li SUSE Linux Enterprise Server 15 (SLES 15) - \li Ubuntu 20.04 - \endlist - \li Windows 10, version 2004 + \li Windows 11 + \li Windows 10 64-bit + \li \macOS 12, 13 + \list + \li ARM-based Mac + \li Intel Mac + \endlist + \li Linux Ubuntu 22.04 (latest LTS) + \li Linux Ubuntu 20.04 \endlist \note For a good user experience on Windows 10, we recommend the following @@ -36,9 +36,9 @@ \QB is available for the following design tools: \list - \li Adobe Photoshop version 24.0 - \li Adobe XD version 55.0.12 - \li Figma version 116.4 - \li Sketch version 90.0 + \li Adobe Photoshop 25.0 + \li Adobe XD 57.1.12.2 + \li Figma 116.15.15 + \li Sketch 99.1 \endlist */ From cea24ad00b86f15fbe549c1177e1852c4bed30d7 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Fri, 15 Mar 2024 11:39:24 +0100 Subject: [PATCH 029/202] Utils: Add char append to SmallString Change-Id: I644db7635010da8bc29d87926a64c4e6939d7c83 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- src/libs/utils/smallstring.h | 30 +++++++++++++++++-- .../unittests/utils/smallstring-test.cpp | 9 ++++++ 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/libs/utils/smallstring.h b/src/libs/utils/smallstring.h index a91b1665392..530022f475c 100644 --- a/src/libs/utils/smallstring.h +++ b/src/libs/utils/smallstring.h @@ -425,13 +425,28 @@ public: size_type oldSize = size(); size_type newSize = oldSize + string.size(); - reserve(optimalCapacity(newSize)); + if (fitsNotInCapacity(newSize)) + reserve(optimalCapacity(newSize)); + std::char_traits::copy(std::next(data(), static_cast(oldSize)), string.data(), string.size()); setSize(newSize); } + void append(char character) noexcept + { + size_type oldSize = size(); + size_type newSize = oldSize + 1; + + if (fitsNotInCapacity(newSize)) + reserve(optimalCapacity(newSize)); + + auto current = std::next(data(), static_cast(oldSize)); + *current = character; + setSize(newSize); + } + template>> void append(Type number) { @@ -498,6 +513,13 @@ public: return *this; } + BasicSmallString &operator+=(char character) noexcept + { + append(character); + + return *this; + } + BasicSmallString &operator+=(QStringView string) noexcept { append(string); @@ -698,8 +720,10 @@ unittest_public: bool fitsNotInCapacity(size_type capacity) const noexcept { - return (isShortString() && capacity > shortStringCapacity()) - || (!isShortString() && capacity > m_data.reference.capacity); + if (isShortString()) + return capacity > shortStringCapacity(); + + return capacity > m_data.reference.capacity; } static size_type optimalHeapCapacity(const size_type size) noexcept diff --git a/tests/unit/tests/unittests/utils/smallstring-test.cpp b/tests/unit/tests/unittests/utils/smallstring-test.cpp index ac979ef65c3..3594c838c96 100644 --- a/tests/unit/tests/unittests/utils/smallstring-test.cpp +++ b/tests/unit/tests/unittests/utils/smallstring-test.cpp @@ -959,6 +959,15 @@ TEST(SmallString, append_float) ASSERT_THAT(text, Eq("some text123.456")); } +TEST(SmallString, append_character) +{ + SmallString text("some text"); + + text += 'x'; + + ASSERT_THAT(text, Eq("some textx")); +} + TEST(SmallString, to_byte_array) { SmallString text("some text"); From 14417135a4a12992010eefb1362dffb03b67e3f8 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Fri, 15 Mar 2024 11:39:43 +0100 Subject: [PATCH 030/202] Nanotrace: Use char append Change-Id: I5a37eff39acd787265c5951237a3f17b3ea6eb5f Reviewed-by: Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- src/libs/nanotrace/nanotracehr.h | 38 ++++++++++++++++---------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index 99ef83ad761..d7f428774ce 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -84,9 +84,9 @@ inline constexpr IsDictonary isDictonary; template void convertToString(String &string, std::string_view text) { - string.append(R"(")"); + string.append('\"'); string.append(text); - string.append(R"(")"); + string.append('\"'); } template @@ -97,25 +97,25 @@ extern template NANOTRACE_EXPORT void convertToString(ArgumentsString &string, c template void convertToString(String &string, const char (&text)[size]) { - string.append(R"(")"); + string.append('\"'); string.append(std::string_view{text, size - 1}); - string.append(R"(")"); + string.append('\"'); } template void convertToString(String &string, QStringView text) { - string.append(R"(")"); + string.append('\"'); string.append(Utils::PathString{text}); - string.append(R"(")"); + string.append('\"'); } template void convertToString(String &string, const QByteArray &text) { - string.append(R"(")"); + string.append('\"'); string.append(std::string_view(text.data(), Utils::usize(text))); - string.append(R"(")"); + string.append('\"'); } template @@ -201,11 +201,11 @@ void convertArrayEntryToString(String &string, const Value &value) template void convertArrayToString(String &string, const IsArray &, Entries &...entries) { - string.append(R"([)"); + string.append('['); (convertArrayEntryToString(string, entries), ...); if (sizeof...(entries)) string.pop_back(); - string.append("]"); + string.append(']'); } template @@ -219,19 +219,19 @@ void convertDictonaryEntryToString(String &string, const std::tuple { const auto &[key, value] = argument; convertToString(string, key); - string.append(":"); + string.append(':'); convertToString(string, value); - string.append(","); + string.append(','); } template void convertDictonaryToString(String &string, const IsDictonary &, Entries &...entries) { - string.append(R"({)"); + string.append('{'); (convertDictonaryEntryToString(string, entries), ...); if (sizeof...(entries)) string.pop_back(); - string.append("}"); + string.append('}'); } template @@ -243,16 +243,16 @@ void convertToString(String &string, const std::tuple template typename Container, typename... Arguments> void convertToString(String &string, const Container &container) { - string.append("["); + string.append('['); for (const auto &entry : container) { convertToString(string, entry); - string.append(","); + string.append(','); } if (container.size()) string.pop_back(); - string.append("]"); + string.append(']'); } namespace Internal { @@ -262,12 +262,12 @@ String toArguments(Arguments &&...arguments) { String text; constexpr auto argumentCount = sizeof...(Arguments); - text.append("{"); + text.append('{'); (convertDictonaryEntryToString(text, arguments), ...); if (argumentCount) text.pop_back(); - text.append("}"); + text.append('}'); return text; } From 438a208ffb42547e87996b8f8deb1cf5119ed617 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Fri, 15 Mar 2024 13:28:18 +0100 Subject: [PATCH 031/202] Nanotrace: Use static string for arguments Adding many small strings to a string can have overhead because there have been checks. A static string simply cannot grow, so that overhead is removed. It has the drawback that it can overflow but in that case simply no arguments are shown. Change-Id: Ia9106b6c49e396453eeb75b28df5a543a28dc8c9 Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- src/libs/nanotrace/CMakeLists.txt | 1 + src/libs/nanotrace/nanotracehr.cpp | 2 +- src/libs/nanotrace/nanotracehr.h | 22 ++++-- src/libs/nanotrace/staticstring.h | 116 +++++++++++++++++++++++++++++ 4 files changed, 132 insertions(+), 9 deletions(-) create mode 100644 src/libs/nanotrace/staticstring.h diff --git a/src/libs/nanotrace/CMakeLists.txt b/src/libs/nanotrace/CMakeLists.txt index 011c26f0492..50693644ea5 100644 --- a/src/libs/nanotrace/CMakeLists.txt +++ b/src/libs/nanotrace/CMakeLists.txt @@ -4,6 +4,7 @@ add_qtc_library(Nanotrace nanotraceglobals.h nanotrace.cpp nanotrace.h nanotracehr.cpp nanotracehr.h + staticstring.h PUBLIC_DEPENDS Qt::Core Qt::Gui PROPERTIES CXX_VISIBILITY_PRESET default diff --git a/src/libs/nanotrace/nanotracehr.cpp b/src/libs/nanotrace/nanotracehr.cpp index 5cd6e1852c0..6a1ab441f5a 100644 --- a/src/libs/nanotrace/nanotracehr.cpp +++ b/src/libs/nanotrace/nanotracehr.cpp @@ -67,7 +67,7 @@ void printEvent(std::ostream &out, const TraceEvent &event, qint64 processId, st out << R"(,"flow_in":true)"; } - if (event.arguments.size()) + if (event.arguments.size() && event.arguments.size() != std::numeric_limits::max()) out << R"(,"args":)" << event.arguments; out << "}"; diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index d7f428774ce..1409db759b2 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -5,6 +5,8 @@ #include "nanotraceglobals.h" +#include "staticstring.h" + #include #include @@ -42,7 +44,7 @@ enum class Tracing { IsDisabled, IsEnabled }; # define NO_UNIQUE_ADDRESS #endif -using ArgumentsString = Utils::BasicSmallString<510>; +using ArgumentsString = StaticString<1016>; namespace Literals { struct TracerLiteral @@ -258,9 +260,8 @@ void convertToString(String &string, const Container &container) namespace Internal { template -String toArguments(Arguments &&...arguments) +void toArguments(String &text, Arguments &&...arguments) { - String text; constexpr auto argumentCount = sizeof...(Arguments); text.append('{'); (convertDictonaryEntryToString(text, arguments), ...); @@ -268,8 +269,6 @@ String toArguments(Arguments &&...arguments) text.pop_back(); text.append('}'); - - return text; } inline std::string_view toArguments(std::string_view arguments) @@ -280,7 +279,10 @@ inline std::string_view toArguments(std::string_view arguments) template void setArguments(String &eventArguments) { - eventArguments = {}; + if constexpr (std::is_same_v) + eventArguments = {}; + else + eventArguments.clear(); } template @@ -296,7 +298,11 @@ template !std::is_same_v, R"(The arguments type of the tracing event queue is a string view. You can only provide trace token arguments as TracerLiteral (""_t).)"); - eventArguments = Internal::toArguments(std::forward(arguments)...); + if constexpr (std::is_same_v) + eventArguments = {}; + else + eventArguments.clear(); + Internal::toArguments(eventArguments, std::forward(arguments)...); } } // namespace Internal @@ -513,7 +519,7 @@ template class EventQueue { using TraceEventsSpan = Utils::span; - using TraceEvents = std::array; + using TraceEvents = std::array; public: using IsActive = std::true_type; diff --git a/src/libs/nanotrace/staticstring.h b/src/libs/nanotrace/staticstring.h new file mode 100644 index 00000000000..2f845919bd1 --- /dev/null +++ b/src/libs/nanotrace/staticstring.h @@ -0,0 +1,116 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#pragma once + +#include + +#if !(defined(__cpp_lib_to_chars) && (__cpp_lib_to_chars >= 201611L)) +# include +#endif + +#include +#include +#include + +namespace NanotraceHR { + +template +class StaticString +{ +public: + StaticString() = default; + StaticString(const StaticString &) = delete; + StaticString &operator=(const StaticString &) = delete; + + char *data() { return m_data.data(); } + + const char *data() const { return m_data.data(); } + + void append(Utils::SmallStringView string) noexcept + { + auto newSize = m_size + string.size(); + + if (newSize < Capacity) { + std::char_traits::copy(std::next(data(), static_cast(m_size)), + string.data(), + string.size()); + m_size = newSize; + } else { + m_size = std::numeric_limits::max(); + } + } + + void append(char character) noexcept + { + auto newSize = m_size + 1; + + if (newSize < Capacity) { + auto current = std::next(data(), static_cast(m_size)); + *current = character; + + m_size = newSize; + } else { + m_size = std::numeric_limits::max(); + } + } + + template>> + void append(Type number) + { +#if !(defined(__cpp_lib_to_chars) && (__cpp_lib_to_chars >= 201611L)) + if constexpr (std::is_floating_point_v) { + QLocale locale{QLocale::Language::C}; + append(locale.toString(number).toStdString()); + return; + } +#endif + // 2 bytes for the sign and because digits10 returns the floor + char buffer[std::numeric_limits::digits10 + 2]; + auto result = std::to_chars(buffer, buffer + sizeof(buffer), number); + auto endOfConversionString = result.ptr; + + append({buffer, endOfConversionString}); + } + + void pop_back() { --m_size; } + + StaticString &operator+=(Utils::SmallStringView string) noexcept + { + append(string); + + return *this; + } + + StaticString &operator+=(char character) noexcept + { + append(character); + + return *this; + } + + template>> + StaticString &operator+=(Type number) noexcept + { + append(number); + + return *this; + } + + bool isValid() const { return m_size != std::numeric_limits::max(); } + + std::size_t size() const { return m_size; } + + friend std::ostream &operator<<(std::ostream &out, const StaticString &text) + { + return out << std::string_view{text.data(), text.size()}; + } + + void clear() { m_size = 0; } + +private: + std::array m_data; + std::size_t m_size = 0; +}; + +} // namespace NanotraceHR From ef96d232e5355bf3d855f74c48c8d41f8c9bb89b Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Fri, 15 Mar 2024 14:11:20 +0100 Subject: [PATCH 032/202] QmlDesigner: Enable removal of trace points The value function could stop the optimizer to remove the trace point. The convert to string function is only called if there is a enabled trace point. Change-Id: I6ead32638438438602ac2116ffd353c6ed70b32d Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- .../projectstorage/projectstorage.h | 21 +-- .../projectstorage/projectstorageinfotypes.h | 11 ++ .../projectstorage/projectstoragetypes.h | 136 ++++++++++-------- 3 files changed, 97 insertions(+), 71 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index 344afae756f..b469dccda2d 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -1619,6 +1619,7 @@ private: Prototypes &relinkablePrototypes, Prototypes &relinkableExtensions) { + using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"synchronize exported types"_t, projectStorageCategory()}; std::sort(exportedTypes.begin(), exportedTypes.end(), [](auto &&first, auto &&second) { @@ -1662,7 +1663,7 @@ private: using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"insert exported type"_t, projectStorageCategory(), - keyValue("exported type", NanotraceHR::value(type))}; + keyValue("exported type", type)}; if (!type.moduleId) throw QmlDesigner::ModuleDoesNotExists{}; @@ -1694,8 +1695,8 @@ private: if (view.typeId != type.typeId) { NanotraceHR::Tracer tracer{"update exported type"_t, projectStorageCategory(), - keyValue("exported type", NanotraceHR::value(type)), - keyValue("exported type view", NanotraceHR::value(view))}; + keyValue("exported type", type), + keyValue("exported type view", view)}; handlePropertyDeclarationWithPropertyType(view.typeId, relinkablePropertyDeclarations); handleAliasPropertyDeclarationsWithPropertyType(view.typeId, @@ -1711,7 +1712,7 @@ private: auto remove = [&](const Storage::Synchronization::ExportedTypeView &view) { NanotraceHR::Tracer tracer{"remove exported type"_t, projectStorageCategory(), - keyValue("exported type view", NanotraceHR::value(view))}; + keyValue("exported type view", view)}; handlePropertyDeclarationWithPropertyType(view.typeId, relinkablePropertyDeclarations); handleAliasPropertyDeclarationsWithPropertyType(view.typeId, @@ -2012,7 +2013,7 @@ private: using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"insert import"_t, projectStorageCategory(), - keyValue("import", NanotraceHR::value(import)), + keyValue("import", import), keyValue("import kind", importKind), keyValue("source id", import.sourceId)}; @@ -2028,7 +2029,7 @@ private: NanotraceHR::Tracer tracer{"insert indirect import"_t, projectStorageCategory(), - keyValue("import", NanotraceHR::value(import)), + keyValue("import", import), keyValue("import kind", exportedImportKind)}; auto indirectImportId = insertDocumentImport(additionImport, @@ -2560,7 +2561,7 @@ private: using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"fetch imported type name id and type id"_t, projectStorageCategory(), - keyValue("impoted type name", NanotraceHR::value(typeName)), + keyValue("impoted type name", typeName), keyValue("source id", sourceId)}; TypeId typeId; @@ -2587,8 +2588,8 @@ private: using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"synchronize prototype and extension"_t, projectStorageCategory(), - keyValue("prototype", NanotraceHR::value(type.prototype)), - keyValue("extension", NanotraceHR::value(type.extension)), + keyValue("prototype", type.prototype), + keyValue("extension", type.extension), keyValue("type id", type.typeId), keyValue("source id", type.sourceId)}; @@ -2673,7 +2674,7 @@ private: NanotraceHR::Tracer tracer{"fetch imported type name id"_t, projectStorageCategory(), keyValue("imported type name", importedType.name), - keyValue("import", NanotraceHR::value(importedType.import)), + keyValue("import", importedType.import), keyValue("type name kind", "qualified exported"sv)}; ImportId importId = storage.fetchImportId(sourceId, importedType.import); diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h index 427c0ff8d61..4dd023ae9fa 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h @@ -206,6 +206,17 @@ public: VersionNumber major; VersionNumber minor; }; + +template +void convertToString(String &string, const Version &version) +{ + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("major version", version.major.value), + keyValue("minor version", version.minor.value)); + + convertToString(string, dict); +} } // namespace QmlDesigner::Storage namespace QmlDesigner::Storage::Info { diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h index 6199911497b..f3272a71157 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h @@ -54,6 +54,17 @@ public: using Imports = std::vector; +template +void convertToString(String &string, const Import &import) +{ + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("module id", import.moduleId), + keyValue("source id", import.sourceId), + keyValue("version", import.version)); + convertToString(string, dict); +} + namespace Synchronization { enum class TypeNameKind { Exported = 1, QualifiedExported = 2 }; @@ -274,6 +285,19 @@ public: using ExportedTypes = std::vector; +template +void convertToString(String &string, const ExportedType &exportedType) +{ + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", exportedType.name), + keyValue("module id", exportedType.moduleId), + keyValue("type id", exportedType.typeId), + keyValue("version", exportedType.version)); + + convertToString(string, dict); +} + class ExportedTypeView { public: @@ -304,8 +328,59 @@ public: ExportedTypeNameId exportedTypeNameId; }; +template +void convertToString(String &string, const ExportedTypeView &exportedType) +{ + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", exportedType.name), + keyValue("module id", exportedType.moduleId), + keyValue("type id", exportedType.typeId), + keyValue("version", exportedType.version), + keyValue("version", exportedType.exportedTypeNameId)); + + convertToString(string, dict); +} + using ImportedTypeName = std::variant; +template +void convertToString(String &string, const ImportedTypeName &typeName) +{ + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + + struct Dispatcher + { + static const QmlDesigner::Storage::Import &nullImport() + { + static QmlDesigner::Storage::Import import; + + return import; + } + + void operator()(const QmlDesigner::Storage::Synchronization::ImportedType &importedType) const + { + auto dict = dictonary(keyValue("name", importedType.name), keyValue("import", "empty")); + + convertToString(string, dict); + } + + void operator()( + const QmlDesigner::Storage::Synchronization::QualifiedImportedType &qualifiedImportedType) const + { + auto dict = dictonary(keyValue("name", qualifiedImportedType.name), + keyValue("import", qualifiedImportedType.import)); + + convertToString(string, dict); + } + + String &string; + }; + + std::visit(Dispatcher{string}, typeName); +} + class EnumeratorDeclaration { public: @@ -879,64 +954,3 @@ public: } // namespace Synchronization } // namespace QmlDesigner::Storage - -namespace NanotraceHR { - -inline auto value(const QmlDesigner::Storage::Version &version) -{ - return dictonary(keyValue("major version", version.major.value), - keyValue("minor version", version.minor.value)); -} - -inline auto value(const QmlDesigner::Storage::Import &import) -{ - return dictonary(keyValue("module id", import.moduleId), - keyValue("source id", import.sourceId), - keyValue("version", value(import.version))); -} - -inline auto value(const QmlDesigner::Storage::Synchronization::ExportedType &exportedType) -{ - return dictonary(keyValue("name", exportedType.name), - keyValue("module id", exportedType.moduleId), - keyValue("type id", exportedType.typeId), - keyValue("version", value(exportedType.version))); -} - -inline auto value(const QmlDesigner::Storage::Synchronization::ExportedTypeView &exportedType) -{ - return dictonary(keyValue("name", exportedType.name), - keyValue("module id", exportedType.moduleId), - keyValue("type id", exportedType.typeId), - keyValue("version", value(exportedType.version)), - keyValue("version", exportedType.exportedTypeNameId)); -} - -inline auto value(const QmlDesigner::Storage::Synchronization::ImportedTypeName &typeName) -{ - struct Dispatcher - { - static const QmlDesigner::Storage::Import &nullImport() - { - static QmlDesigner::Storage::Import import; - - return import; - } - - auto operator()(const QmlDesigner::Storage::Synchronization::ImportedType &importedType) const - { - return dictonary(keyValue("name", importedType.name), - keyValue("import", value(nullImport()))); - } - - auto operator()( - const QmlDesigner::Storage::Synchronization::QualifiedImportedType &qualifiedImportedType) const - { - return dictonary(keyValue("name", qualifiedImportedType.name), - keyValue("import", value(qualifiedImportedType.import))); - } - }; - - return std::visit(Dispatcher{}, typeName); -} -} // namespace NanotraceHR From 645acc4fc4a66da55be5045c7899b0269117fb98 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Fri, 15 Mar 2024 21:20:54 +0100 Subject: [PATCH 033/202] QmlDesigner: Speedup fetchTypeId by 5x We gave now sqlite some hints how to merge the tables. That makes them much faster. Still it is quite slow but we could introduce a caching table. Change-Id: I1f75820a53d648621a82dd719e8d8deb97e8d207 Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- .../projectstorage/projectstorage.h | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index b469dccda2d..2fbabcd0819 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -3773,12 +3773,19 @@ public: "etn.minorVersion DESC NULLS FIRST LIMIT 1", database}; mutable ReadStatement<1, 1> selectTypeIdForImportedTypeNameNamesStatement{ - "SELECT typeId FROM importedTypeNames AS itn JOIN documentImports AS di ON " - "importOrSourceId=sourceId JOIN exportedTypeNames AS etn USING(moduleId) WHERE " - "itn.kind=1 AND importedTypeNameId=?1 AND itn.name=etn.name AND " - "(di.majorVersion IS NULL OR (di.majorVersion=etn.majorVersion AND (di.minorVersion IS " - "NULL OR di.minorVersion>=etn.minorVersion))) ORDER BY di.kind, etn.majorVersion DESC " - "NULLS FIRST, etn.minorVersion DESC NULLS FIRST LIMIT 1", + "WITH " + " importTypeNames(moduleId, name, kind, majorVersion, minorVersion) AS MATERIALIZED ( " + " SELECT moduleId, name, di.kind, majorVersion, minorVersion " + " FROM importedTypeNames AS itn JOIN documentImports AS di ON " + " importOrSourceId=sourceId " + " WHERE " + " importedTypeNameId=?1 AND itn.kind=1) " + "SELECT typeId FROM importTypeNames AS itn " + " JOIN exportedTypeNames AS etn USING(moduleId, name) " + "WHERE (itn.majorVersion IS NULL OR (itn.majorVersion=etn.majorVersion " + " AND (itn.minorVersion IS NULL OR itn.minorVersion>=etn.minorVersion))) " + "ORDER BY itn.kind, etn.majorVersion DESC NULLS FIRST, etn.minorVersion DESC NULLS FIRST " + "LIMIT 1", database}; WriteStatement<0> deleteAllSourcesStatement{"DELETE FROM sources", database}; WriteStatement<0> deleteAllSourceContextsStatement{"DELETE FROM sourceContexts", database}; From 1087375b28604498393d0c3bf8cf81947551765a Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 18 Mar 2024 13:03:18 +0100 Subject: [PATCH 034/202] Nanotrace: Add instant event Sometimes we have events without any duration Change-Id: Iae81585051129b5c6a7983dff5e45161d6b6c389 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- src/libs/nanotrace/nanotracehr.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index 1409db759b2..8fd9d1669b0 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -1177,6 +1177,10 @@ public: return std::pair(); } + template + void threadEvent(ArgumentType, Arguments &&...) + {} + static constexpr bool isActive() { return false; } }; @@ -1264,6 +1268,24 @@ public: std::forward_as_tuple(PrivateTag{}, traceName, bindId, m_self)}; } + template + void threadEvent(ArgumentType traceName, Arguments &&...arguments) + { + if (isEnabled == IsEnabled::No) + return; + + auto &traceEvent = getTraceEvent(m_eventQueue); + + traceEvent.time = Clock::now(); + traceEvent.name = std::move(traceName); + traceEvent.category = traceName; + traceEvent.type = 'i'; + traceEvent.id = 0; + traceEvent.bindId = 0; + traceEvent.flow = IsFlow::No; + Internal::setArguments(traceEvent.arguments, std::forward(arguments)...); + } + EnabledEventQueue &eventQueue() const { return m_eventQueue; } std::string_view name() const { return m_name; } From 99def2c3377a81ee74f0503a9a94958d13fe211b Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 18 Mar 2024 11:46:21 +0100 Subject: [PATCH 035/202] Sqlite: trace exceptions That makes it easier to see what got wrong in the trace. So less debugging. Change-Id: I26ec3d6a6f81cbd20871260a2b32123343a20618 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- src/libs/sqlite/CMakeLists.txt | 2 +- src/libs/sqlite/sqlitebasestatement.cpp | 30 ------------------- src/libs/sqlite/sqlitebasestatement.h | 19 +------------ src/libs/sqlite/sqliteexception.cpp | 30 +++++++++++++++++-- src/libs/sqlite/sqliteexception.h | 9 ++++-- src/libs/sqlite/sqlitetracing.cpp | 38 +++++++++++++++++++++++++ src/libs/sqlite/sqlitetracing.h | 29 +++++++++++++++++++ 7 files changed, 103 insertions(+), 54 deletions(-) create mode 100644 src/libs/sqlite/sqlitetracing.cpp create mode 100644 src/libs/sqlite/sqlitetracing.h diff --git a/src/libs/sqlite/CMakeLists.txt b/src/libs/sqlite/CMakeLists.txt index 2943d1e77ac..f86f31871a4 100644 --- a/src/libs/sqlite/CMakeLists.txt +++ b/src/libs/sqlite/CMakeLists.txt @@ -58,7 +58,7 @@ add_qtc_library(Sqlite sqlitesessionchangeset.cpp sqlitesessionchangeset.h sqlitesessions.cpp sqlitesessions.h sqlitetable.h - sqlitetransaction.h + sqlitetracing.cpp sqlitetracing.h sqlitetransaction.h sqlitevalue.h sqlitewritestatement.h diff --git a/src/libs/sqlite/sqlitebasestatement.cpp b/src/libs/sqlite/sqlitebasestatement.cpp index 9fa1638d382..c904f3ae3ed 100644 --- a/src/libs/sqlite/sqlitebasestatement.cpp +++ b/src/libs/sqlite/sqlitebasestatement.cpp @@ -26,38 +26,8 @@ extern "C" int sqlite3_carray_bind( namespace Sqlite { -TraceFile &traceFile() -{ - static TraceFile traceFile{"tracing.json"}; - - return traceFile; -} - -namespace { - -thread_local NanotraceHR::EventQueue - eventQueue(traceFile()); - -NanotraceHR::StringViewWithStringArgumentsCategory &sqliteLowLevelCategory() -{ - thread_local NanotraceHR::StringViewWithStringArgumentsCategory - sqliteLowLevelCategory_{"sqlite low level"_t, eventQueue, sqliteLowLevelCategory}; - return sqliteLowLevelCategory_; -} - using NanotraceHR::keyValue; -} // namespace - -NanotraceHR::StringViewWithStringArgumentsCategory &sqliteHighLevelCategory() -{ - thread_local NanotraceHR::StringViewWithStringArgumentsCategory - sqliteHighLevelCategory_{"sqlite high level"_t, eventQueue, sqliteHighLevelCategory}; - - return sqliteHighLevelCategory_; -} - BaseStatement::BaseStatement(Utils::SmallStringView sqlStatement, Database &database) : m_database(database) { diff --git a/src/libs/sqlite/sqlitebasestatement.h b/src/libs/sqlite/sqlitebasestatement.h index 9f81bfb9e26..81b31473f83 100644 --- a/src/libs/sqlite/sqlitebasestatement.h +++ b/src/libs/sqlite/sqlitebasestatement.h @@ -9,6 +9,7 @@ #include "sqliteblob.h" #include "sqliteexception.h" #include "sqliteids.h" +#include "sqlitetracing.h" #include "sqlitetransaction.h" #include "sqlitevalue.h" @@ -30,8 +31,6 @@ using std::int64_t; namespace Sqlite { -using namespace NanotraceHR::Literals; - class Database; class DatabaseBackend; @@ -44,22 +43,6 @@ constexpr static std::underlying_type_t to_underlying(Enumeration e return static_cast>(enumeration); } -constexpr NanotraceHR::Tracing sqliteTracingStatus() -{ -#ifdef ENABLE_SQLITE_TRACING - return NanotraceHR::Tracing::IsEnabled; -#else - return NanotraceHR::Tracing::IsDisabled; -#endif -} - -using TraceFile = NanotraceHR::TraceFile; - -SQLITE_EXPORT TraceFile &traceFile(); - -SQLITE_EXPORT NanotraceHR::StringViewWithStringArgumentsCategory & -sqliteHighLevelCategory(); - class SQLITE_EXPORT BaseStatement { public: diff --git a/src/libs/sqlite/sqliteexception.cpp b/src/libs/sqlite/sqliteexception.cpp index b5f581ad685..bb4a474adb0 100644 --- a/src/libs/sqlite/sqliteexception.cpp +++ b/src/libs/sqlite/sqliteexception.cpp @@ -3,14 +3,20 @@ #include "sqliteexception.h" +#include "sqlitetracing.h" + #include +#include + #include #include namespace Sqlite { +using NanotraceHR::keyValue; + const char *Exception::what() const noexcept { return "Sqlite::Exception"; @@ -18,7 +24,10 @@ const char *Exception::what() const noexcept const char *ExceptionWithMessage::what() const noexcept { - return "Sqlite::ExceptionWithMessage"; + static Utils::SmallString text = Utils::SmallString::join( + {"Sqlite::ExceptionWithMessage", m_sqliteErrorMessage}); + + return text.data(); } void ExceptionWithMessage::printWarning() const @@ -26,6 +35,13 @@ void ExceptionWithMessage::printWarning() const qWarning() << what() << m_sqliteErrorMessage; } +StatementIsBusy::StatementIsBusy(Utils::SmallString &&sqliteErrorMessage) + : ExceptionWithMessage{std::move(sqliteErrorMessage)} +{ + sqliteHighLevelCategory().threadEvent("StatementIsBusy"_t, + keyValue("error message", std::string_view{what()})); +} + const char *StatementIsBusy::what() const noexcept { return "Sqlite::StatementIsBusy"; @@ -36,9 +52,19 @@ const char *DatabaseIsBusy::what() const noexcept return "Sqlite::DatabaseIsBusy"; } +StatementHasError::StatementHasError(Utils::SmallString &&sqliteErrorMessage) + : ExceptionWithMessage{std::move(sqliteErrorMessage)} +{ + sqliteHighLevelCategory().threadEvent("StatementHasError"_t, + keyValue("error message", std::string_view{what()})); +} + const char *StatementHasError::what() const noexcept { - return "Sqlite::StatementHasError"; + static Utils::SmallString text = Utils::SmallString::join( + {"Sqlite::StatementHasError: ", message()}); + + return text.data(); } const char *StatementIsMisused::what() const noexcept diff --git a/src/libs/sqlite/sqliteexception.h b/src/libs/sqlite/sqliteexception.h index f0cadfc7483..17a0639e19f 100644 --- a/src/libs/sqlite/sqliteexception.h +++ b/src/libs/sqlite/sqliteexception.h @@ -23,13 +23,15 @@ public: class SQLITE_EXPORT ExceptionWithMessage : public Exception { public: - ExceptionWithMessage(Utils::SmallString &&sqliteErrorMessage = Utils::SmallString{}) + ExceptionWithMessage(Utils::SmallString &&sqliteErrorMessage = {}) : m_sqliteErrorMessage(std::move(sqliteErrorMessage)) {} const char *what() const noexcept override; void printWarning() const; + std::string_view message() const noexcept { return m_sqliteErrorMessage; } + private: Utils::SmallString m_sqliteErrorMessage; }; @@ -37,7 +39,7 @@ private: class SQLITE_EXPORT StatementIsBusy : public ExceptionWithMessage { public: - using ExceptionWithMessage::ExceptionWithMessage; + StatementIsBusy(Utils::SmallString &&sqliteErrorMessage); const char *what() const noexcept override; }; @@ -90,7 +92,8 @@ public: class SQLITE_EXPORT StatementHasError : public ExceptionWithMessage { public: - using ExceptionWithMessage::ExceptionWithMessage; + StatementHasError(Utils::SmallString &&sqliteErrorMessage); + const char *what() const noexcept override; }; diff --git a/src/libs/sqlite/sqlitetracing.cpp b/src/libs/sqlite/sqlitetracing.cpp new file mode 100644 index 00000000000..700546f1467 --- /dev/null +++ b/src/libs/sqlite/sqlitetracing.cpp @@ -0,0 +1,38 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "sqlitetracing.h" + +namespace Sqlite { + +TraceFile &traceFile() +{ + static TraceFile traceFile{"tracing.json"}; + + return traceFile; +} + +namespace { + +thread_local NanotraceHR::EventQueue + eventQueue(traceFile()); + +} // namespace + +NanotraceHR::StringViewWithStringArgumentsCategory &sqliteLowLevelCategory() +{ + thread_local NanotraceHR::StringViewWithStringArgumentsCategory + sqliteLowLevelCategory_{"sqlite low level"_t, eventQueue, sqliteLowLevelCategory}; + return sqliteLowLevelCategory_; +} + +NanotraceHR::StringViewWithStringArgumentsCategory &sqliteHighLevelCategory() +{ + thread_local NanotraceHR::StringViewWithStringArgumentsCategory + sqliteHighLevelCategory_{"sqlite high level"_t, eventQueue, sqliteHighLevelCategory}; + + return sqliteHighLevelCategory_; +} + +} // namespace Sqlite diff --git a/src/libs/sqlite/sqlitetracing.h b/src/libs/sqlite/sqlitetracing.h new file mode 100644 index 00000000000..8dadc6de0db --- /dev/null +++ b/src/libs/sqlite/sqlitetracing.h @@ -0,0 +1,29 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "sqliteglobal.h" + +#include + +namespace Sqlite { +using namespace NanotraceHR::Literals; + +constexpr NanotraceHR::Tracing sqliteTracingStatus() +{ +#ifdef ENABLE_SQLITE_TRACING + return NanotraceHR::Tracing::IsEnabled; +#else + return NanotraceHR::Tracing::IsDisabled; +#endif +} + +using TraceFile = NanotraceHR::TraceFile; + +SQLITE_EXPORT TraceFile &traceFile(); + +NanotraceHR::StringViewWithStringArgumentsCategory &sqliteLowLevelCategory(); + +SQLITE_EXPORT NanotraceHR::StringViewWithStringArgumentsCategory & +sqliteHighLevelCategory(); + +} // namespace Sqlite From 26870406cdf45a56a95b9b00df7eff8828f124fc Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 18 Mar 2024 12:23:42 +0100 Subject: [PATCH 036/202] QmlDesigner: Trace project storage exceptions Change-Id: I58c261bea4cca6c5bede0d2ad504be8bc687ddc6 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- .../projectstorage/projectstorage.cpp | 16 --- .../projectstorage/projectstorage.h | 14 +-- .../projectstorageexceptions.cpp | 107 +++++++++++++++++- .../projectstorage/projectstorageexceptions.h | 23 +++- .../tracing/qmldesignertracing.cpp | 19 +++- .../designercore/tracing/qmldesignertracing.h | 14 +++ .../projectstorageupdater-test.cpp | 12 +- 7 files changed, 170 insertions(+), 35 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp index 25588efd9c7..2bef2244d70 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp @@ -3,22 +3,6 @@ #include "projectstorage.h" -#include - #include -namespace QmlDesigner { - -NanotraceHR::StringViewWithStringArgumentsCategory &projectStorageCategory() -{ - thread_local NanotraceHR::StringViewWithStringArgumentsCategory - projectStorageCategory_{"project storage"_t, - Tracing::eventQueueWithStringArguments(), - projectStorageCategory}; - - return projectStorageCategory_; -} - -} // namespace QmlDesigner - template class QmlDesigner::ProjectStorage; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index 2fbabcd0819..e1a10b87374 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -9,6 +9,8 @@ #include "sourcepathcachetypes.h" #include "storagecache.h" +#include + #include #include #include @@ -28,17 +30,7 @@ namespace QmlDesigner { using namespace NanotraceHR::Literals; -constexpr NanotraceHR::Tracing projectStorageTracingStatus() -{ -#ifdef ENABLE_PROJECT_STORAGE_TRACING - return NanotraceHR::Tracing::IsEnabled; -#else - return NanotraceHR::Tracing::IsDisabled; -#endif -} - -[[gnu::pure]] NanotraceHR::StringViewWithStringArgumentsCategory & -projectStorageCategory(); +using ProjectStorageTracing::projectStorageCategory; template class ProjectStorage final : public ProjectStorageInterface diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp index 0f70ccf4075..a5dc60c4fa4 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp @@ -3,38 +3,85 @@ #include "projectstorageexceptions.h" +#include + namespace QmlDesigner { +using namespace NanotraceHR::Literals; +using NanotraceHR::keyValue; + +namespace { +auto &category() +{ + return ProjectStorageTracing::projectStorageCategory(); +} +} // namespace + +NoSourcePathForInvalidSourceId::NoSourcePathForInvalidSourceId() +{ + category().threadEvent("NoSourcePathForInvalidSourceId"_t); +} + const char *NoSourcePathForInvalidSourceId::what() const noexcept { return "You cannot get a file path for an invalid file path id!"; } +NoSourceContextPathForInvalidSourceContextId::NoSourceContextPathForInvalidSourceContextId() +{ + category().threadEvent("NoSourceContextPathForInvalidSourceContextId"_t); +} + const char *NoSourceContextPathForInvalidSourceContextId::what() const noexcept { return "You cannot get a directory path for an invalid directory path id!"; } +SourceContextIdDoesNotExists::SourceContextIdDoesNotExists() +{ + category().threadEvent("SourceContextIdDoesNotExists"_t); +} + const char *SourceContextIdDoesNotExists::what() const noexcept { return "The source context id does not exist in the database!"; } +SourceIdDoesNotExists::SourceIdDoesNotExists() +{ + category().threadEvent("SourceIdDoesNotExists"_t); +} + const char *SourceIdDoesNotExists::what() const noexcept { return "The source id does not exist in the database!"; } +TypeHasInvalidSourceId::TypeHasInvalidSourceId() +{ + category().threadEvent("TypeHasInvalidSourceId"_t); +} + const char *TypeHasInvalidSourceId::what() const noexcept { return "The source id is invalid!"; } +ModuleDoesNotExists::ModuleDoesNotExists() +{ + category().threadEvent("ModuleDoesNotExists"_t); +} + const char *ModuleDoesNotExists::what() const noexcept { return "The module does not exist!"; } +ModuleAlreadyExists::ModuleAlreadyExists() +{ + category().threadEvent("ModuleAlreadyExists"_t); +} + const char *ModuleAlreadyExists::what() const noexcept { return "The module does already exist!"; @@ -45,48 +92,97 @@ TypeNameDoesNotExists::TypeNameDoesNotExists(std::string_view typeName, SourceId "TypeNameDoesNotExists"sv, Utils::SmallString::join( {"type: ", typeName, ", source id: ", Utils::SmallString::number(sourceId.internalId())})} -{} +{ + category().threadEvent("TypeNameDoesNotExists"_t, + keyValue("type name", typeName), + keyValue("source id", sourceId)); +} + +PropertyNameDoesNotExists::PropertyNameDoesNotExists() +{ + category().threadEvent("PropertyNameDoesNotExists"_t); +} const char *PropertyNameDoesNotExists::what() const noexcept { return "The property name does not exist!"; } +PrototypeChainCycle::PrototypeChainCycle() +{ + category().threadEvent("PrototypeChainCycle"_t); +} + const char *PrototypeChainCycle::what() const noexcept { return "There is a prototype chain cycle!"; } +AliasChainCycle::AliasChainCycle() +{ + category().threadEvent("AliasChainCycle"_t); +} + const char *AliasChainCycle::what() const noexcept { return "There is a prototype chain cycle!"; } +CannotParseQmlTypesFile::CannotParseQmlTypesFile() +{ + category().threadEvent("CannotParseQmlTypesFile"_t); +} + const char *CannotParseQmlTypesFile::what() const noexcept { return "Cannot parse qml types file!"; } +CannotParseQmlDocumentFile::CannotParseQmlDocumentFile() +{ + category().threadEvent("CannotParseQmlDocumentFile"_t); +} + const char *CannotParseQmlDocumentFile::what() const noexcept { return "Cannot parse qml types file!"; } +ProjectDataHasInvalidProjectSourceId::ProjectDataHasInvalidProjectSourceId() +{ + category().threadEvent("ProjectDataHasInvalidProjectSourceId"_t); +} + const char *ProjectDataHasInvalidProjectSourceId::what() const noexcept { return "The project source id is invalid!"; } +ProjectDataHasInvalidSourceId::ProjectDataHasInvalidSourceId() +{ + category().threadEvent("ProjectDataHasInvalidSourceId"_t); +} + const char *ProjectDataHasInvalidSourceId::what() const noexcept { return "The source id is invalid!"; } +ProjectDataHasInvalidModuleId::ProjectDataHasInvalidModuleId() +{ + category().threadEvent("ProjectDataHasInvalidModuleId"_t); +} + const char *ProjectDataHasInvalidModuleId::what() const noexcept { return "The module id is invalid!"; } +FileStatusHasInvalidSourceId::FileStatusHasInvalidSourceId() +{ + category().threadEvent("FileStatusHasInvalidSourceId"_t); +} + const char *FileStatusHasInvalidSourceId::what() const noexcept { return "The source id in file status is invalid!"; @@ -113,7 +209,14 @@ const char *ProjectStorageErrorWithMessage::what() const noexcept ExportedTypeCannotBeInserted::ExportedTypeCannotBeInserted(std::string_view errorMessage) : ProjectStorageErrorWithMessage{"ExportedTypeCannotBeInserted"sv, errorMessage} -{} +{ + category().threadEvent("ExportedTypeCannotBeInserted"_t, keyValue("error message", errorMessage)); +} + +TypeAnnotationHasInvalidSourceId::TypeAnnotationHasInvalidSourceId() +{ + category().threadEvent("TypeAnnotationHasInvalidSourceId"_t); +} const char *TypeAnnotationHasInvalidSourceId::what() const noexcept { diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h index e19421dbf45..d85f1f7f9ee 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h @@ -15,15 +15,19 @@ using namespace std::literals::string_view_literals; class QMLDESIGNERCORE_EXPORT ProjectStorageError : public std::exception { +protected: + ProjectStorageError() = default; + public: const char *what() const noexcept override; }; class ProjectStorageErrorWithMessage : public ProjectStorageError { -public: +protected: ProjectStorageErrorWithMessage(std::string_view error, std::string_view errorMessage); +public: const char *what() const noexcept override; public: @@ -33,42 +37,49 @@ public: class QMLDESIGNERCORE_EXPORT NoSourcePathForInvalidSourceId : public ProjectStorageError { public: + NoSourcePathForInvalidSourceId(); const char *what() const noexcept override; }; class QMLDESIGNERCORE_EXPORT NoSourceContextPathForInvalidSourceContextId : public ProjectStorageError { public: + NoSourceContextPathForInvalidSourceContextId(); const char *what() const noexcept override; }; class QMLDESIGNERCORE_EXPORT SourceContextIdDoesNotExists : public ProjectStorageError { public: + SourceContextIdDoesNotExists(); const char *what() const noexcept override; }; class QMLDESIGNERCORE_EXPORT SourceIdDoesNotExists : public ProjectStorageError { public: + SourceIdDoesNotExists(); const char *what() const noexcept override; }; class QMLDESIGNERCORE_EXPORT TypeHasInvalidSourceId : public ProjectStorageError { public: + TypeHasInvalidSourceId(); const char *what() const noexcept override; }; class QMLDESIGNERCORE_EXPORT ModuleDoesNotExists : public ProjectStorageError { public: + ModuleDoesNotExists(); const char *what() const noexcept override; }; class QMLDESIGNERCORE_EXPORT ModuleAlreadyExists : public ProjectStorageError { public: + ModuleAlreadyExists(); const char *what() const noexcept override; }; @@ -87,60 +98,70 @@ public: class QMLDESIGNERCORE_EXPORT PropertyNameDoesNotExists : public ProjectStorageError { public: + PropertyNameDoesNotExists(); const char *what() const noexcept override; }; class QMLDESIGNERCORE_EXPORT PrototypeChainCycle : public ProjectStorageError { public: + PrototypeChainCycle(); const char *what() const noexcept override; }; class QMLDESIGNERCORE_EXPORT AliasChainCycle : public ProjectStorageError { public: + AliasChainCycle(); const char *what() const noexcept override; }; class QMLDESIGNERCORE_EXPORT CannotParseQmlTypesFile : public ProjectStorageError { public: + CannotParseQmlTypesFile(); const char *what() const noexcept override; }; class QMLDESIGNERCORE_EXPORT CannotParseQmlDocumentFile : public ProjectStorageError { public: + CannotParseQmlDocumentFile(); const char *what() const noexcept override; }; class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidProjectSourceId : public ProjectStorageError { public: + ProjectDataHasInvalidProjectSourceId(); const char *what() const noexcept override; }; class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidSourceId : public ProjectStorageError { public: + ProjectDataHasInvalidSourceId(); const char *what() const noexcept override; }; class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidModuleId : public ProjectStorageError { public: + ProjectDataHasInvalidModuleId(); const char *what() const noexcept override; }; class QMLDESIGNERCORE_EXPORT FileStatusHasInvalidSourceId : public ProjectStorageError { public: + FileStatusHasInvalidSourceId(); const char *what() const noexcept override; }; class QMLDESIGNERCORE_EXPORT TypeAnnotationHasInvalidSourceId : public ProjectStorageError { public: + TypeAnnotationHasInvalidSourceId(); const char *what() const noexcept override; }; diff --git a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp index bb7cb809bd1..a22ef0ef746 100644 --- a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp +++ b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp @@ -6,6 +6,9 @@ #include namespace QmlDesigner { + +using namespace NanotraceHR::Literals; + namespace Tracing { namespace { @@ -51,7 +54,6 @@ StringEventQueue &stringEventQueue() namespace ModelTracing { namespace { -using namespace NanotraceHR::Literals; thread_local Category category_{"model"_t, Tracing::stringEventQueue(), category}; @@ -63,4 +65,19 @@ Category &category() } } // namespace ModelTracing + +namespace ProjectStorageTracing { + +NanotraceHR::StringViewWithStringArgumentsCategory &projectStorageCategory() +{ + thread_local NanotraceHR::StringViewWithStringArgumentsCategory + projectStorageCategory_{"project storage"_t, + Tracing::eventQueueWithStringArguments(), + projectStorageCategory}; + + return projectStorageCategory_; +} + +} // namespace ProjectStorageTracing + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h index 31058260d6e..3a592d8a0c3 100644 --- a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h +++ b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h @@ -44,4 +44,18 @@ using AsynchronousToken = Category::AsynchronousTokenType; [[gnu::pure]] QMLDESIGNERCORE_EXPORT Category &category(); } // namespace ModelTracing + +namespace ProjectStorageTracing { +constexpr NanotraceHR::Tracing projectStorageTracingStatus() +{ +#ifdef ENABLE_PROJECT_STORAGE_TRACING + return NanotraceHR::Tracing::IsEnabled; +#else + return NanotraceHR::Tracing::IsDisabled; +#endif +} + +[[gnu::pure]] NanotraceHR::StringViewWithStringArgumentsCategory & +projectStorageCategory(); +} // namespace ProjectStorageTracing } // namespace QmlDesigner diff --git a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp index d9578d2f1b6..a28c5e4935d 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp @@ -3210,7 +3210,8 @@ TEST_F(ProjectStorageUpdater, errors_for_watcher_updates_are_handled) SecondType 2.2 Second.qml)"}; setContent(u"/path/qmldir", qmldir); - ON_CALL(projectStorageMock, synchronize(_)).WillByDefault(Throw(QmlDesigner::ProjectStorageError{})); + ON_CALL(projectStorageMock, synchronize(_)) + .WillByDefault(Throw(QmlDesigner::NoSourcePathForInvalidSourceId{})); ASSERT_NO_THROW(updater.pathsWithIdsChanged({{directoryProjectChunkId, {directoryPathSourceId}}})); } @@ -3227,7 +3228,8 @@ TEST_F(ProjectStorageUpdater, input_is_reused_next_call_if_an_error_happens) {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}}); setFilesDontChanged({directoryPathSourceId, qmlDirPathSourceId}); - ON_CALL(projectStorageMock, synchronize(_)).WillByDefault(Throw(QmlDesigner::ProjectStorageError{})); + ON_CALL(projectStorageMock, synchronize(_)) + .WillByDefault(Throw(QmlDesigner::NoSourcePathForInvalidSourceId{})); updater.pathsWithIdsChanged( {{qmltypesProjectChunkId, {qmltypesPathSourceId, qmltypes2PathSourceId}}}); ON_CALL(projectStorageMock, synchronize(_)).WillByDefault(Return()); @@ -3286,7 +3288,8 @@ TEST_F(ProjectStorageUpdater, input_is_reused_next_call_if_an_error_happens_and_ {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}}); setFilesDontChanged({directoryPathSourceId, qmlDirPathSourceId}); - ON_CALL(projectStorageMock, synchronize(_)).WillByDefault(Throw(QmlDesigner::ProjectStorageError{})); + ON_CALL(projectStorageMock, synchronize(_)) + .WillByDefault(Throw(QmlDesigner::NoSourcePathForInvalidSourceId{})); updater.pathsWithIdsChanged( {{qmltypesProjectChunkId, {qmltypesPathSourceId, qmltypes2PathSourceId}}}); ON_CALL(projectStorageMock, synchronize(_)).WillByDefault(Return()); @@ -3348,7 +3351,8 @@ TEST_F(ProjectStorageUpdater, input_is_reused_next_call_if_an_error_happens_and_ {directoryPathSourceId, qmlDocumentSourceId1, QmlDesigner::ModuleId{}, FileType::QmlDocument}, {directoryPathSourceId, qmlDocumentSourceId1, QmlDesigner::ModuleId{}, FileType::QmlDocument}}); setFilesDontChanged({directoryPathSourceId, qmlDirPathSourceId}); - ON_CALL(projectStorageMock, synchronize(_)).WillByDefault(Throw(QmlDesigner::ProjectStorageError{})); + ON_CALL(projectStorageMock, synchronize(_)) + .WillByDefault(Throw(QmlDesigner::NoSourcePathForInvalidSourceId{})); updater.pathsWithIdsChanged( {{qmlDocumentProjectChunkId, {qmlDocumentSourceId1, qmlDocumentSourceId2}}}); ON_CALL(projectStorageMock, synchronize(_)).WillByDefault(Return()); From 7de2ae2ff4e7a532b2a559cbad9823f4ba41b878 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 18 Mar 2024 18:34:11 +0100 Subject: [PATCH 037/202] QmlDesigner: Add more tracing to project storage Change-Id: Iee113981ffa49b4f03b7641e7030a2f19f917fc6 Reviewed-by: Tim Jenssen Reviewed-by: Reviewed-by: Qt CI Patch Build Bot --- src/libs/nanotrace/nanotracehr.h | 81 +- src/libs/nanotrace/staticstring.h | 2 +- src/libs/sqlite/sqliteids.h | 15 +- src/libs/sqlite/sqlitevalue.h | 24 + src/libs/utils/smallstring.h | 10 +- .../designercore/projectstorage/filestatus.h | 12 + .../projectstorage/projectstorage.h | 1323 ++++++++++++++--- .../projectstorage/projectstorageinfotypes.h | 173 ++- .../projectstorage/projectstoragetypes.h | 437 +++++- 9 files changed, 1772 insertions(+), 305 deletions(-) diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index 8fd9d1669b0..5f2b031d55a 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -11,7 +11,9 @@ #include #include +#include #include +#include #include #include @@ -44,7 +46,7 @@ enum class Tracing { IsDisabled, IsEnabled }; # define NO_UNIQUE_ADDRESS #endif -using ArgumentsString = StaticString<1016>; +using ArgumentsString = StaticString<3700>; namespace Literals { struct TracerLiteral @@ -129,32 +131,14 @@ void convertToString(String &string, bool isTrue) string.append("false"); } -template>> +template, bool> = true> void convertToString(String &string, Callable &&callable) { convertToString(string, callable()); } -template -void convertToString(String &string, int number) -{ - string.append(number); -} - -template -void convertToString(String &string, long long number) -{ - string.append(number); -} - -template -void convertToString(String &string, std::size_t number) -{ - string.append(number); -} - -template -void convertToString(String &string, double number) +template, bool> = true> +void convertToString(String &string, Number number) { string.append(number); } @@ -166,7 +150,7 @@ constexpr std::underlying_type_t to_underlying(Enumeration enumerat return static_cast>(enumeration); } -template>> +template, bool> = true> void convertToString(String &string, Enumeration enumeration) { string.append(to_underlying(enumeration)); @@ -184,14 +168,14 @@ void convertToString(String &string, const QVariant &value) convertToString(string, value.toString()); } -template -void convertToString(String &string, const std::tuple &dictonary); - -template -void convertToString(String &string, const std::tuple &list); - -template typename Container, typename... Arguments> -void convertToString(String &string, const Container &container); +template +void convertToString(String &string, const std::optional &value) +{ + if (value) + convertToString(string, *value); + else + convertToString(string, "empty optional"); +} template void convertArrayEntryToString(String &string, const Value &value) @@ -211,7 +195,7 @@ void convertArrayToString(String &string, const IsArray &, Entries &...entries) } template -void convertToString(String &string, const std::tuple &list) +void convertToString(String &string, const std::tuple &list) { std::apply([&](auto &&...entries) { convertArrayToString(string, entries...); }, list); } @@ -242,16 +226,31 @@ void convertToString(String &string, const std::tuple std::apply([&](auto &&...entries) { convertDictonaryToString(string, entries...); }, dictonary); } -template typename Container, typename... Arguments> -void convertToString(String &string, const Container &container) +template +struct is_container : std::false_type +{}; + +template +struct is_container> : std::true_type +{}; + +template +struct is_container> : std::true_type +{}; + +template +struct is_container> : std::true_type +{}; + +template::value, bool> = true> +void convertToString(String &string, const Container &values) { string.append('['); - for (const auto &entry : container) { - convertToString(string, entry); - string.append(','); - } - if (container.size()) + for (const auto &value : values) + convertToString(string, value); + + if (values.size()) string.pop_back(); string.append(']'); @@ -337,7 +336,7 @@ inline bool operator&(IsFlow first, IsFlow second) } template -struct TraceEvent +struct alignas(4096) TraceEvent { using StringType = String; using ArgumentType = std::conditional_t, TracerLiteral, String>; @@ -352,13 +351,13 @@ struct TraceEvent String name; String category; - ArgumentsString arguments; TimePoint time; Duration duration; std::size_t id = 0; std::size_t bindId : 62; IsFlow flow : 2; char type = ' '; + ArgumentsString arguments; }; using StringViewTraceEvent = TraceEvent; diff --git a/src/libs/nanotrace/staticstring.h b/src/libs/nanotrace/staticstring.h index 2f845919bd1..0d5c8a14007 100644 --- a/src/libs/nanotrace/staticstring.h +++ b/src/libs/nanotrace/staticstring.h @@ -55,7 +55,7 @@ public: } } - template>> + template, bool> = true> void append(Type number) { #if !(defined(__cpp_lib_to_chars) && (__cpp_lib_to_chars >= 201611L)) diff --git a/src/libs/sqlite/sqliteids.h b/src/libs/sqlite/sqliteids.h index 75490ecccd9..1ffd546d9f2 100644 --- a/src/libs/sqlite/sqliteids.h +++ b/src/libs/sqlite/sqliteids.h @@ -65,6 +65,15 @@ public: [[noreturn, deprecated]] InternalIntegerType operator&() const { throw std::exception{}; } + template + friend void convertToString(String &string, BasicId id) + { + if (id.isValid()) + NanotraceHR::convertToString(string, id.internalId()); + else + NanotraceHR::convertToString(string, "invalid"); + } + private: InternalIntegerType id = 0; }; @@ -78,12 +87,6 @@ auto toIntegers(const Container &container) return Utils::span{data, container.size()}; } -template -void convertToString(String &string, BasicId id) -{ - NanotraceHR::convertToString(string, id.internalId()); -} - } // namespace Sqlite namespace std { diff --git a/src/libs/sqlite/sqlitevalue.h b/src/libs/sqlite/sqlitevalue.h index fe576f3fec9..ec4120139ab 100644 --- a/src/libs/sqlite/sqlitevalue.h +++ b/src/libs/sqlite/sqlitevalue.h @@ -6,6 +6,7 @@ #include "sqliteblob.h" #include "sqliteexception.h" +#include #include #include @@ -386,4 +387,27 @@ private: }; using Values = std::vector; + +template +void convertToString(String &string, const Value &value) +{ + switch (value.type()) { + case ValueType::Null: + convertToString(string, "null"); + break; + case ValueType::Integer: + convertToString(string, value.toInteger()); + break; + case ValueType::Float: + convertToString(string, value.toFloat()); + break; + case ValueType::String: + convertToString(string, value.toStringView()); + break; + case ValueType::Blob: + convertToString(string, "blob"); + break; + } +} + } // namespace Sqlite diff --git a/src/libs/utils/smallstring.h b/src/libs/utils/smallstring.h index 530022f475c..a8869b4ccf8 100644 --- a/src/libs/utils/smallstring.h +++ b/src/libs/utils/smallstring.h @@ -93,7 +93,7 @@ public: static_cast(std::distance(begin, end))} {} - template::value>> + template::value, bool> = true> BasicSmallString(Type characterPointer) noexcept : BasicSmallString(characterPointer, std::char_traits::length(characterPointer)) { @@ -118,7 +118,7 @@ public: template::value>> + typename std::enable_if_t::value, bool> = true> BasicSmallString(BeginIterator begin, EndIterator end) noexcept : BasicSmallString(&(*begin), size_type(end - begin)) {} @@ -447,7 +447,7 @@ public: setSize(newSize); } - template>> + template, bool> = true> void append(Type number) { #if defined(__cpp_lib_to_chars) && (__cpp_lib_to_chars >= 201611L) @@ -527,7 +527,7 @@ public: return *this; } - template>> + template, bool> = true> BasicSmallString &operator+=(Type number) noexcept { append(number); @@ -639,7 +639,7 @@ public: return joinedString; } - template>> + template, bool> = true> static BasicSmallString number(Type number) { BasicSmallString string; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/filestatus.h b/src/plugins/qmldesigner/designercore/projectstorage/filestatus.h index f3e275b8f3c..48b3ba2700c 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/filestatus.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/filestatus.h @@ -50,6 +50,18 @@ public: explicit operator bool() const { return isValid(); } + template + friend void convertToString(String &string, const FileStatus &fileStatus) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("source id", fileStatus.sourceId), + keyValue("size", fileStatus.size), + keyValue("last modified", fileStatus.lastModified)); + + convertToString(string, dict); + } + public: SourceId sourceId; long long size = -1; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index e1a10b87374..aac67a3007d 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -6,6 +6,7 @@ #include "commontypecache.h" #include "projectstorageexceptions.h" #include "projectstorageinterface.h" +#include "projectstoragetypes.h" #include "sourcepathcachetypes.h" #include "storagecache.h" @@ -128,7 +129,11 @@ public: void synchronizeDocumentImports(Storage::Imports imports, SourceId sourceId) override { - NanotraceHR::Tracer tracer{"synchronize document imports"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"synchronize document imports"_t, + projectStorageCategory(), + keyValue("imports", imports), + keyValue("source id", sourceId)}; Sqlite::withImmediateTransaction(database, [&] { synchronizeDocumentImports(imports, @@ -137,188 +142,336 @@ public: }); } - void addObserver(ProjectStorageObserver *observer) override { observers.push_back(observer); } + void addObserver(ProjectStorageObserver *observer) override + { + NanotraceHR::Tracer tracer{"add observer"_t, projectStorageCategory()}; + observers.push_back(observer); + } void removeObserver(ProjectStorageObserver *observer) override { + NanotraceHR::Tracer tracer{"remove observer"_t, projectStorageCategory()}; observers.removeOne(observer); } ModuleId moduleId(Utils::SmallStringView moduleName) const override { - NanotraceHR::Tracer tracer{"get module id"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get module id"_t, + projectStorageCategory(), + keyValue("module name", moduleName)}; - return moduleCache.id(moduleName); + auto moduleId = moduleCache.id(moduleName); + + tracer.end(keyValue("module id", moduleId)); + + return moduleId; } Utils::SmallString moduleName(ModuleId moduleId) const { - NanotraceHR::Tracer tracer{"get module name"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get module name"_t, + projectStorageCategory(), + keyValue("module id", moduleId)}; if (!moduleId) throw ModuleDoesNotExists{}; - return moduleCache.value(moduleId); + auto moduleName = moduleCache.value(moduleId); + + tracer.end(keyValue("module name", moduleName)); + + return moduleName; } TypeId typeId(ModuleId moduleId, Utils::SmallStringView exportedTypeName, Storage::Version version) const override { - NanotraceHR::Tracer tracer{"get type id by exported name"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get type id by exported name"_t, + projectStorageCategory(), + keyValue("module id", moduleId), + keyValue("exported type name", exportedTypeName), + keyValue("version", version)}; - if (version.minor) - return selectTypeIdByModuleIdAndExportedNameAndVersionStatement - .template valueWithTransaction(moduleId, - exportedTypeName, - version.major.value, - version.minor.value); + TypeId typeId; - if (version.major) - return selectTypeIdByModuleIdAndExportedNameAndMajorVersionStatement - .template valueWithTransaction(moduleId, exportedTypeName, version.major.value); + if (version.minor) { + typeId = selectTypeIdByModuleIdAndExportedNameAndVersionStatement + .template valueWithTransaction(moduleId, + exportedTypeName, + version.major.value, + version.minor.value); - return selectTypeIdByModuleIdAndExportedNameStatement - .template valueWithTransaction(moduleId, exportedTypeName); + } else if (version.major) { + typeId = selectTypeIdByModuleIdAndExportedNameAndMajorVersionStatement + .template valueWithTransaction(moduleId, + exportedTypeName, + version.major.value); + + } else { + typeId = selectTypeIdByModuleIdAndExportedNameStatement + .template valueWithTransaction(moduleId, exportedTypeName); + } + + tracer.end(keyValue("type id", typeId)); + + return typeId; } TypeId typeId(ImportedTypeNameId typeNameId) const override { - NanotraceHR::Tracer tracer{"get type id by imported type name"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get type id by imported type name"_t, + projectStorageCategory(), + keyValue("imported type name id", typeNameId)}; - return Sqlite::withDeferredTransaction(database, [&] { return fetchTypeId(typeNameId); }); + auto typeId = Sqlite::withDeferredTransaction(database, + [&] { return fetchTypeId(typeNameId); }); + + tracer.end(keyValue("type id", typeId)); + + return typeId; } QVarLengthArray typeIds(ModuleId moduleId) const override { - NanotraceHR::Tracer tracer{"get type ids by module id"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get type ids by module id"_t, + projectStorageCategory(), + keyValue("module id", moduleId)}; - return selectTypeIdsByModuleIdStatement - .template valuesWithTransaction>(moduleId); + auto typeIds = selectTypeIdsByModuleIdStatement + .template valuesWithTransaction>(moduleId); + + tracer.end(keyValue("type ids", typeIds)); + + return typeIds; } Storage::Info::ExportedTypeNames exportedTypeNames(TypeId typeId) const override { - NanotraceHR::Tracer tracer{"get exported type names by type id"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get exported type names by type id"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; - return selectExportedTypesByTypeIdStatement - .template valuesWithTransaction(typeId); + auto exportedTypenames = selectExportedTypesByTypeIdStatement + .template valuesWithTransaction( + typeId); + + tracer.end(keyValue("exported type names", exportedTypenames)); + + return exportedTypenames; } - Storage::Info::ExportedTypeNames exportedTypeNames(TypeId typeId, - SourceId sourceId) const override + Storage::Info::ExportedTypeNames exportedTypeNames(TypeId typeId, SourceId sourceId) const override { - NanotraceHR::Tracer tracer{"get exported type names by source id"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get exported type names by source id"_t, + projectStorageCategory(), + keyValue("type id", typeId), + keyValue("source id", sourceId)}; - return selectExportedTypesByTypeIdAndSourceIdStatement - .template valuesWithTransaction(typeId, sourceId); + auto exportedTypenames = selectExportedTypesByTypeIdAndSourceIdStatement + .template valuesWithTransaction(typeId, sourceId); + + tracer.end(keyValue("exported type names", exportedTypenames)); + + return exportedTypenames; } ImportId importId(const Storage::Import &import) const override { - NanotraceHR::Tracer tracer{"get import id by import"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get import id by import"_t, + projectStorageCategory(), + keyValue("import", import)}; - return Sqlite::withDeferredTransaction(database, [&] { + auto importId = Sqlite::withDeferredTransaction(database, [&] { return fetchImportId(import.sourceId, import); }); + + tracer.end(keyValue("import id", importId)); + + return importId; } - ImportedTypeNameId importedTypeNameId(ImportId importId, - Utils::SmallStringView typeName) override + ImportedTypeNameId importedTypeNameId(ImportId importId, Utils::SmallStringView typeName) override { + using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"get imported type name id by import id"_t, - projectStorageCategory()}; + projectStorageCategory(), + keyValue("import id", importId), + keyValue("imported type name", typeName)}; - return Sqlite::withDeferredTransaction(database, [&] { + auto importedTypeNameId = Sqlite::withDeferredTransaction(database, [&] { return fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::QualifiedExported, importId, typeName); }); + + tracer.end(keyValue("imported type name id", importedTypeNameId)); + + return importedTypeNameId; } - ImportedTypeNameId importedTypeNameId(SourceId sourceId, - Utils::SmallStringView typeName) override + ImportedTypeNameId importedTypeNameId(SourceId sourceId, Utils::SmallStringView typeName) override { + using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"get imported type name id by source id"_t, - projectStorageCategory()}; + projectStorageCategory(), + keyValue("source id", sourceId), + keyValue("imported type name", typeName)}; - return Sqlite::withDeferredTransaction(database, [&] { + auto importedTypeNameId = Sqlite::withDeferredTransaction(database, [&] { return fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::Exported, sourceId, typeName); }); + + tracer.end(keyValue("imported type name id", importedTypeNameId)); + + return importedTypeNameId; } QVarLengthArray propertyDeclarationIds(TypeId typeId) const override { - NanotraceHR::Tracer tracer{"get property declaration ids"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get property declaration ids"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; - return selectPropertyDeclarationIdsForTypeStatement - .template valuesWithTransaction>(typeId); + auto propertyDeclarationIds = selectPropertyDeclarationIdsForTypeStatement + .template valuesWithTransaction< + QVarLengthArray>(typeId); + + tracer.end(keyValue("property declaration ids", propertyDeclarationIds)); + + return propertyDeclarationIds; } QVarLengthArray localPropertyDeclarationIds(TypeId typeId) const override { - NanotraceHR::Tracer tracer{"get local property declaration ids"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get local property declaration ids"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; - return selectLocalPropertyDeclarationIdsForTypeStatement - .template valuesWithTransaction>(typeId); + auto propertyDeclarationIds = selectLocalPropertyDeclarationIdsForTypeStatement + .template valuesWithTransaction< + QVarLengthArray>(typeId); + + tracer.end(keyValue("property declaration ids", propertyDeclarationIds)); + + return propertyDeclarationIds; } PropertyDeclarationId propertyDeclarationId(TypeId typeId, Utils::SmallStringView propertyName) const override { - NanotraceHR::Tracer tracer{"get property declaration id"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get property declaration id"_t, + projectStorageCategory(), + keyValue("type id", typeId), + keyValue("property name", propertyName)}; - return selectPropertyDeclarationIdForTypeAndPropertyNameStatement - .template valueWithTransaction(typeId, propertyName); + auto propertyDeclarationId = selectPropertyDeclarationIdForTypeAndPropertyNameStatement + .template valueWithTransaction( + typeId, propertyName); + + tracer.end(keyValue("property declaration id", propertyDeclarationId)); + + return propertyDeclarationId; } PropertyDeclarationId localPropertyDeclarationId(TypeId typeId, Utils::SmallStringView propertyName) const { - NanotraceHR::Tracer tracer{"get local property declaration id"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get local property declaration id"_t, + projectStorageCategory(), + keyValue("type id", typeId), + keyValue("property name", propertyName)}; - return selectLocalPropertyDeclarationIdForTypeAndPropertyNameStatement - .template valueWithTransaction(typeId, propertyName); + auto propertyDeclarationId = selectLocalPropertyDeclarationIdForTypeAndPropertyNameStatement + .template valueWithTransaction( + typeId, propertyName); + + tracer.end(keyValue("property declaration id", propertyDeclarationId)); + + return propertyDeclarationId; } std::optional propertyDeclaration( PropertyDeclarationId propertyDeclarationId) const override { - NanotraceHR::Tracer tracer{"get property declaration"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get property declaration"_t, + projectStorageCategory(), + keyValue("property declaration id", propertyDeclarationId)}; - return selectPropertyDeclarationForPropertyDeclarationIdStatement - .template optionalValueWithTransaction( - propertyDeclarationId); + auto propertyDeclaration = selectPropertyDeclarationForPropertyDeclarationIdStatement + .template optionalValueWithTransaction( + propertyDeclarationId); + + tracer.end(keyValue("property declaration", propertyDeclaration)); + + return propertyDeclaration; } std::optional type(TypeId typeId) const override { - NanotraceHR::Tracer tracer{"get type"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get type"_t, projectStorageCategory(), keyValue("type id", typeId)}; - return selectInfoTypeByTypeIdStatement.template optionalValueWithTransaction( - typeId); + auto type = selectInfoTypeByTypeIdStatement + .template optionalValueWithTransaction(typeId); + + tracer.end(keyValue("type", type)); + + return type; } Utils::PathString typeIconPath(TypeId typeId) const override { - NanotraceHR::Tracer tracer{"get type icon path"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get type icon path"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; - return selectTypeIconPathStatement.template valueWithTransaction(typeId); + auto typeIconPath = selectTypeIconPathStatement.template valueWithTransaction( + typeId); + + tracer.end(keyValue("type icon path", typeIconPath)); + + return typeIconPath; } Storage::Info::TypeHints typeHints(TypeId typeId) const override { - NanotraceHR::Tracer tracer{"get type hints"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get type hints"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; - return selectTypeHintsStatement.template valuesWithTransaction( - typeId); + auto typeHints = selectTypeHintsStatement + .template valuesWithTransaction(typeId); + + tracer.end(keyValue("type hints", typeHints)); + + return typeHints; } Storage::Info::ItemLibraryEntries itemLibraryEntries(TypeId typeId) const override { - NanotraceHR::Tracer tracer{"get item library entries by type id"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get item library entries by type id"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; using Storage::Info::ItemLibraryProperties; Storage::Info::ItemLibraryEntries entries; @@ -342,13 +495,17 @@ public: selectItemLibraryEntriesByTypeIdStatement.readCallbackWithTransaction(callback, typeId); + tracer.end(keyValue("item library entries", entries)); + return entries; } Storage::Info::ItemLibraryEntries itemLibraryEntries(SourceId sourceId) const override { + using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"get item library entries by source id"_t, - projectStorageCategory()}; + projectStorageCategory(), + keyValue("source id", sourceId)}; using Storage::Info::ItemLibraryProperties; Storage::Info::ItemLibraryEntries entries; @@ -372,11 +529,14 @@ public: selectItemLibraryEntriesBySourceIdStatement.readCallbackWithTransaction(callback, sourceId); + tracer.end(keyValue("item library entries", entries)); + return entries; } Storage::Info::ItemLibraryEntries allItemLibraryEntries() const override { + using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"get all item library entries"_t, projectStorageCategory()}; using Storage::Info::ItemLibraryProperties; @@ -401,31 +561,57 @@ public: selectItemLibraryEntriesStatement.readCallbackWithTransaction(callback); + tracer.end(keyValue("item library entries", entries)); + return entries; } std::vector signalDeclarationNames(TypeId typeId) const override { - NanotraceHR::Tracer tracer{"get signal names"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get signal names"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; - return selectSignalDeclarationNamesForTypeStatement - .template valuesWithTransaction(typeId); + auto signalDeclarationNames = selectSignalDeclarationNamesForTypeStatement + .template valuesWithTransaction( + typeId); + + tracer.end(keyValue("signal names", signalDeclarationNames)); + + return signalDeclarationNames; } std::vector functionDeclarationNames(TypeId typeId) const override { - NanotraceHR::Tracer tracer{"get function names"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get function names"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; - return selectFuncionDeclarationNamesForTypeStatement - .template valuesWithTransaction(typeId); + auto functionDeclarationNames = selectFuncionDeclarationNamesForTypeStatement + .template valuesWithTransaction( + typeId); + + tracer.end(keyValue("function names", functionDeclarationNames)); + + return functionDeclarationNames; } std::optional propertyName(PropertyDeclarationId propertyDeclarationId) const override { - NanotraceHR::Tracer tracer{"get property name"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get property name"_t, + projectStorageCategory(), + keyValue("property declaration id", propertyDeclarationId)}; - return selectPropertyNameStatement.template optionalValueWithTransaction( - propertyDeclarationId); + auto propertyName = selectPropertyNameStatement + .template optionalValueWithTransaction( + propertyDeclarationId); + + tracer.end(keyValue("property name", propertyName)); + + return propertyName; } const Storage::Info::CommonTypeCache &commonTypeCache() const override @@ -436,73 +622,115 @@ public: template TypeId commonTypeId() const { - NanotraceHR::Tracer tracer{"get type id from common type cache"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get type id from common type cache"_t, + projectStorageCategory(), + keyValue("module name", std::string_view{moduleName}), + keyValue("type name", std::string_view{typeName})}; - return commonTypeCache_.template typeId(); + auto typeId = commonTypeCache_.template typeId(); + + tracer.end(keyValue("type id", typeId)); + + return typeId; } template TypeId builtinTypeId() const { + using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"get builtin type id from common type cache"_t, projectStorageCategory()}; - return commonTypeCache_.template builtinTypeId(); + auto typeId = commonTypeCache_.template builtinTypeId(); + + tracer.end(keyValue("type id", typeId)); + + return typeId; } template TypeId builtinTypeId() const { + using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"get builtin type id from common type cache"_t, projectStorageCategory()}; - return commonTypeCache_.template builtinTypeId(); + auto typeId = commonTypeCache_.template builtinTypeId(); + + tracer.end(keyValue("type id", typeId)); + + return typeId; } TypeIds prototypeIds(TypeId type) const override { - NanotraceHR::Tracer tracer{"get prototypes"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get prototypes"_t, + projectStorageCategory(), + keyValue("type id", type)}; - return selectPrototypeIdsForTypeIdInOrderStatement.template valuesWithTransaction( - type); + auto prototypeIds = selectPrototypeIdsForTypeIdInOrderStatement + .template valuesWithTransaction(type); + + tracer.end(keyValue("type ids", prototypeIds)); + + return prototypeIds; } TypeIds prototypeAndSelfIds(TypeId type) const override { + using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"get prototypes and self"_t, projectStorageCategory()}; - return selectPrototypeAndSelfIdsForTypeIdInOrderStatement - .template valuesWithTransaction(type); + TypeIds prototypeAndSelfIds = selectPrototypeAndSelfIdsForTypeIdInOrderStatement + .template valuesWithTransaction(type); + + tracer.end(keyValue("type ids", prototypeAndSelfIds)); + + return prototypeAndSelfIds; } TypeIds heirIds(TypeId typeId) const override { + using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"get heirs"_t, projectStorageCategory()}; - return selectHeirTypeIdsStatement.template valuesWithTransaction(typeId); + auto heirIds = selectHeirTypeIdsStatement.template valuesWithTransaction(typeId); + + tracer.end(keyValue("type ids", heirIds)); + + return heirIds; } template bool isBasedOn_(TypeId typeId, TypeIds... baseTypeIds) const { - NanotraceHR::Tracer tracer{"is based on"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is based on"_t, + projectStorageCategory(), + keyValue("type id", typeId), + keyValue("base type ids", NanotraceHR::array(baseTypeIds...))}; static_assert(((std::is_same_v) &&...), "Parameter must be a TypeId!"); - if (((typeId == baseTypeIds) || ...)) + if (((typeId == baseTypeIds) || ...)) { + tracer.end(keyValue("is based on", true)); return true; + } auto range = selectPrototypeIdsStatement.template rangeWithTransaction(typeId); - for ([[maybe_unused]] TypeId currentTypeId : range) { - if (((currentTypeId == baseTypeIds) || ...)) - return true; - } + auto isBasedOn = std::any_of(range.begin(), range.end(), [&](TypeId currentTypeId) { + return ((currentTypeId == baseTypeIds) || ...); + }); - return false; + tracer.end(keyValue("is based on", isBasedOn)); + + return isBasedOn; } - bool isBasedOn(TypeId typeId) const { return isBasedOn_(typeId); } + bool isBasedOn(TypeId) const { return false; } bool isBasedOn(TypeId typeId, TypeId id1) const override { return isBasedOn_(typeId, id1); } @@ -546,26 +774,57 @@ public: TypeId fetchTypeIdByExportedName(Utils::SmallStringView name) const { - NanotraceHR::Tracer tracer{"is based on"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is based on"_t, + projectStorageCategory(), + keyValue("exported type name", name)}; - return selectTypeIdByExportedNameStatement.template valueWithTransaction(name); + auto typeId = selectTypeIdByExportedNameStatement.template valueWithTransaction(name); + + tracer.end(keyValue("type id", typeId)); + + return typeId; } TypeId fetchTypeIdByModuleIdsAndExportedName(ModuleIds moduleIds, Utils::SmallStringView name) const { - return selectTypeIdByModuleIdsAndExportedNameStatement.template valueWithTransaction( + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch type id by module ids and exported name"_t, + projectStorageCategory(), + keyValue("module ids", NanotraceHR::array(moduleIds)), + keyValue("exported type name", name)}; + auto typeId = selectTypeIdByModuleIdsAndExportedNameStatement.template valueWithTransaction( static_cast(moduleIds.data()), static_cast(moduleIds.size()), name); + + tracer.end(keyValue("type id", typeId)); + + return typeId; } TypeId fetchTypeIdByName(SourceId sourceId, Utils::SmallStringView name) { - return selectTypeIdBySourceIdAndNameStatement.template valueWithTransaction(sourceId, - name); + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch type id by name"_t, + projectStorageCategory(), + keyValue("source id", sourceId), + keyValue("internal type name", name)}; + + auto typeId = selectTypeIdBySourceIdAndNameStatement + .template valueWithTransaction(sourceId, name); + + tracer.end(keyValue("type id", typeId)); + + return typeId; } Storage::Synchronization::Type fetchTypeByTypeId(TypeId typeId) { - return Sqlite::withDeferredTransaction(database, [&] { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch type by type id"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + auto type = Sqlite::withDeferredTransaction(database, [&] { auto type = selectTypeByTypeIdStatement.template value( typeId); @@ -577,11 +836,18 @@ public: return type; }); + + tracer.end(keyValue("type", type)); + + return type; } Storage::Synchronization::Types fetchTypes() { - return Sqlite::withDeferredTransaction(database, [&] { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch types"_t, projectStorageCategory()}; + + auto types = Sqlite::withDeferredTransaction(database, [&] { auto types = selectTypesStatement.template values(); for (Storage::Synchronization::Type &type : types) { @@ -594,20 +860,15 @@ public: return types; }); - } - bool fetchIsProtype(TypeId type, TypeId prototype) - { - return bool(selectPrototypeIdStatement.template valueWithTransaction(type, prototype)); - } + tracer.end(keyValue("type", types)); - auto fetchPrototypes(TypeId type) - { - return selectPrototypeIdsInOrderStatement.template rangeWithTransaction(type); + return types; } SourceContextId fetchSourceContextIdUnguarded(Utils::SmallStringView sourceContextPath) { + using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"fetch source context id unguarded"_t, projectStorageCategory()}; auto sourceContextId = readSourceContextId(sourceContextPath); @@ -617,22 +878,33 @@ public: SourceContextId fetchSourceContextId(Utils::SmallStringView sourceContextPath) { - NanotraceHR::Tracer tracer{"fetch source context id"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch source context id"_t, + projectStorageCategory(), + keyValue("source context path", sourceContextPath)}; + SourceContextId sourceContextId; try { - return Sqlite::withDeferredTransaction(database, [&] { + sourceContextId = Sqlite::withDeferredTransaction(database, [&] { return fetchSourceContextIdUnguarded(sourceContextPath); }); } catch (const Sqlite::ConstraintPreventsModification &) { - return fetchSourceContextId(sourceContextPath); + sourceContextId = fetchSourceContextId(sourceContextPath); } + + tracer.end(keyValue("source context id", sourceContextId)); + + return sourceContextId; } Utils::PathString fetchSourceContextPath(SourceContextId sourceContextId) const { - NanotraceHR::Tracer tracer{"fetch source context path"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch source context path"_t, + projectStorageCategory(), + keyValue("source context id", sourceContextId)}; - return Sqlite::withDeferredTransaction(database, [&] { + auto path = Sqlite::withDeferredTransaction(database, [&] { auto optionalSourceContextPath = selectSourceContextPathFromSourceContextsBySourceContextIdStatement .template optionalValue( sourceContextId); @@ -642,6 +914,10 @@ public: return std::move(*optionalSourceContextPath); }); + + tracer.end(keyValue("source context path", path)); + + return path; } auto fetchAllSourceContexts() const @@ -654,17 +930,27 @@ public: SourceId fetchSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName) { - NanotraceHR::Tracer tracer{"fetch source id"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch source id"_t, + projectStorageCategory(), + keyValue("source context id", sourceContextId), + keyValue("source name", sourceName)}; - return Sqlite::withDeferredTransaction(database, [&] { + auto sourceId = Sqlite::withDeferredTransaction(database, [&] { return fetchSourceIdUnguarded(sourceContextId, sourceName); }); + + tracer.end(keyValue("source id", sourceId)); + + return sourceId; } auto fetchSourceNameAndSourceContextId(SourceId sourceId) const { + using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"fetch source name and source context id"_t, - projectStorageCategory()}; + projectStorageCategory(), + keyValue("source id", sourceId)}; auto value = selectSourceNameAndSourceContextIdFromSourcesBySourceIdStatement .template valueWithTransaction(sourceId); @@ -672,6 +958,9 @@ public: if (!value.sourceContextId) throw SourceIdDoesNotExists(); + tracer.end(keyValue("source name", value.sourceName), + keyValue("source context id", value.sourceContextId)); + return value; } @@ -685,7 +974,10 @@ public: SourceContextId fetchSourceContextId(SourceId sourceId) const { - NanotraceHR::Tracer tracer{"fetch source context id"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch source context id"_t, + projectStorageCategory(), + keyValue("source id", sourceId)}; auto sourceContextId = selectSourceContextIdFromSourcesBySourceIdStatement .template valueWithTransaction(sourceId); @@ -693,6 +985,8 @@ public: if (!sourceContextId) throw SourceIdDoesNotExists(); + tracer.end(keyValue("source context id", sourceContextId)); + return sourceContextId; } @@ -705,14 +999,20 @@ public: SourceId fetchSourceIdUnguarded(SourceContextId sourceContextId, Utils::SmallStringView sourceName) { - NanotraceHR::Tracer tracer{"fetch source id unguarded"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch source id unguarded"_t, + projectStorageCategory(), + keyValue("source context id", sourceContextId), + keyValue("source name", sourceName)}; auto sourceId = readSourceId(sourceContextId, sourceName); - if (sourceId) - return sourceId; + if (!sourceId) + sourceId = writeSourceId(sourceContextId, sourceName); - return writeSourceId(sourceContextId, sourceName); + tracer.end(keyValue("source id", sourceId)); + + return sourceId; } auto fetchAllFileStatuses() const @@ -724,36 +1024,64 @@ public: FileStatus fetchFileStatus(SourceId sourceId) const override { - NanotraceHR::Tracer tracer{"fetch file status"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch file status"_t, + projectStorageCategory(), + keyValue("source id", sourceId)}; - return selectFileStatusesForSourceIdStatement.template valueWithTransaction( - sourceId); + auto fileStatus = selectFileStatusesForSourceIdStatement + .template valueWithTransaction(sourceId); + + tracer.end(keyValue("file status", fileStatus)); + + return fileStatus; } std::optional fetchProjectData(SourceId sourceId) const override { - NanotraceHR::Tracer tracer{"fetch project data"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch project data"_t, + projectStorageCategory(), + keyValue("source id", sourceId)}; - return selectProjectDataForSourceIdStatement - .template optionalValueWithTransaction(sourceId); + auto projectData = selectProjectDataForSourceIdStatement.template optionalValueWithTransaction< + Storage::Synchronization::ProjectData>(sourceId); + + tracer.end(keyValue("project data", projectData)); + + return projectData; } Storage::Synchronization::ProjectDatas fetchProjectDatas(SourceId projectSourceId) const override { - NanotraceHR::Tracer tracer{"fetch project datas by source id"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch project datas by source id"_t, + projectStorageCategory(), + keyValue("source id", projectSourceId)}; - return selectProjectDatasForSourceIdStatement - .template valuesWithTransaction( - projectSourceId); + auto projectDatas = selectProjectDatasForSourceIdStatement + .template valuesWithTransaction( + projectSourceId); + + tracer.end(keyValue("project datas", projectDatas)); + + return projectDatas; } Storage::Synchronization::ProjectDatas fetchProjectDatas(const SourceIds &projectSourceIds) const { - NanotraceHR::Tracer tracer{"fetch project datas by source ids"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch project datas by source ids"_t, + projectStorageCategory(), + keyValue("source ids", projectSourceIds)}; - return selectProjectDatasForSourceIdsStatement - .template valuesWithTransaction( - toIntegers(projectSourceIds)); + auto projectDatas = selectProjectDatasForSourceIdsStatement + .template valuesWithTransaction( + toIntegers(projectSourceIds)); + + tracer.end(keyValue("project datas", projectDatas)); + + return projectDatas; } void setPropertyEditorPathId(TypeId typeId, SourceId pathId) @@ -767,7 +1095,17 @@ public: SourceId propertyEditorPathId(TypeId typeId) const override { - return selectPropertyEditorPathIdStatement.template valueWithTransaction(typeId); + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"property editor path id"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + auto sourceId = selectPropertyEditorPathIdStatement.template valueWithTransaction( + typeId); + + tracer.end(keyValue("source id", sourceId)); + + return sourceId; } Storage::Imports fetchDocumentImports() const @@ -828,22 +1166,50 @@ private: ModuleId fetchModuleId(Utils::SmallStringView moduleName) { - return Sqlite::withDeferredTransaction(database, - [&] { return fetchModuleIdUnguarded(moduleName); }); + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch module id"_t, + projectStorageCategory(), + keyValue("module name", moduleName)}; + + auto moduleId = Sqlite::withDeferredTransaction(database, [&] { + return fetchModuleIdUnguarded(moduleName); + }); + + tracer.end(keyValue("module id", moduleId)); + + return moduleId; } auto fetchModuleName(ModuleId id) { - return Sqlite::withDeferredTransaction(database, [&] { return fetchModuleNameUnguarded(id); }); + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch module name"_t, + projectStorageCategory(), + keyValue("module id", id)}; + + auto moduleName = Sqlite::withDeferredTransaction(database, [&] { + return fetchModuleNameUnguarded(id); + }); + + tracer.end(keyValue("module name", moduleName)); + + return moduleName; } auto fetchAllModules() const { + NanotraceHR::Tracer tracer{"fetch all modules"_t, projectStorageCategory()}; + return selectAllModulesStatement.template valuesWithTransaction(); } void callRefreshMetaInfoCallback(const TypeIds &deletedTypeIds) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"call refresh meta info callback"_t, + projectStorageCategory(), + keyValue("type ids", deletedTypeIds)}; + if (deletedTypeIds.size()) { for (ProjectStorageObserver *observer : observers) observer->removedTypeIds(deletedTypeIds); @@ -875,6 +1241,25 @@ private: < std::tie(second.typeId, second.propertyDeclarationId); } + template + friend void convertToString(String &string, + const AliasPropertyDeclaration &aliasPropertyDeclaration) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary( + keyValue("type id", aliasPropertyDeclaration.typeId), + keyValue("property declaration id", aliasPropertyDeclaration.propertyDeclarationId), + keyValue("alias imported type name id", + aliasPropertyDeclaration.aliasImportedTypeNameId), + keyValue("alias property name", aliasPropertyDeclaration.aliasPropertyName), + keyValue("alias property name tail", aliasPropertyDeclaration.aliasPropertyNameTail), + keyValue("alias property declaration id", + aliasPropertyDeclaration.aliasPropertyDeclarationId)); + + convertToString(string, dict); + } + public: TypeId typeId; PropertyDeclarationId propertyDeclarationId; @@ -903,6 +1288,20 @@ private: < std::tie(second.typeId, second.propertyDeclarationId); } + template + friend void convertToString(String &string, const PropertyDeclaration &propertyDeclaration) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("type id", propertyDeclaration.typeId), + keyValue("property declaration id", + propertyDeclaration.propertyDeclarationId), + keyValue("imported type name id", + propertyDeclaration.importedTypeNameId)); + + convertToString(string, dict); + } + public: TypeId typeId; PropertyDeclarationId propertyDeclarationId; @@ -924,6 +1323,17 @@ private: return first.typeId < second.typeId; } + template + friend void convertToString(String &string, const Prototype &prototype) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("type id", prototype.typeId), + keyValue("prototype name id", prototype.prototypeNameId)); + + convertToString(string, dict); + } + public: TypeId typeId; ImportedTypeNameId prototypeNameId; @@ -980,6 +1390,11 @@ private: TypeIds fetchTypeIds(const SourceIds &sourceIds) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch type ids"_t, + projectStorageCategory(), + keyValue("source ids", sourceIds)}; + return selectTypeIdsForSourceIdsStatement.template values(toIntegers(sourceIds)); } @@ -992,6 +1407,12 @@ private: void synchronizeTypeTraits(TypeId typeId, Storage::TypeTraits traits) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"synchronize type traits"_t, + projectStorageCategory(), + keyValue("type id", typeId), + keyValue("type traits", traits)}; + updateTypeAnnotationTraitStatement.write(typeId, traits.annotation); } @@ -1008,6 +1429,19 @@ private: , hintsJson{hintsJson} {} + template + friend void convertToString(String &string, const TypeAnnotationView &typeAnnotationView) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("type id", typeAnnotationView.typeId), + keyValue("icon path", typeAnnotationView.iconPath), + keyValue("item library json", typeAnnotationView.itemLibraryJson), + keyValue("hints json", typeAnnotationView.hintsJson)); + + convertToString(string, dict); + } + public: TypeId typeId; Utils::SmallStringView iconPath; @@ -1017,6 +1451,8 @@ private: void updateTypeIdInTypeAnnotations(Storage::Synchronization::TypeAnnotations &typeAnnotations) { + NanotraceHR::Tracer tracer{"update type id in type annotations"_t, projectStorageCategory()}; + for (auto &annotation : typeAnnotations) { annotation.typeId = fetchTypeIdByModuleIdAndExportedName(annotation.moduleId, annotation.typeName); @@ -1047,6 +1483,11 @@ private: synchronizeTypeTraits(annotation.typeId, annotation.traits); + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert type annotations"_t, + projectStorageCategory(), + keyValue("type annotation", annotation)}; + insertTypeAnnotationStatement.write(annotation.typeId, annotation.sourceId, annotation.iconPath, @@ -1061,6 +1502,13 @@ private: if (annotationFromDatabase.iconPath != annotation.iconPath || annotationFromDatabase.itemLibraryJson != annotation.itemLibraryJson || annotationFromDatabase.hintsJson != annotation.hintsJson) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"update type annotations"_t, + projectStorageCategory(), + keyValue("type annotation from database", + annotationFromDatabase), + keyValue("type annotation", annotation)}; + updateTypeAnnotationStatement.write(annotation.typeId, annotation.iconPath, annotation.itemLibraryJson, @@ -1074,6 +1522,11 @@ private: auto remove = [&](const TypeAnnotationView &annotationFromDatabase) { synchronizeTypeTraits(annotationFromDatabase.typeId, Storage::TypeTraits{}); + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"remove type annotations"_t, + projectStorageCategory(), + keyValue("type annotation", annotationFromDatabase)}; + deleteTypeAnnotationStatement.write(annotationFromDatabase.typeId); }; @@ -1172,6 +1625,11 @@ private: toIntegers(updatedProjectSourceIds)); auto insert = [&](const Storage::Synchronization::ProjectData &projectData) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert project data"_t, + projectStorageCategory(), + keyValue("project data", projectData)}; + if (!projectData.projectSourceId) throw ProjectDataHasInvalidProjectSourceId{}; if (!projectData.sourceId) @@ -1187,6 +1645,13 @@ private: const Storage::Synchronization::ProjectData &projectData) { if (projectDataFromDatabase.fileType != projectData.fileType || !compareInvalidAreTrue(projectDataFromDatabase.moduleId, projectData.moduleId)) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"update project data"_t, + projectStorageCategory(), + keyValue("project data", projectData), + keyValue("project data from database", + projectDataFromDatabase)}; + updateProjectDataStatement.write(projectData.projectSourceId, projectData.sourceId, projectData.moduleId, @@ -1198,6 +1663,11 @@ private: }; auto remove = [&](const Storage::Synchronization::ProjectData &projectData) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"remove project data"_t, + projectStorageCategory(), + keyValue("project data", projectData)}; + deleteProjectDataStatement.write(projectData.projectSourceId, projectData.sourceId); }; @@ -1220,6 +1690,11 @@ private: toIntegers(updatedSourceIds)); auto insert = [&](const FileStatus &fileStatus) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert file status"_t, + projectStorageCategory(), + keyValue("file status", fileStatus)}; + if (!fileStatus.sourceId) throw FileStatusHasInvalidSourceId{}; insertFileStatusStatement.write(fileStatus.sourceId, @@ -1230,6 +1705,13 @@ private: auto update = [&](const FileStatus &fileStatusFromDatabase, const FileStatus &fileStatus) { if (fileStatusFromDatabase.lastModified != fileStatus.lastModified || fileStatusFromDatabase.size != fileStatus.size) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"update file status"_t, + projectStorageCategory(), + keyValue("file status", fileStatus), + keyValue("file status from database", + fileStatusFromDatabase)}; + updateFileStatusStatement.write(fileStatus.sourceId, fileStatus.size, fileStatus.lastModified); @@ -1240,6 +1722,11 @@ private: }; auto remove = [&](const FileStatus &fileStatus) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"remove file status"_t, + projectStorageCategory(), + keyValue("file status", fileStatus)}; + deleteFileStatusStatement.write(fileStatus.sourceId); }; @@ -1256,18 +1743,25 @@ private: NanotraceHR::Tracer tracer{"synchronize imports"_t, projectStorageCategory()}; synchromizeModuleExportedImports(moduleExportedImports, updatedModuleIds); + NanotraceHR::Tracer importTracer{"synchronize qml document imports"_t, + projectStorageCategory()}; synchronizeDocumentImports(imports, updatedSourceIds, Storage::Synchronization::ImportKind::Import); + importTracer.end(); + NanotraceHR::Tracer moduleDependenciesTracer{"synchronize module depdencies"_t, + projectStorageCategory()}; synchronizeDocumentImports(moduleDependencies, updatedModuleDependencySourceIds, Storage::Synchronization::ImportKind::ModuleDependency); + moduleDependenciesTracer.end(); } void synchromizeModuleExportedImports( Storage::Synchronization::ModuleExportedImports &moduleExportedImports, const ModuleIds &updatedModuleIds) { + NanotraceHR::Tracer tracer{"synchronize module exported imports"_t, projectStorageCategory()}; std::sort(moduleExportedImports.begin(), moduleExportedImports.end(), [](auto &&first, auto &&second) { @@ -1289,6 +1783,13 @@ private: }; auto insert = [&](const Storage::Synchronization::ModuleExportedImport &import) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert module exported import"_t, + projectStorageCategory(), + keyValue("module exported import", import), + keyValue("module id", import.moduleId)}; + tracer.tick("exported module"_t, keyValue("module id", import.exportedModuleId)); + if (import.version.minor) { insertModuleExportedImportWithVersionStatement.write(import.moduleId, import.exportedModuleId, @@ -1313,6 +1814,13 @@ private: }; auto remove = [&](const Storage::Synchronization::ModuleExportedImportView &view) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"remove module exported import"_t, + projectStorageCategory(), + keyValue("module exported import view", view), + keyValue("module id", view.moduleId)}; + tracer.tick("exported module"_t, keyValue("module id", view.exportedModuleId)); + deleteModuleExportedImportStatement.write(view.moduleExportedImportId); }; @@ -1321,27 +1829,48 @@ private: ModuleId fetchModuleIdUnguarded(Utils::SmallStringView name) const override { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch module id ungarded"_t, + projectStorageCategory(), + keyValue("module name", name)}; + auto moduleId = selectModuleIdByNameStatement.template value(name); - if (moduleId) - return moduleId; + if (!moduleId) + moduleId = insertModuleNameStatement.template value(name); - return insertModuleNameStatement.template value(name); + tracer.end(keyValue("module id", moduleId)); + + return moduleId; } auto fetchModuleNameUnguarded(ModuleId id) const { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch module name ungarded"_t, + projectStorageCategory(), + keyValue("module id", id)}; + auto moduleName = selectModuleNameStatement.template value(id); if (moduleName.empty()) throw ModuleDoesNotExists{}; + tracer.end(keyValue("module name", moduleName)); + return moduleName; } void handleAliasPropertyDeclarationsWithPropertyType( TypeId typeId, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"handle alias property declarations with property type"_t, + projectStorageCategory(), + keyValue("type id", typeId), + keyValue("relinkable alias property declarations", + relinkableAliasPropertyDeclarations)}; + auto callback = [&](TypeId typeId_, PropertyDeclarationId propertyDeclarationId, ImportedTypeNameId propertyImportedTypeNameId, @@ -1371,12 +1900,25 @@ private: void handlePropertyDeclarationWithPropertyType(TypeId typeId, PropertyDeclarations &relinkablePropertyDeclarations) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"handle property declarations with property type"_t, + projectStorageCategory(), + keyValue("type id", typeId), + keyValue("relinkable property declarations", + relinkablePropertyDeclarations)}; + updatesPropertyDeclarationPropertyTypeToNullStatement.readTo(relinkablePropertyDeclarations, typeId); } void handlePrototypes(TypeId prototypeId, Prototypes &relinkablePrototypes) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"handle prototypes"_t, + projectStorageCategory(), + keyValue("type id", prototypeId), + keyValue("relinkable prototypes", relinkablePrototypes)}; + auto callback = [&](TypeId typeId, ImportedTypeNameId prototypeNameId) { relinkablePrototypes.emplace_back(typeId, prototypeNameId); }; @@ -1386,6 +1928,12 @@ private: void handleExtensions(TypeId extensionId, Prototypes &relinkableExtensions) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"handle extension"_t, + projectStorageCategory(), + keyValue("type id", extensionId), + keyValue("relinkable extensions", relinkableExtensions)}; + auto callback = [&](TypeId typeId, ImportedTypeNameId extensionNameId) { relinkableExtensions.emplace_back(typeId, extensionNameId); }; @@ -1399,6 +1947,11 @@ private: Prototypes &relinkablePrototypes, Prototypes &relinkableExtensions) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"delete type"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + handlePropertyDeclarationWithPropertyType(typeId, relinkablePropertyDeclarations); handleAliasPropertyDeclarationsWithPropertyType(typeId, relinkableAliasPropertyDeclarations); handlePrototypes(typeId, relinkablePrototypes); @@ -1414,7 +1967,11 @@ private: void relinkAliasPropertyDeclarations(AliasPropertyDeclarations &aliasPropertyDeclarations, const TypeIds &deletedTypeIds) { - NanotraceHR::Tracer tracer{"relink alias properties"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"relink alias properties"_t, + projectStorageCategory(), + keyValue("alias property declarations", aliasPropertyDeclarations), + keyValue("deleted type ids", deletedTypeIds)}; std::sort(aliasPropertyDeclarations.begin(), aliasPropertyDeclarations.end()); @@ -1444,7 +2001,12 @@ private: void relinkPropertyDeclarations(PropertyDeclarations &relinkablePropertyDeclaration, const TypeIds &deletedTypeIds) { - NanotraceHR::Tracer tracer{"relink properties"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"relink property declarations"_t, + projectStorageCategory(), + keyValue("relinkable property declarations", + relinkablePropertyDeclaration), + keyValue("deleted type ids", deletedTypeIds)}; std::sort(relinkablePropertyDeclaration.begin(), relinkablePropertyDeclaration.end()); @@ -1470,7 +2032,11 @@ private: const TypeIds &deletedTypeIds, Callable updateStatement) { - NanotraceHR::Tracer tracer{"relink prototypes"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"relink prototypes"_t, + projectStorageCategory(), + keyValue("relinkable prototypes", relinkablePrototypes), + keyValue("deleted type ids", deletedTypeIds)}; std::sort(relinkablePrototypes.begin(), relinkablePrototypes.end()); @@ -1500,7 +2066,12 @@ private: Prototypes &relinkableExtensions, TypeIds &deletedTypeIds) { - NanotraceHR::Tracer tracer{"delete not updated types"_t, projectStorageCategory()}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"delete not updated types"_t, + projectStorageCategory(), + keyValue("updated type ids", updatedTypeIds), + keyValue("updated source ids", updatedSourceIds), + keyValue("type ids to be deleted", typeIdsToBeDeleted)}; auto callback = [&](TypeId typeId) { deletedTypeIds.push_back(typeId); @@ -1542,6 +2113,13 @@ private: Utils::SmallStringView aliasPropertyName, Utils::SmallStringView aliasPropertyNameTail) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch alias id"_t, + projectStorageCategory(), + keyValue("alias type id", aliasTypeId), + keyValue("alias property name", aliasPropertyName), + keyValue("alias property name tail", aliasPropertyNameTail)}; + if (aliasPropertyNameTail.empty()) return fetchPropertyDeclarationIdByTypeIdAndNameUngarded(aliasTypeId, aliasPropertyName); @@ -1554,6 +2132,11 @@ private: void linkAliasPropertyDeclarationAliasIds(const AliasPropertyDeclarations &aliasDeclarations) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"link alias property declarations alias ids"_t, + projectStorageCategory(), + keyValue("alias property declarations", aliasDeclarations)}; + for (const auto &aliasDeclaration : aliasDeclarations) { auto aliasTypeId = fetchTypeId(aliasDeclaration.aliasImportedTypeNameId); @@ -1575,6 +2158,11 @@ private: void updateAliasPropertyDeclarationValues(const AliasPropertyDeclarations &aliasDeclarations) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"update alias property declarations"_t, + projectStorageCategory(), + keyValue("alias property declarations", aliasDeclarations)}; + for (const auto &aliasDeclaration : aliasDeclarations) { updatetPropertiesDeclarationValuesOfAliasStatement.write( aliasDeclaration.propertyDeclarationId); @@ -1585,6 +2173,10 @@ private: void checkAliasPropertyDeclarationCycles(const AliasPropertyDeclarations &aliasDeclarations) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"check alias property declarations cycles"_t, + projectStorageCategory(), + keyValue("alias property declarations", aliasDeclarations)}; for (const auto &aliasDeclaration : aliasDeclarations) checkForAliasChainCycle(aliasDeclaration.propertyDeclarationId); } @@ -1592,6 +2184,7 @@ private: void linkAliases(const AliasPropertyDeclarations &insertedAliasPropertyDeclarations, const AliasPropertyDeclarations &updatedAliasPropertyDeclarations) { + using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"link aliases"_t, projectStorageCategory()}; linkAliasPropertyDeclarationAliasIds(insertedAliasPropertyDeclarations); @@ -1655,7 +2248,9 @@ private: using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"insert exported type"_t, projectStorageCategory(), - keyValue("exported type", type)}; + keyValue("exported type", type), + keyValue("type id", type.typeId), + keyValue("module id", type.moduleId)}; if (!type.moduleId) throw QmlDesigner::ModuleDoesNotExists{}; @@ -1688,7 +2283,9 @@ private: NanotraceHR::Tracer tracer{"update exported type"_t, projectStorageCategory(), keyValue("exported type", type), - keyValue("exported type view", view)}; + keyValue("exported type view", view), + keyValue("type id", type.typeId), + keyValue("module id", type.typeId)}; handlePropertyDeclarationWithPropertyType(view.typeId, relinkablePropertyDeclarations); handleAliasPropertyDeclarationsWithPropertyType(view.typeId, @@ -1704,7 +2301,9 @@ private: auto remove = [&](const Storage::Synchronization::ExportedTypeView &view) { NanotraceHR::Tracer tracer{"remove exported type"_t, projectStorageCategory(), - keyValue("exported type view", view)}; + keyValue("exported type", view), + keyValue("type id", view.typeId), + keyValue("module id", view.moduleId)}; handlePropertyDeclarationWithPropertyType(view.typeId, relinkablePropertyDeclarations); handleAliasPropertyDeclarationsWithPropertyType(view.typeId, @@ -1723,6 +2322,11 @@ private: SourceId sourceId, TypeId typeId) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert property declaration to alias"_t, + projectStorageCategory(), + keyValue("property declaration", value)}; + auto callback = [&](PropertyDeclarationId propertyDeclarationId) { insertedAliasPropertyDeclarations.emplace_back(typeId, propertyDeclarationId, @@ -1739,6 +2343,11 @@ private: void synchronizePropertyDeclarationsInsertProperty( const Storage::Synchronization::PropertyDeclaration &value, SourceId sourceId, TypeId typeId) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert property declaration"_t, + projectStorageCategory(), + keyValue("property declaration", value)}; + auto propertyImportedTypeNameId = fetchImportedTypeNameId(value.typeName, sourceId); auto propertyTypeId = fetchTypeId(propertyImportedTypeNameId); @@ -1765,13 +2374,18 @@ private: const Storage::Synchronization::PropertyDeclaration &value, SourceId sourceId) { - auto last = updatedAliasPropertyDeclarations.emplace_back(view.typeId, - view.id, - fetchImportedTypeNameId(value.typeName, - sourceId), - value.aliasPropertyName, - value.aliasPropertyNameTail, - view.aliasId); + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"update property declaration to alias"_t, + projectStorageCategory(), + keyValue("property declaration", value), + keyValue("property declaration view", view)}; + + updatedAliasPropertyDeclarations.emplace_back(view.typeId, + view.id, + fetchImportedTypeNameId(value.typeName, sourceId), + value.aliasPropertyName, + value.aliasPropertyNameTail, + view.aliasId); } auto synchronizePropertyDeclarationsUpdateProperty( @@ -1780,6 +2394,12 @@ private: SourceId sourceId, PropertyDeclarationIds &propertyDeclarationIds) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"update property declaration"_t, + projectStorageCategory(), + keyValue("property declaration", value), + keyValue("property declaration view", view)}; + auto propertyImportedTypeNameId = fetchImportedTypeNameId(value.typeName, sourceId); auto propertyTypeId = fetchTypeId(propertyImportedTypeNameId); @@ -1799,6 +2419,9 @@ private: propertyTypeId, value.traits); propertyDeclarationIds.push_back(view.id); + + tracer.end(keyValue("updated", "yes")); + return Sqlite::UpdateChange::Update; } @@ -1856,6 +2479,11 @@ private: }; auto remove = [&](const Storage::Synchronization::PropertyDeclarationView &view) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"remove property declaration"_t, + projectStorageCategory(), + keyValue("property declaratio viewn", view)}; + auto nextPropertyDeclarationId = selectPropertyDeclarationIdPrototypeChainDownStatement .template value(typeId, view.name); @@ -1872,31 +2500,47 @@ private: Sqlite::insertUpdateDelete(range, propertyDeclarations, compareKey, insert, update, remove); } + class AliasPropertyDeclarationView + { + public: + explicit AliasPropertyDeclarationView(Utils::SmallStringView name, + PropertyDeclarationId id, + PropertyDeclarationId aliasId) + : name{name} + , id{id} + , aliasId{aliasId} + {} + + template + friend void convertToString(String &string, + const AliasPropertyDeclarationView &aliasPropertyDeclarationView) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", aliasPropertyDeclarationView.name), + keyValue("id", aliasPropertyDeclarationView.id), + keyValue("alias id", aliasPropertyDeclarationView.aliasId)); + + convertToString(string, dict); + } + + public: + Utils::SmallStringView name; + PropertyDeclarationId id; + PropertyDeclarationId aliasId; + }; + void resetRemovedAliasPropertyDeclarationsToNull(Storage::Synchronization::Type &type, PropertyDeclarationIds &propertyDeclarationIds) { + NanotraceHR::Tracer tracer{"reset removed alias property declaration to null"_t, + projectStorageCategory()}; + if (type.changeLevel == Storage::Synchronization::ChangeLevel::Minimal) return; Storage::Synchronization::PropertyDeclarations &aliasDeclarations = type.propertyDeclarations; - class AliasPropertyDeclarationView - { - public: - explicit AliasPropertyDeclarationView(Utils::SmallStringView name, - PropertyDeclarationId id, - PropertyDeclarationId aliasId) - : name{name} - , id{id} - , aliasId{aliasId} - {} - - public: - Utils::SmallStringView name; - PropertyDeclarationId id; - PropertyDeclarationId aliasId; - }; - std::sort(aliasDeclarations.begin(), aliasDeclarations.end(), [](auto &&first, auto &&second) { return Sqlite::compare(first.name, second.name) < 0; }); @@ -1917,6 +2561,11 @@ private: }; auto remove = [&](const AliasPropertyDeclarationView &view) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"reset removed alias property declaration to null"_t, + projectStorageCategory(), + keyValue("alias property declaration view", view)}; + updatePropertyDeclarationAliasIdToNullStatement.write(view.id); propertyDeclarationIds.push_back(view.id); }; @@ -2007,7 +2656,8 @@ private: projectStorageCategory(), keyValue("import", import), keyValue("import kind", importKind), - keyValue("source id", import.sourceId)}; + keyValue("source id", import.sourceId), + keyValue("module id", import.moduleId)}; auto importId = insertDocumentImport(import, importKind, import.moduleId, ImportId{}); auto callback = [&](ModuleId exportedModuleId, int majorVersion, int minorVersion) { @@ -2022,7 +2672,9 @@ private: NanotraceHR::Tracer tracer{"insert indirect import"_t, projectStorageCategory(), keyValue("import", import), - keyValue("import kind", exportedImportKind)}; + keyValue("import kind", exportedImportKind), + keyValue("source id", import.sourceId), + keyValue("module id", import.moduleId)}; auto indirectImportId = insertDocumentImport(additionImport, exportedImportKind, @@ -2047,8 +2699,10 @@ private: using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"remove import"_t, projectStorageCategory(), + keyValue("import", view), keyValue("import id", view.importId), - keyValue("source id", view.sourceId)}; + keyValue("source id", view.sourceId), + keyValue("module id", view.moduleId)}; deleteDocumentImportStatement.write(view.importId); deleteDocumentImportsWithParentImportIdStatement.write(view.sourceId, view.importId); @@ -2059,6 +2713,9 @@ private: static Utils::PathString createJson(const Storage::Synchronization::ParameterDeclarations ¶meters) { + NanotraceHR::Tracer tracer{"create json from parameter declarations"_t, + projectStorageCategory()}; + Utils::PathString json; json.append("["); @@ -2088,11 +2745,20 @@ private: TypeId fetchTypeIdByModuleIdAndExportedName(ModuleId moduleId, Utils::SmallStringView name) const override { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch type id by module id and exported name"_t, + projectStorageCategory(), + keyValue("module id", moduleId), + keyValue("exported name", name)}; + return selectTypeIdByModuleIdAndExportedNameStatement.template value(moduleId, name); } void addTypeIdToPropertyEditorQmlPaths(Storage::Synchronization::PropertyEditorQmlPaths &paths) { + NanotraceHR::Tracer tracer{"add type id to property editor qml paths"_t, + projectStorageCategory()}; + for (auto &path : paths) path.typeId = fetchTypeIdByModuleIdAndExportedName(path.moduleId, path.typeName); } @@ -2106,6 +2772,19 @@ private: , directoryId{directoryId} {} + template + friend void convertToString(String &string, + const PropertyEditorQmlPathView &propertyEditorQmlPathView) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("type id", propertyEditorQmlPathView.typeId), + keyValue("source id", propertyEditorQmlPathView.pathId), + keyValue("directory id", propertyEditorQmlPathView.directoryId)); + + convertToString(string, dict); + } + public: TypeId typeId; SourceId pathId; @@ -2130,19 +2809,38 @@ private: }; auto insert = [&](const PropertyEditorQmlPath &path) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert property editor paths"_t, + projectStorageCategory(), + keyValue("property editor qml path", path)}; + if (path.typeId) insertPropertyEditorPathStatement.write(path.typeId, path.pathId, path.directoryId); }; auto update = [&](const PropertyEditorQmlPathView &view, const PropertyEditorQmlPath &value) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"update property editor paths"_t, + projectStorageCategory(), + keyValue("property editor qml path", value), + keyValue("property editor qml path view", view)}; + if (value.pathId != view.pathId || value.directoryId != view.directoryId) { updatePropertyEditorPathsStatement.write(value.typeId, value.pathId, value.directoryId); + + tracer.end(keyValue("updated", "yes")); + return Sqlite::UpdateChange::Update; } return Sqlite::UpdateChange::No; }; auto remove = [&](const PropertyEditorQmlPathView &view) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"remove property editor paths"_t, + projectStorageCategory(), + keyValue("property editor qml path view", view)}; + deletePropertyEditorPathStatement.write(view.typeId); }; @@ -2194,6 +2892,11 @@ private: }; auto insert = [&](const Storage::Synchronization::FunctionDeclaration &value) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert function declaration"_t, + projectStorageCategory(), + keyValue("function declaration", value)}; + Utils::PathString signature{createJson(value.parameters)}; insertFunctionDeclarationStatement.write(typeId, value.name, value.returnTypeName, signature); @@ -2201,6 +2904,12 @@ private: auto update = [&](const Storage::Synchronization::FunctionDeclarationView &view, const Storage::Synchronization::FunctionDeclaration &value) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"update function declaration"_t, + projectStorageCategory(), + keyValue("function declaration", value), + keyValue("function declaration view", view)}; + Utils::PathString signature{createJson(value.parameters)}; if (value.returnTypeName == view.returnTypeName) @@ -2208,10 +2917,17 @@ private: updateFunctionDeclarationStatement.write(view.id, value.returnTypeName); + tracer.end(keyValue("updated", "yes")); + return Sqlite::UpdateChange::Update; }; auto remove = [&](const Storage::Synchronization::FunctionDeclarationView &view) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"remove function declaration"_t, + projectStorageCategory(), + keyValue("function declaration view", view)}; + deleteFunctionDeclarationStatement.write(view.id); }; @@ -2251,6 +2967,11 @@ private: }; auto insert = [&](const Storage::Synchronization::SignalDeclaration &value) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert signal declaration"_t, + projectStorageCategory(), + keyValue("signal declaration", value)}; + Utils::PathString signature{createJson(value.parameters)}; insertSignalDeclarationStatement.write(typeId, value.name, signature); @@ -2262,6 +2983,11 @@ private: }; auto remove = [&](const Storage::Synchronization::SignalDeclarationView &view) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"remove signal declaration"_t, + projectStorageCategory(), + keyValue("signal declaration view", view)}; + deleteSignalDeclarationStatement.write(view.id); }; @@ -2271,6 +2997,10 @@ private: static Utils::PathString createJson( const Storage::Synchronization::EnumeratorDeclarations &enumeratorDeclarations) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"create json from enumerator declarations"_t, + projectStorageCategory()}; + Utils::PathString json; json.append("{"); @@ -2297,7 +3027,7 @@ private: void synchronizeEnumerationDeclarations( TypeId typeId, Storage::Synchronization::EnumerationDeclarations &enumerationDeclarations) { - NanotraceHR::Tracer tracer{"synchronize enumeation declaration"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"synchronize enumeration declaration"_t, projectStorageCategory()}; std::sort(enumerationDeclarations.begin(), enumerationDeclarations.end(), @@ -2314,6 +3044,11 @@ private: }; auto insert = [&](const Storage::Synchronization::EnumerationDeclaration &value) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert enumeration declaration"_t, + projectStorageCategory(), + keyValue("enumeration declaration", value)}; + Utils::PathString signature{createJson(value.enumeratorDeclarations)}; insertEnumerationDeclarationStatement.write(typeId, value.name, signature); @@ -2321,6 +3056,12 @@ private: auto update = [&](const Storage::Synchronization::EnumerationDeclarationView &view, const Storage::Synchronization::EnumerationDeclaration &value) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"update enumeration declaration"_t, + projectStorageCategory(), + keyValue("enumeration declaration", value), + keyValue("enumeration declaration view", view)}; + Utils::PathString enumeratorDeclarations{createJson(value.enumeratorDeclarations)}; if (enumeratorDeclarations == view.enumeratorDeclarations) @@ -2328,10 +3069,17 @@ private: updateEnumerationDeclarationStatement.write(view.id, enumeratorDeclarations); + tracer.end(keyValue("updated", "yes")); + return Sqlite::UpdateChange::Update; }; auto remove = [&](const Storage::Synchronization::EnumerationDeclarationView &view) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"remove enumeration declaration"_t, + projectStorageCategory(), + keyValue("enumeration declaration view", view)}; + deleteEnumerationDeclarationStatement.write(view.id); }; @@ -2448,6 +3196,17 @@ private: , defaultPropertyId{defaultPropertyId} {} + template + friend void convertToString(String &string, const TypeWithDefaultPropertyView &view) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("type id", view.typeId), + keyValue("property id", view.defaultPropertyId)); + + convertToString(string, dict); + } + TypeId typeId; PropertyDeclarationId defaultPropertyId; }; @@ -2469,6 +3228,12 @@ private: auto update = [&](const TypeWithDefaultPropertyView &view, const Storage::Synchronization::Type &value) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"reset default properties by update"_t, + projectStorageCategory(), + keyValue("value", value), + keyValue("view", view)}; + PropertyDeclarationId valueDefaultPropertyId; if (value.defaultPropertyName.size()) valueDefaultPropertyId = fetchPropertyDeclarationByTypeIdAndNameUngarded( @@ -2480,6 +3245,8 @@ private: updateDefaultPropertyIdStatement.write(value.typeId, valueDefaultPropertyId); + tracer.end(keyValue("updated", "yes")); + return Sqlite::UpdateChange::Update; }; @@ -2505,6 +3272,12 @@ private: auto update = [&](const TypeWithDefaultPropertyView &view, const Storage::Synchronization::Type &value) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"reset changed default properties by update"_t, + projectStorageCategory(), + keyValue("value", value), + keyValue("view", view)}; + PropertyDeclarationId valueDefaultPropertyId; if (value.defaultPropertyName.size()) { auto optionalValueDefaultPropertyId = fetchOptionalPropertyDeclarationByTypeIdAndNameUngarded( @@ -2518,6 +3291,8 @@ private: updateDefaultPropertyIdStatement.write(value.typeId, Sqlite::NullValue{}); + tracer.end(keyValue("updated", "yes")); + return Sqlite::UpdateChange::Update; }; @@ -2528,6 +3303,11 @@ private: void checkForPrototypeChainCycle(TypeId typeId) const { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"check for prototype chain cycle"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + auto callback = [=](TypeId currentTypeId) { if (typeId == currentTypeId) throw PrototypeChainCycle{}; @@ -2538,6 +3318,10 @@ private: void checkForAliasChainCycle(PropertyDeclarationId propertyDeclarationId) const { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"check for alias chain cycle"_t, + projectStorageCategory(), + keyValue("property declaration id", propertyDeclarationId)}; auto callback = [=](PropertyDeclarationId currentPropertyDeclarationId) { if (propertyDeclarationId == currentPropertyDeclarationId) throw AliasChainCycle{}; @@ -2553,7 +3337,7 @@ private: using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"fetch imported type name id and type id"_t, projectStorageCategory(), - keyValue("impoted type name", typeName), + keyValue("imported type name", typeName), keyValue("source id", sourceId)}; TypeId typeId; @@ -2626,18 +3410,29 @@ private: ImportId fetchImportId(SourceId sourceId, const Storage::Import &import) const { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch imported type name id"_t, + projectStorageCategory(), + keyValue("import", import), + keyValue("source id", sourceId)}; + + ImportId importId; if (import.version) { - return selectImportIdBySourceIdAndModuleIdAndVersionStatement.template value( + importId = selectImportIdBySourceIdAndModuleIdAndVersionStatement.template value( sourceId, import.moduleId, import.version.major.value, import.version.minor.value); + } else if (import.version.major) { + importId = selectImportIdBySourceIdAndModuleIdAndMajorVersionStatement + .template value(sourceId, + import.moduleId, + import.version.major.value); + } else { + importId = selectImportIdBySourceIdAndModuleIdStatement + .template value(sourceId, import.moduleId); } - if (import.version.major) { - return selectImportIdBySourceIdAndModuleIdAndMajorVersionStatement - .template value(sourceId, import.moduleId, import.version.major.value); - } + tracer.end(keyValue("import id", importId)); - return selectImportIdBySourceIdAndModuleIdStatement.template value(sourceId, - import.moduleId); + return importId; } ImportedTypeNameId fetchImportedTypeNameId(const Storage::Synchronization::ImportedTypeName &name, @@ -2662,7 +3457,6 @@ private: auto operator()(const Storage::Synchronization::QualifiedImportedType &importedType) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch imported type name id"_t, projectStorageCategory(), keyValue("imported type name", importedType.name), @@ -2693,13 +3487,22 @@ private: Id id, Utils::SmallStringView typeName) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch imported type name id"_t, + projectStorageCategory(), + keyValue("imported type name", typeName), + keyValue("kind", to_underlying(kind))}; + auto importedTypeNameId = selectImportedTypeNameIdStatement .template value(kind, id, typeName); - if (importedTypeNameId) - return importedTypeNameId; + if (!importedTypeNameId) + importedTypeNameId = insertImportedTypeNameIdStatement + .template value(kind, id, typeName); - return insertImportedTypeNameIdStatement.template value(kind, id, typeName); + tracer.end(keyValue("imported type name id", importedTypeNameId)); + + return importedTypeNameId; } TypeId fetchTypeId(ImportedTypeNameId typeNameId) const @@ -2756,6 +3559,18 @@ private: , propertyTraits{propertyTraits} {} + template + friend void convertToString(String &string, const FetchPropertyDeclarationResult &result) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("property type id", result.propertyTypeId), + keyValue("property declaration id", result.propertyDeclarationId), + keyValue("property traits", to_underlying(result.propertyTraits))); + + convertToString(string, dict); + } + public: TypeId propertyTypeId; PropertyDeclarationId propertyDeclarationId; @@ -2765,15 +3580,33 @@ private: auto fetchOptionalPropertyDeclarationByTypeIdAndNameUngarded(TypeId typeId, Utils::SmallStringView name) { - return selectPropertyDeclarationByTypeIdAndNameStatement - .template optionalValue(typeId, name); + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch optional property declaration by type id and name ungarded"_t, + projectStorageCategory(), + keyValue("type id", typeId), + keyValue("property name", name)}; + + auto propertyDeclaration = selectPropertyDeclarationByTypeIdAndNameStatement + .template optionalValue(typeId, + name); + + tracer.end(keyValue("property declaration", propertyDeclaration)); + + return propertyDeclaration; } FetchPropertyDeclarationResult fetchPropertyDeclarationByTypeIdAndNameUngarded( TypeId typeId, Utils::SmallStringView name) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch property declaration by type id and name ungarded"_t, + projectStorageCategory(), + keyValue("type id", typeId), + keyValue("property name", name)}; + auto propertyDeclaration = fetchOptionalPropertyDeclarationByTypeIdAndNameUngarded(typeId, name); + tracer.end(keyValue("property declaration", propertyDeclaration)); if (propertyDeclaration) return *propertyDeclaration; @@ -2784,9 +3617,17 @@ private: PropertyDeclarationId fetchPropertyDeclarationIdByTypeIdAndNameUngarded(TypeId typeId, Utils::SmallStringView name) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch property declaration id by type id and name ungarded"_t, + projectStorageCategory(), + keyValue("type id", typeId), + keyValue("property name", name)}; + auto propertyDeclarationId = selectPropertyDeclarationIdByTypeIdAndNameStatement .template value(typeId, name); + tracer.end(keyValue("property declaration id", propertyDeclarationId)); + if (propertyDeclarationId) return propertyDeclarationId; @@ -2795,44 +3636,106 @@ private: SourceContextId readSourceContextId(Utils::SmallStringView sourceContextPath) { - return selectSourceContextIdFromSourceContextsBySourceContextPathStatement - .template value(sourceContextPath); + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"read source context id"_t, + projectStorageCategory(), + keyValue("source context path", sourceContextPath)}; + + auto sourceContextId = selectSourceContextIdFromSourceContextsBySourceContextPathStatement + .template value(sourceContextPath); + + tracer.end(keyValue("source context id", sourceContextId)); + + return sourceContextId; } SourceContextId writeSourceContextId(Utils::SmallStringView sourceContextPath) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"write source context id"_t, + projectStorageCategory(), + keyValue("source context path", sourceContextPath)}; + insertIntoSourceContextsStatement.write(sourceContextPath); - return SourceContextId::create(database.lastInsertedRowId()); + auto sourceContextId = SourceContextId::create(database.lastInsertedRowId()); + + tracer.end(keyValue("source context id", sourceContextId)); + + return sourceContextId; } SourceId writeSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"write source id"_t, + projectStorageCategory(), + keyValue("source context id", sourceContextId), + keyValue("source name", sourceName)}; + insertIntoSourcesStatement.write(sourceContextId, sourceName); - return SourceId::create(database.lastInsertedRowId()); + auto sourceId = SourceId::create(database.lastInsertedRowId()); + + tracer.end(keyValue("source id", sourceId)); + + return sourceId; } SourceId readSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName) { - return selectSourceIdFromSourcesBySourceContextIdAndSourceNameStatement - .template value(sourceContextId, sourceName); + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"read source id"_t, + projectStorageCategory(), + keyValue("source context id", sourceContextId), + keyValue("source name", sourceName)}; + + auto sourceId = selectSourceIdFromSourcesBySourceContextIdAndSourceNameStatement + .template value(sourceContextId, sourceName); + + tracer.end(keyValue("source id", sourceId)); + + return sourceId; } auto fetchExportedTypes(TypeId typeId) { - return selectExportedTypesByTypeIdStatement - .template values(typeId); + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch exported type"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + auto typeIds = selectExportedTypesByTypeIdStatement + .template values(typeId); + + tracer.end(keyValue("type ids", typeIds)); + + return typeIds; } auto fetchPropertyDeclarations(TypeId typeId) { - return selectPropertyDeclarationsByTypeIdStatement - .template values(typeId); + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch property declarations"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + auto propertyDeclarations = selectPropertyDeclarationsByTypeIdStatement + .template values( + typeId); + + tracer.end(keyValue("property declarations", propertyDeclarations)); + + return propertyDeclarations; } auto fetchFunctionDeclarations(TypeId typeId) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch signal declarations"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + Storage::Synchronization::FunctionDeclarations functionDeclarations; auto callback = [&](Utils::SmallStringView name, @@ -2846,11 +3749,18 @@ private: selectFunctionDeclarationsForTypeIdWithoutSignatureStatement.readCallback(callback, typeId); + tracer.end(keyValue("function declarations", functionDeclarations)); + return functionDeclarations; } auto fetchSignalDeclarations(TypeId typeId) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch signal declarations"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + Storage::Synchronization::SignalDeclarations signalDeclarations; auto callback = [&](Utils::SmallStringView name, SignalDeclarationId signalDeclarationId) { @@ -2862,11 +3772,18 @@ private: selectSignalDeclarationsForTypeIdWithoutSignatureStatement.readCallback(callback, typeId); + tracer.end(keyValue("signal declarations", signalDeclarations)); + return signalDeclarations; } auto fetchEnumerationDeclarations(TypeId typeId) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch enumeration declarations"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + Storage::Synchronization::EnumerationDeclarations enumerationDeclarations; auto callback = [&](Utils::SmallStringView name, @@ -2881,6 +3798,8 @@ private: selectEnumerationDeclarationsForTypeIdWithoutEnumeratorDeclarationsStatement .readCallback(callback, typeId); + tracer.end(keyValue("enumeration declarations", enumerationDeclarations)); + return enumerationDeclarations; } diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h index 4dd023ae9fa..3be6fc4cae4 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h @@ -24,6 +24,20 @@ constexpr std::underlying_type_t to_underlying(Enumeration enumerat enum class FlagIs : unsigned int { False, Set, True }; +template +void convertToString(String &string, const FlagIs &flagIs) +{ + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + + if (flagIs == FlagIs::False) + convertToString(string, false); + else if (flagIs == FlagIs::True) + convertToString(string, true); + else + convertToString(string, "is set"); +} + } // namespace QmlDesigner namespace QmlDesigner::Storage { @@ -46,6 +60,18 @@ constexpr bool operator&(PropertyDeclarationTraits first, PropertyDeclarationTra return static_cast(first) & static_cast(second); } +template +void convertToString(String &string, const PropertyDeclarationTraits &traits) +{ + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("is read only", traits & PropertyDeclarationTraits::IsReadOnly), + keyValue("is pointer", traits & PropertyDeclarationTraits::IsPointer), + keyValue("is list", traits & PropertyDeclarationTraits::IsList)); + + convertToString(string, dict); +} + enum class TypeTraitsKind : unsigned int { None, Reference, @@ -53,6 +79,25 @@ enum class TypeTraitsKind : unsigned int { Sequence, }; +template +void convertToString(String &string, const TypeTraitsKind &kind) +{ + switch (kind) { + case TypeTraitsKind::None: + convertToString(string, "None"); + break; + case TypeTraitsKind::Reference: + convertToString(string, "Reference"); + break; + case TypeTraitsKind::Value: + convertToString(string, "Value"); + break; + case TypeTraitsKind::Sequence: + convertToString(string, "Sequence"); + break; + } +} + struct TypeTraits { constexpr TypeTraits() @@ -100,6 +145,35 @@ struct TypeTraits return first.type == second.type && first.annotation == second.annotation; } + template + friend void convertToString(String &string, const TypeTraits &typeTraits) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary( + keyValue("kind", typeTraits.kind), + keyValue("is enum", typeTraits.isEnum), + keyValue("is file component", typeTraits.isFileComponent), + keyValue("is project component", typeTraits.isProjectComponent), + keyValue("is in project module", typeTraits.isInProjectModule), + keyValue("uses custom parser", typeTraits.usesCustomParser), + keyValue("can be container", typeTraits.canBeContainer), + keyValue("force clip", typeTraits.forceClip), + keyValue("does layout children", typeTraits.doesLayoutChildren), + keyValue("can be dropped in form editor", typeTraits.canBeDroppedInFormEditor), + keyValue("can be dropped in navigator", typeTraits.canBeDroppedInNavigator), + keyValue("can be dropped in view 3D", typeTraits.canBeDroppedInView3D), + keyValue("is movable", typeTraits.isMovable), + keyValue("is resizable", typeTraits.isResizable), + keyValue("has form editor item", typeTraits.hasFormEditorItem), + keyValue("is stacked container", typeTraits.isStackedContainer), + keyValue("takes over rendering of children", typeTraits.takesOverRenderingOfChildren), + keyValue("visible in navigator", typeTraits.visibleInNavigator), + keyValue("visible in library", typeTraits.visibleInLibrary)); + + convertToString(string, dict); + } + union { struct { @@ -202,21 +276,22 @@ public: explicit operator bool() const { return major && minor; } + template + friend void convertToString(String &string, const Version &version) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("major version", version.major.value), + keyValue("minor version", version.minor.value)); + + convertToString(string, dict); + } + public: VersionNumber major; VersionNumber minor; }; -template -void convertToString(String &string, const Version &version) -{ - using NanotraceHR::dictonary; - using NanotraceHR::keyValue; - auto dict = dictonary(keyValue("major version", version.major.value), - keyValue("minor version", version.minor.value)); - - convertToString(string, dict); -} } // namespace QmlDesigner::Storage namespace QmlDesigner::Storage::Info { @@ -228,6 +303,17 @@ struct TypeHint , expression{expression} {} + template + friend void convertToString(String &string, const TypeHint &typeHint) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", typeHint.name), + keyValue("expression", typeHint.expression)); + + convertToString(string, dict); + } + Utils::SmallString name; Utils::PathString expression; }; @@ -242,6 +328,18 @@ struct ItemLibraryProperty , value{value} {} + template + friend void convertToString(String &string, const ItemLibraryProperty &property) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", property.name), + keyValue("type", property.type), + keyValue("value", property.value)); + + convertToString(string, dict); + } + Utils::SmallString name; Utils::SmallString type; Sqlite::Value value; @@ -285,6 +383,24 @@ struct ItemLibraryEntry , properties{std::move(properties)} {} + template + friend void convertToString(String &string, const ItemLibraryEntry &entry) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("type id", entry.typeId), + keyValue("name", entry.name), + keyValue("icon path", entry.iconPath), + keyValue("category", entry.category), + keyValue("import", entry.import), + keyValue("tool tip", entry.toolTip), + keyValue("template path", entry.templatePath), + keyValue("properties", entry.properties), + keyValue("extra file paths", entry.extraFilePaths)); + + convertToString(string, dict); + } + TypeId typeId; Utils::SmallString name; Utils::PathString iconPath; @@ -332,6 +448,18 @@ public: < std::tie(second.moduleId, second.name, second.version); } + template + friend void convertToString(String &string, const ExportedTypeName &exportedTypeName) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", exportedTypeName.name), + keyValue("version", exportedTypeName.version), + keyValue("module id", exportedTypeName.moduleId)); + + convertToString(string, dict); + } + public: ::Utils::SmallString name; Storage::Version version; @@ -353,6 +481,19 @@ public: , propertyTypeId{propertyTypeId} {} + template + friend void convertToString(String &string, const PropertyDeclaration &propertyDeclaration) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("type id", propertyDeclaration.typeId), + keyValue("name", propertyDeclaration.name), + keyValue("traits", propertyDeclaration.traits), + keyValue("property type id", propertyDeclaration.propertyTypeId)); + + convertToString(string, dict); + } + TypeId typeId; ::Utils::SmallString name; PropertyDeclarationTraits traits; @@ -377,6 +518,18 @@ public: , traits{traits} {} + template + friend void convertToString(String &string, const Type &type) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("default property id", type.defaultPropertyId), + keyValue("source id", type.sourceId), + keyValue("traits", type.traits)); + + convertToString(string, dict); + } + PropertyDeclarationId defaultPropertyId; SourceId sourceId; TypeTraits traits; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h index f3272a71157..76c7722d6cf 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h @@ -46,6 +46,17 @@ public: < std::tie(second.sourceId, second.moduleId, second.version); } + template + friend void convertToString(String &string, const Import &import) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("module id", import.moduleId), + keyValue("source id", import.sourceId), + keyValue("version", import.version)); + convertToString(string, dict); + } + public: Storage::Version version; ModuleId moduleId; @@ -54,26 +65,54 @@ public: using Imports = std::vector; -template -void convertToString(String &string, const Import &import) -{ - using NanotraceHR::dictonary; - using NanotraceHR::keyValue; - auto dict = dictonary(keyValue("module id", import.moduleId), - keyValue("source id", import.sourceId), - keyValue("version", import.version)); - convertToString(string, dict); -} - namespace Synchronization { enum class TypeNameKind { Exported = 1, QualifiedExported = 2 }; +template +void convertToString(String &string, const TypeNameKind &kind) +{ + switch (kind) { + case TypeNameKind::Exported: + convertToString(string, "Exported"); + break; + case TypeNameKind::QualifiedExported: + convertToString(string, "QualifiedExported"); + break; + } +} + enum class FileType : char { QmlTypes, QmlDocument }; +template +void convertToString(String &string, const FileType &type) +{ + switch (type) { + case FileType::QmlTypes: + convertToString(string, "QmlTypes"); + break; + case FileType::QmlDocument: + convertToString(string, "QmlDocument"); + break; + } +} + enum class IsQualified : int { No, Yes }; -inline int operator-(IsQualified first, IsQualified second) +template +void convertToString(String &string, const IsQualified &isQualified) +{ + switch (isQualified) { + case IsQualified::No: + convertToString(string, "No"); + break; + case IsQualified::Yes: + convertToString(string, "Yes"); + break; + } +} + +inline int operator-(IsQualified first, const IsQualified &second) { return static_cast(first) - static_cast(second); } @@ -90,6 +129,25 @@ enum class ImportKind : char { ModuleExportedModuleDependency }; +template +void convertToString(String &string, const ImportKind &kind) +{ + switch (kind) { + case ImportKind::Import: + convertToString(string, "Import"); + break; + case ImportKind::ModuleDependency: + convertToString(string, "ModuleDependency"); + break; + case ImportKind::ModuleExportedImport: + convertToString(string, "ModuleExportedImport"); + break; + case ImportKind::ModuleExportedModuleDependency: + convertToString(string, "ModuleExportedModuleDependency"); + break; + } +} + class ImportView { public: @@ -109,6 +167,19 @@ public: && first.version == second.version; } + template + friend void convertToString(String &string, const ImportView &import) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("import id", import.importId), + keyValue("source id", import.sourceId), + keyValue("module id", import.moduleId), + keyValue("version", import.version)); + + convertToString(string, dict); + } + public: ImportId importId; SourceId sourceId; @@ -118,6 +189,19 @@ public: enum class IsAutoVersion : char { No, Yes }; +template +void convertToString(String &string, const IsAutoVersion &isAutoVersion) +{ + switch (isAutoVersion) { + case IsAutoVersion::No: + convertToString(string, "No"); + break; + case IsAutoVersion::Yes: + convertToString(string, "Yes"); + break; + } +} + constexpr bool operator<(IsAutoVersion first, IsAutoVersion second) { return to_underlying(first) < to_underlying(second); @@ -149,6 +233,19 @@ public: < std::tie(second.moduleId, second.exportedModuleId, second.isAutoVersion, second.version); } + template + friend void convertToString(String &string, const ModuleExportedImport &import) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("module id", import.moduleId), + keyValue("exported module id", import.exportedModuleId), + keyValue("version", import.version), + keyValue("is auto version", import.isAutoVersion)); + + convertToString(string, dict); + } + public: Storage::Version version; ModuleId moduleId; @@ -183,6 +280,20 @@ public: && first.version == second.version && first.isAutoVersion == second.isAutoVersion; } + template + friend void convertToString(String &string, const ModuleExportedImportView &import) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("module exported import id", import.moduleExportedImportId), + keyValue("module id", import.moduleId), + keyValue("exported module id", import.exportedModuleId), + keyValue("version", import.version), + keyValue("is auto version", import.isAutoVersion)); + + convertToString(string, dict); + } + public: ModuleExportedImportId moduleExportedImportId; Storage::Version version; @@ -204,6 +315,16 @@ public: return first.name == second.name; } + template + friend void convertToString(String &string, const ImportedType &importedType) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", importedType.name)); + + convertToString(string, dict); + } + public: TypeNameString name; }; @@ -222,6 +343,17 @@ public: return first.name == second.name && first.import == second.import; } + template + friend void convertToString(String &string, const QualifiedImportedType &importedType) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", importedType.name), + keyValue("import", importedType.import)); + + convertToString(string, dict); + } + public: TypeNameString name; Import import; @@ -276,6 +408,19 @@ public: < std::tie(second.moduleId, second.name, second.version); } + template + friend void convertToString(String &string, const ExportedType &exportedType) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", exportedType.name), + keyValue("module id", exportedType.moduleId), + keyValue("type id", exportedType.typeId), + keyValue("version", exportedType.version)); + + convertToString(string, dict); + } + public: ::Utils::SmallString name; Storage::Version version; @@ -285,19 +430,6 @@ public: using ExportedTypes = std::vector; -template -void convertToString(String &string, const ExportedType &exportedType) -{ - using NanotraceHR::dictonary; - using NanotraceHR::keyValue; - auto dict = dictonary(keyValue("name", exportedType.name), - keyValue("module id", exportedType.moduleId), - keyValue("type id", exportedType.typeId), - keyValue("version", exportedType.version)); - - convertToString(string, dict); -} - class ExportedTypeView { public: @@ -320,6 +452,20 @@ public: , exportedTypeNameId{exportedTypeNameId} {} + template + friend void convertToString(String &string, const ExportedTypeView &exportedType) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", exportedType.name), + keyValue("module id", exportedType.moduleId), + keyValue("type id", exportedType.typeId), + keyValue("version", exportedType.version), + keyValue("version", exportedType.exportedTypeNameId)); + + convertToString(string, dict); + } + public: ::Utils::SmallStringView name; Storage::Version version; @@ -328,20 +474,6 @@ public: ExportedTypeNameId exportedTypeNameId; }; -template -void convertToString(String &string, const ExportedTypeView &exportedType) -{ - using NanotraceHR::dictonary; - using NanotraceHR::keyValue; - auto dict = dictonary(keyValue("name", exportedType.name), - keyValue("module id", exportedType.moduleId), - keyValue("type id", exportedType.typeId), - keyValue("version", exportedType.version), - keyValue("version", exportedType.exportedTypeNameId)); - - convertToString(string, dict); -} - using ImportedTypeName = std::variant; template @@ -361,7 +493,7 @@ void convertToString(String &string, const ImportedTypeName &typeName) void operator()(const QmlDesigner::Storage::Synchronization::ImportedType &importedType) const { - auto dict = dictonary(keyValue("name", importedType.name), keyValue("import", "empty")); + auto dict = dictonary(keyValue("name", importedType.name)); convertToString(string, dict); } @@ -401,6 +533,18 @@ public: && first.hasValue == second.hasValue; } + template + friend void convertToString(String &string, const EnumeratorDeclaration &enumeratorDeclaration) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", enumeratorDeclaration.name), + keyValue("value", enumeratorDeclaration.value), + keyValue("has value", enumeratorDeclaration.hasValue)); + + convertToString(string, dict); + } + public: ::Utils::SmallString name; long long value = 0; @@ -425,6 +569,18 @@ public: && first.enumeratorDeclarations == second.enumeratorDeclarations; } + template + friend void convertToString(String &string, const EnumerationDeclaration &enumerationDeclaration) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", enumerationDeclaration.name), + keyValue("enumerator declarations", + enumerationDeclaration.enumeratorDeclarations)); + + convertToString(string, dict); + } + public: TypeNameString name; EnumeratorDeclarations enumeratorDeclarations; @@ -444,6 +600,20 @@ public: , id{id} {} + template + friend void convertToString(String &string, + const EnumerationDeclarationView &enumerationDeclaration) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", enumerationDeclaration.name), + keyValue("enumerator declarations", + enumerationDeclaration.enumeratorDeclarations), + keyValue("id", enumerationDeclaration.id)); + + convertToString(string, dict); + } + public: ::Utils::SmallStringView name; ::Utils::SmallStringView enumeratorDeclarations; @@ -468,6 +638,18 @@ public: && first.traits == second.traits; } + template + friend void convertToString(String &string, const ParameterDeclaration ¶meterDeclaration) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", parameterDeclaration.name), + keyValue("type name", parameterDeclaration.typeName), + keyValue("traits", parameterDeclaration.traits)); + + convertToString(string, dict); + } + public: ::Utils::SmallString name; TypeNameString typeName; @@ -494,6 +676,17 @@ public: return first.name == second.name && first.parameters == second.parameters; } + template + friend void convertToString(String &string, const SignalDeclaration &signalDeclaration) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", signalDeclaration.name), + keyValue("parameters", signalDeclaration.parameters)); + + convertToString(string, dict); + } + public: ::Utils::SmallString name; ParameterDeclarations parameters; @@ -513,6 +706,18 @@ public: , id{id} {} + template + friend void convertToString(String &string, const SignalDeclarationView &signalDeclaration) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", signalDeclaration.name), + keyValue("signature", signalDeclaration.signature), + keyValue("id", signalDeclaration.id)); + + convertToString(string, dict); + } + public: ::Utils::SmallStringView name; ::Utils::SmallStringView signature; @@ -543,6 +748,18 @@ public: && first.parameters == second.parameters; } + template + friend void convertToString(String &string, const FunctionDeclaration &functionDeclaration) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", functionDeclaration.name), + keyValue("return type name", functionDeclaration.returnTypeName), + keyValue("parameters", functionDeclaration.parameters)); + + convertToString(string, dict); + } + public: ::Utils::SmallString name; TypeNameString returnTypeName; @@ -565,6 +782,19 @@ public: , id{id} {} + template + friend void convertToString(String &string, const FunctionDeclarationView &functionDeclaration) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", functionDeclaration.name), + keyValue("return type name", functionDeclaration.returnTypeName), + keyValue("signature", functionDeclaration.signature), + keyValue("id", functionDeclaration.id)); + + convertToString(string, dict); + } + public: ::Utils::SmallStringView name; ::Utils::SmallStringView returnTypeName; @@ -574,6 +804,19 @@ public: enum class PropertyKind { Property, Alias }; +template +void convertToString(String &string, const PropertyKind &kind) +{ + switch (kind) { + case PropertyKind::Property: + convertToString(string, "Property"); + break; + case PropertyKind::Alias: + convertToString(string, "Alias"); + break; + } +} + class PropertyDeclaration { public: @@ -643,6 +886,24 @@ public: && first.traits == second.traits && first.kind == second.kind; } + template + friend void convertToString(String &string, const PropertyDeclaration &propertyDeclaration) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", propertyDeclaration.name), + keyValue("type name", propertyDeclaration.typeName), + keyValue("alias property name", propertyDeclaration.aliasPropertyName), + keyValue("alias property name tail", + propertyDeclaration.aliasPropertyNameTail), + keyValue("traits", propertyDeclaration.traits), + keyValue("type id", propertyDeclaration.typeId), + keyValue("property type id", propertyDeclaration.propertyTypeId), + keyValue("kind", to_underlying(propertyDeclaration.kind))); + + convertToString(string, dict); + } + public: ::Utils::SmallString name; ImportedTypeName typeName; @@ -673,6 +934,21 @@ public: , aliasId{aliasId} {} + template + friend void convertToString(String &string, const PropertyDeclarationView &propertyDeclaration) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", propertyDeclaration.name), + keyValue("traits", propertyDeclaration.traits), + keyValue("type id", propertyDeclaration.typeId), + keyValue("type name id", propertyDeclaration.typeNameId), + keyValue("id", propertyDeclaration.id), + keyValue("alias id", propertyDeclaration.aliasId)); + + convertToString(string, dict); + } + public: ::Utils::SmallStringView name; PropertyDeclarationTraits traits = {}; @@ -684,6 +960,22 @@ public: enum class ChangeLevel : char { Full, Minimal, ExcludeExportedTypes }; +template +void convertToString(String &string, const ChangeLevel &changeLevel) +{ + switch (changeLevel) { + case ChangeLevel::Full: + convertToString(string, "Full"); + break; + case ChangeLevel::Minimal: + convertToString(string, "Minimal"); + break; + case ChangeLevel::ExcludeExportedTypes: + convertToString(string, "ExcludeExportedTypes"); + break; + } +} + class Type { public: @@ -793,6 +1085,27 @@ public: && first.sourceId == second.sourceId; } + template + friend void convertToString(String &string, const Type &type) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("type name", type.typeName), + keyValue("prototype", type.prototype), + keyValue("extension", type.extension), + keyValue("exported types", type.exportedTypes), + keyValue("property declarations", type.propertyDeclarations), + keyValue("function declarations", type.functionDeclarations), + keyValue("signal declarations", type.signalDeclarations), + keyValue("enumeration declarations", type.enumerationDeclarations), + keyValue("traits", type.traits), + keyValue("source id", type.sourceId), + keyValue("change level", to_underlying(type.changeLevel)), + keyValue("default property name", type.defaultPropertyName)); + + convertToString(string, dict); + } + public: TypeNameString typeName; ::Utils::SmallString defaultPropertyName; @@ -823,6 +1136,20 @@ public: , moduleId{moduleId} {} + template + friend void convertToString(String &string, const PropertyEditorQmlPath &propertyEditorQmlPath) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("type name", propertyEditorQmlPath.typeName), + keyValue("type id", propertyEditorQmlPath.typeId), + keyValue("path id", propertyEditorQmlPath.pathId), + keyValue("directory id", propertyEditorQmlPath.directoryId), + keyValue("module id", propertyEditorQmlPath.moduleId)); + + convertToString(string, dict); + } + public: TypeNameString typeName; TypeId typeId; @@ -850,6 +1177,19 @@ public: && first.fileType == second.fileType; } + template + friend void convertToString(String &string, const ProjectData &projectData) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("project source id", projectData.projectSourceId), + keyValue("source id", projectData.sourceId), + keyValue("module id", projectData.moduleId), + keyValue("file type", to_underlying(projectData.fileType))); + + convertToString(string, dict); + } + public: SourceId projectSourceId; SourceId sourceId; @@ -881,6 +1221,23 @@ public: , traits{traits} {} + template + friend void convertToString(String &string, const TypeAnnotation &typeAnnotation) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("type name", typeAnnotation.typeName), + keyValue("icon path", typeAnnotation.iconPath), + keyValue("item library json", typeAnnotation.itemLibraryJson), + keyValue("hints json", typeAnnotation.hintsJson), + keyValue("type id", typeAnnotation.typeId), + keyValue("source id", typeAnnotation.sourceId), + keyValue("module id", typeAnnotation.moduleId), + keyValue("traits", typeAnnotation.traits)); + + convertToString(string, dict); + } + public: TypeNameString typeName; Utils::PathString iconPath; From 3722710c42223834a312441ce95a723a692f533e Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 20 Mar 2024 14:00:32 +0100 Subject: [PATCH 038/202] QmlDesigner: fix icon generation One missed #ifdef for the project cache. Task-number: QDS-12253 Change-Id: Ib9fe2ef085d1208c3676fb184cb15899fa89bbde Reviewed-by: Tim Jenssen --- .../qmldesigner/designercore/imagecache/imagecachecollector.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp index 955e676d3ba..97148e664f1 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp @@ -76,8 +76,10 @@ void ImageCacheCollector::start(Utils::SmallStringView name, AbortCallback abortCallback, ImageCache::TraceToken traceToken) { +#ifdef QDS_USE_PROJECTSTORAGE if (!m_projectStorage || !m_pathCache) return; +#endif using namespace NanotraceHR::Literals; auto [collectorTraceToken, flowtoken] = traceToken.beginDurationWithFlow( From 5319fae46e88c0c5002691ba0a8df38947b91de4 Mon Sep 17 00:00:00 2001 From: Teea Poldsam Date: Tue, 19 Mar 2024 12:29:36 +0200 Subject: [PATCH 039/202] Doc: Fix QDS docs warnings Fixed some formatting issues and updated some links. Fixes: QDS-12247 Change-Id: Ibca5791f8c2f6cbda50f604892d131787910804c Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Leena Miettinen Reviewed-by: Mats Honkamaa --- .../external-resources-qds.qdoc | 36 +++++++++++++++++++ doc/qtcreator/src/vcs/creator-vcs-git.qdoc | 4 --- .../qtquick-component-context-menu.qdocinc | 4 +-- .../studio-designer-developer-workflow.qdoc | 6 ++-- .../overviews/qtquick-creating-ui-logic.qdoc | 2 +- .../qtdesignstudio-3d-editor.qdoc | 20 ++++++----- .../src/views/qtquick-effect-maker-view.qdoc | 2 +- 7 files changed, 54 insertions(+), 20 deletions(-) diff --git a/doc/qtcreator/src/external-resources/external-resources-qds.qdoc b/doc/qtcreator/src/external-resources/external-resources-qds.qdoc index d5b7fcf1bd2..2a340b9ea99 100644 --- a/doc/qtcreator/src/external-resources/external-resources-qds.qdoc +++ b/doc/qtcreator/src/external-resources/external-resources-qds.qdoc @@ -69,3 +69,39 @@ \externalpage https://www.qt.io/download \title Try Qt */ +/*! + \externalpage https://doc.qt.io/qt-5/custom-material-reference.html + \title Qt Quick 3D Custom Material Reference +*/ +/*! + \externalpage https://doc.qt.io/qt-5/qml-qtquick3d-arealight.html + \title AreaLight +*/ +/*! + \externalpage https://doc.qt.io/qt-5/qml-qtquick3d-shaderinfo.html + \title ShaderInfo +*/ +/*! + \externalpage https://doc.qt.io/qt-5/qml-qtquick3d-bufferblit.html + \title BufferBlit +*/ +/*! + \externalpage https://doc.qt.io/qt-5/qml-qtquick3d-cullmode.html + \title CullMode +*/ +/*! + \externalpage https://doc.qt.io/qt-5/qml-qtquick3d-depthinput.html + \title DepthInput +*/ +/*! + \externalpage https://doc.qt.io/qt-5/qml-qtquick3d-renderstate.html + \title RenderState +*/ +/*! + \externalpage https://doc.qt.io/qt/i18n-source-translation.html#mark-strings-for-translation + \title Mark Strings for Translation +*/ +/*! + \externalpage https://doc.qt.io/qt/linguist-id-based-i18n.html + \title Text ID based translations +*/ diff --git a/doc/qtcreator/src/vcs/creator-vcs-git.qdoc b/doc/qtcreator/src/vcs/creator-vcs-git.qdoc index ea23e53443b..e853a868122 100644 --- a/doc/qtcreator/src/vcs/creator-vcs-git.qdoc +++ b/doc/qtcreator/src/vcs/creator-vcs-git.qdoc @@ -30,10 +30,6 @@ You can use the \l{http://code.google.com/p/gerrit/}{Gerrit} code review tool for projects that use Git. - \if defined(qtdesignstudio) - \include creator-vcs-options.qdocinc vcs options - \endif - \if defined(qtcreator) \section1 Using Git for Windows diff --git a/doc/qtdesignstudio/src/components/qtquick-component-context-menu.qdocinc b/doc/qtdesignstudio/src/components/qtquick-component-context-menu.qdocinc index 34140b88947..7ab83cc737e 100644 --- a/doc/qtdesignstudio/src/components/qtquick-component-context-menu.qdocinc +++ b/doc/qtdesignstudio/src/components/qtquick-component-context-menu.qdocinc @@ -53,8 +53,8 @@ \li Move Component Instances into Separate Files \li \l{Turning Component Instances into Custom Components} \row - \li Add New Signal Handler - \li \l{Adding Signal Handlers} + \li Connecting Components to Signals + \li \l{Connecting Components to Signals in the Connection View} \row \li Go to Implementation \li \l{Using UI Files} diff --git a/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc b/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc index 74ffff54688..8c13b7b116c 100644 --- a/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc +++ b/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc @@ -46,7 +46,7 @@ \li Select \uicontrol {File} > \uicontrol {Export Project} > \uicontrol {Generate CMake Build Files}. \image studio-project-export.webp "Export the \QDS project for Qt Creator" - \li Select \uicontrol {Details} to access the \l {Advanced Options}. + \li Select \uicontrol {Details} to access the \uicontrol {Advanced Options}. \image studio-project-export-advanced.webp "Access Advanced Options in the project exporter" \note The project exporter has default settings selected. This works better if the project @@ -68,7 +68,7 @@ After exporting the project from the \QDS, you have to open it from Qt Creator. If you have used any version before \QDS 4.0 to create the project, manually include this code - in the \l {CMakeLists.txt} file so the exported project works in Qt Creator. + in the \e {CMakeLists.txt} file so the exported project works in Qt Creator. \code set(BUILD_QDS_COMPONENTS ON CACHE BOOL "Build design studio components") @@ -83,6 +83,6 @@ \endcode \note If you have created the project with the \QDS version 4.0 or above, you already have this code in - \l {CMakeLists.txt} by default. + \e {CMakeLists.txt} by default. */ diff --git a/doc/qtdesignstudio/src/overviews/qtquick-creating-ui-logic.qdoc b/doc/qtdesignstudio/src/overviews/qtquick-creating-ui-logic.qdoc index a5e05f8beb3..1ea19bac4b7 100644 --- a/doc/qtdesignstudio/src/overviews/qtquick-creating-ui-logic.qdoc +++ b/doc/qtdesignstudio/src/overviews/qtquick-creating-ui-logic.qdoc @@ -84,7 +84,7 @@ \li \l{Connecting Components to Signals} \row \li Formatting connections - \li \l{Adding Actions and Assignments} + \li \l{Actions and Conditions} \row \li Dynamically changing the behavior of a component \li \l{Adding Bindings Between Properties} diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc index 7111d2db90e..65a63da3f2a 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc @@ -104,6 +104,17 @@ selected, the camera is pointed at the world origin. This does not affect the camera zoom level. + \image studio-3d-editor-axis-helper.webp "Axis helper in the 3D view" + + You can use scene cameras (2) to view the \uicontrol View3D component from a + specific angle in the \l {2D} view while editing scenes. Different types of + cameras are available in \uicontrol Components + > \uicontrol {Qt Quick 3D} > \uicontrol {Qt Quick 3D}. For more information + about using cameras in the scene, the available camera types, and their + properties, see \l{Cameras}. + + \section2 Using Split View + To view the scene in a split view of four different point of views, select \inlineimage icons/split-view.png. @@ -114,15 +125,6 @@ independently. Navigate each split by panning, rotating, and zooming, as described above. - \image studio-3d-editor-axis-helper.webp "Axis helper in the 3D view" - - You can use scene cameras (2) to view the \uicontrol View3D component from a - specific angle in the \l {2D} view while editing scenes. Different types of - cameras are available in \uicontrol Components - > \uicontrol {Qt Quick 3D} > \uicontrol {Qt Quick 3D}. For more information - about using cameras in the scene, the available camera types, and their - properties, see \l{Cameras}. - \section1 Using Global and Local Orientation To switch between local and global orientation, select diff --git a/doc/qtdesignstudio/src/views/qtquick-effect-maker-view.qdoc b/doc/qtdesignstudio/src/views/qtquick-effect-maker-view.qdoc index 3d7e046d853..842e5033ed7 100644 --- a/doc/qtdesignstudio/src/views/qtquick-effect-maker-view.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-effect-maker-view.qdoc @@ -86,4 +86,4 @@ \uicontrol Navigator, and in \uicontrol Properties > \uicontrol {Exposed Custom Properties}, select or clear the \uicontrol timeRunning checkbox. - +*/ From e1a4967b03b22b07827e7a8bddf830e0c3e0c794 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 20 Mar 2024 16:52:19 +0100 Subject: [PATCH 040/202] QmlDesigner: Fix compiler warning Change-Id: Ie0f81c735b5351e8a12a62ced4e86c5083bf24cf Reviewed-by: Tim Jenssen --- .../qmldesigner/designercore/imagecache/imagecachecollector.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h index c2a912cc3ae..fec68c28949 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h @@ -64,8 +64,10 @@ private: QSize captureImageMaximumSize; ExternalDependenciesInterface &m_externalDependencies; ImageCacheCollectorNullImageHandling nullImageHandling{}; +#ifdef QDS_USE_PROJECTSTORAGE ProjectStorageType *m_projectStorage = nullptr; PathCacheType *m_pathCache = nullptr; +#endif }; } // namespace QmlDesigner From 81681897a3948bc1c679ab31b0e282c48e3eda82 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 20 Mar 2024 15:53:47 +0100 Subject: [PATCH 041/202] QmlDesigner: Register ContextObject only once Change-Id: Id92fa1a5ccf556dcc653b0422369df35414719ed Reviewed-by: Marco Bubke --- .../qmldesigner/components/timelineeditor/timelineview.cpp | 7 ++++--- .../components/transitioneditor/transitioneditorview.cpp | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp index 7905df68e91..8288e69316f 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp @@ -644,11 +644,12 @@ void TimelineView::registerActions() TimelineWidget *TimelineView::createWidget() { - if (!m_timelineWidget) + if (!m_timelineWidget) { m_timelineWidget = new TimelineWidget(this); - auto *timelineContext = new TimelineContext(m_timelineWidget); - Core::ICore::addContextObject(timelineContext); + auto *timelineContext = new TimelineContext(m_timelineWidget); + Core::ICore::addContextObject(timelineContext); + } return m_timelineWidget; } diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp index 104127bd49d..9f9e888823e 100644 --- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp @@ -318,11 +318,12 @@ ModelNode TransitionEditorView::addNewTransition() TransitionEditorWidget *TransitionEditorView::createWidget() { - if (!m_transitionEditorWidget) + if (!m_transitionEditorWidget) { m_transitionEditorWidget = new TransitionEditorWidget(this); - auto *transitionContext = new TransitionContext(m_transitionEditorWidget); - Core::ICore::addContextObject(transitionContext); + auto *transitionContext = new TransitionContext(m_transitionEditorWidget); + Core::ICore::addContextObject(transitionContext); + } return m_transitionEditorWidget; } From 2f728e6d59b03c27f04c234a809809dd3bbde76f Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 20 Mar 2024 15:57:54 +0100 Subject: [PATCH 042/202] QmlDesigner: Use 'Qt Design Studio' in option Task-number: QDS-12149 Change-Id: I27ffd717ef73ff788e8b5cfdb9a116162cee8015 Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/documentwarningwidget.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/documentwarningwidget.cpp b/src/plugins/qmldesigner/documentwarningwidget.cpp index 9fb2b87635d..907b79ee7a3 100644 --- a/src/plugins/qmldesigner/documentwarningwidget.cpp +++ b/src/plugins/qmldesigner/documentwarningwidget.cpp @@ -39,8 +39,7 @@ DocumentWarningWidget::DocumentWarningWidget(QWidget *parent) m_messageLabel->setWordWrap(true); m_messageLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); - m_ignoreWarningsCheckBox->setText(tr("Always ignore these warnings about features " - "not supported by Qt Quick Designer.")); + m_ignoreWarningsCheckBox->setText(tr("Turn off warnings about unsupported Qt Design Studio features.")); connect(m_navigateLabel, &QLabel::linkActivated, this, [this](const QString &link) { if (link == QLatin1String("goToCode")) { From 98d1339637d6e1418feecc9f83fa59767e65324d Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 20 Mar 2024 16:13:18 +0100 Subject: [PATCH 043/202] QmlDesigner: Add default root id to templates Task-number: QDS-12248 Change-Id: I105ada4f4b3a2eb5b3f422de7426dfaf8dc0a34a Reviewed-by: Tim Jenssen --- .../qmldesigner/studio_templates/files/qtquickfile/file.qml.tpl | 1 + .../studio_templates/files/qtuiquickfile/file.qml.tpl | 1 + 2 files changed, 2 insertions(+) diff --git a/share/qtcreator/qmldesigner/studio_templates/files/qtquickfile/file.qml.tpl b/share/qtcreator/qmldesigner/studio_templates/files/qtquickfile/file.qml.tpl index f77f7d99c99..996d474208d 100644 --- a/share/qtcreator/qmldesigner/studio_templates/files/qtquickfile/file.qml.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/files/qtquickfile/file.qml.tpl @@ -7,6 +7,7 @@ import %{ApplicationImport} @endif %{RootItem} { + id: root @if %{UseImport} width: Constants.width height: Constants.height diff --git a/share/qtcreator/qmldesigner/studio_templates/files/qtuiquickfile/file.qml.tpl b/share/qtcreator/qmldesigner/studio_templates/files/qtuiquickfile/file.qml.tpl index 47ca3af2995..e7e86e9ffa5 100644 --- a/share/qtcreator/qmldesigner/studio_templates/files/qtuiquickfile/file.qml.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/files/qtuiquickfile/file.qml.tpl @@ -14,6 +14,7 @@ import %{ApplicationImport} @endif %{RootItem} { + id: root @if %{UseImport} width: Constants.width height: Constants.height From 7dc52035cb03fc147eb08f3f25e80327dd677886 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 20 Mar 2024 16:38:21 +0100 Subject: [PATCH 044/202] QmlDesigner: Use Qt Design Studio instead of Qt Quick Designer Change-Id: I47d66a20c95269bea1662b1d4c42509ddffe2473 Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- src/plugins/qmldesigner/documentwarningwidget.cpp | 2 +- src/plugins/qmldesigner/settingspage.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/documentwarningwidget.cpp b/src/plugins/qmldesigner/documentwarningwidget.cpp index 907b79ee7a3..e1b8346a66b 100644 --- a/src/plugins/qmldesigner/documentwarningwidget.cpp +++ b/src/plugins/qmldesigner/documentwarningwidget.cpp @@ -92,7 +92,7 @@ void DocumentWarningWidget::refreshContent() m_ignoreWarningsCheckBox->hide(); m_continueButton->setText(tr("OK")); } else { - m_headerLabel->setText(tr("This QML file contains features which are not supported by Qt Quick Designer at:")); + m_headerLabel->setText(tr("This QML file contains features which are not supported by Qt Design Studio at:")); { QSignalBlocker blocker(m_ignoreWarningsCheckBox); m_ignoreWarningsCheckBox->setChecked(!warningsEnabled()); diff --git a/src/plugins/qmldesigner/settingspage.cpp b/src/plugins/qmldesigner/settingspage.cpp index 344e2700d95..17c75a02853 100644 --- a/src/plugins/qmldesigner/settingspage.cpp +++ b/src/plugins/qmldesigner/settingspage.cpp @@ -135,7 +135,7 @@ SettingsPageWidget::SettingsPageWidget(ExternalDependencies &externalDependencie m_useDefaultPuppetRadioButton = new QRadioButton(tr("Use fallback QML emulation layer")); m_useDefaultPuppetRadioButton->setToolTip( - tr("If you select this radio button, Qt Quick Designer always uses the " + tr("If you select this radio button, Qt Design Studio always uses the " "QML emulation layer (QML Puppet) located at the following path.")); m_useDefaultPuppetRadioButton->setChecked(true); @@ -167,7 +167,7 @@ SettingsPageWidget::SettingsPageWidget(ExternalDependencies &externalDependencie m_designerWarningsCheckBox = new QCheckBox( tr("Warn about unsupported features in .ui.qml files")); m_designerWarningsCheckBox->setToolTip( - tr("Warns about QML features that are not properly supported by the Qt Quick Designer.")); + tr("Warns about QML features that are not properly supported by the Qt Design Studio.")); m_designerWarningsUiQmlfiles = new QCheckBox( tr("Warn about using .qml files instead of .ui.qml files")); From 1dcfbbfa8e340edd383f2ce9afa2b6f4be4962d9 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 20 Mar 2024 15:51:20 +0100 Subject: [PATCH 045/202] QmlDesigner: Improve error message for invalid id * Move the message into single place * Improve the warning Task-number: QDS-12195 Change-Id: I70ca89f39cf04b937336936015b2ccbf7deb98c5 Reviewed-by: Marco Bubke Reviewed-by: Pranta Ghosh Dastider Reviewed-by: Qt CI Patch Build Bot --- src/plugins/qmldesigner/CMakeLists.txt | 1 + .../components/componentcore/dialogutils.cpp | 32 +++++++++++++++++++ .../components/componentcore/dialogutils.h | 17 ++++++++++ .../components/navigator/nameitemdelegate.cpp | 6 ++-- .../timelineeditor/timelineanimationform.cpp | 4 +-- .../timelineeditor/timelineform.cpp | 4 +-- .../transitioneditor/transitionform.cpp | 4 +-- .../designercore/model/modelutils.cpp | 2 ++ 8 files changed, 60 insertions(+), 10 deletions(-) create mode 100644 src/plugins/qmldesigner/components/componentcore/dialogutils.cpp create mode 100644 src/plugins/qmldesigner/components/componentcore/dialogutils.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index ae954872d9d..01f03cc2712 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -634,6 +634,7 @@ extend_qtc_plugin(QmlDesigner svgpasteaction.cpp svgpasteaction.h viewmanager.cpp viewmanager.h utils3d.cpp utils3d.h + dialogutils.cpp dialogutils.h ) extend_qtc_plugin(QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/dialogutils.cpp b/src/plugins/qmldesigner/components/componentcore/dialogutils.cpp new file mode 100644 index 00000000000..f882ae528dc --- /dev/null +++ b/src/plugins/qmldesigner/components/componentcore/dialogutils.cpp @@ -0,0 +1,32 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include + +#include + +namespace QmlDesigner { + +namespace DialogUtils { + +void showWarningForInvalidId(const QString &id) +{ + constexpr char text[] = R"( +The ID '%1' is invalid. + +Make sure the ID is: +
    +
  • Unique within the QML file.
  • +
  • Beginning with a lowercase letter.
  • +
  • Without any blank space or symbol.
  • +
  • Not a reserved QML keyword.
  • +
+)"; + + Core::AsynchronousMessageBox::warning(Model::tr("Invalid Id"), + Model::tr(text).arg(id)); +} + +} // namespace DialogUtils + +} //QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/dialogutils.h b/src/plugins/qmldesigner/components/componentcore/dialogutils.h new file mode 100644 index 00000000000..3ca98016dd9 --- /dev/null +++ b/src/plugins/qmldesigner/components/componentcore/dialogutils.h @@ -0,0 +1,17 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include + +#include + +namespace QmlDesigner { + +namespace DialogUtils { + +QMLDESIGNERCOMPONENTS_EXPORT void showWarningForInvalidId(const QString &id); + +} // namespace DialogUtils +} //QmlDesigner diff --git a/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp b/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp index aacaf6dc0d9..223e04fd967 100644 --- a/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp +++ b/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp @@ -9,9 +9,8 @@ #include "navigatortreeview.h" #include "navigatorwidget.h" #include "choosefrompropertylistdialog.h" -#include "qproxystyle.h" - #include +#include #include #include #include @@ -169,8 +168,7 @@ static void setId(const QModelIndex &index, const QString &newId) return; if (!ModelNode::isValidId(newId)) { - Core::AsynchronousMessageBox::warning(NavigatorTreeView::tr("Invalid Id"), - NavigatorTreeView::tr("%1 is an invalid id.").arg(newId)); + DialogUtils::showWarningForInvalidId(newId); } else if (modelNode.view()->hasId(newId)) { Core::AsynchronousMessageBox::warning(NavigatorTreeView::tr("Invalid Id"), NavigatorTreeView::tr("%1 already exists.").arg(newId)); diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.cpp index 83551378eea..e65c05c39fe 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include @@ -141,8 +142,7 @@ TimelineAnimationForm::TimelineAnimationForm(QWidget *parent) bool error = false; if (!ModelNode::isValidId(newId)) { - Core::AsynchronousMessageBox::warning(tr("Invalid Id"), - tr("%1 is an invalid id.").arg(newId)); + DialogUtils::showWarningForInvalidId(newId); error = true; } else if (animation().view()->hasId(newId)) { Core::AsynchronousMessageBox::warning(tr("Invalid Id"), diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineform.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelineform.cpp index b3d408dc0d0..08915b65771 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelineform.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelineform.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -125,8 +126,7 @@ TimelineForm::TimelineForm(QWidget *parent) bool error = false; if (!ModelNode::isValidId(newId)) { - Core::AsynchronousMessageBox::warning(tr("Invalid Id"), - tr("%1 is an invalid id.").arg(newId)); + DialogUtils::showWarningForInvalidId(newId); error = true; } else if (m_timeline.view()->hasId(newId)) { Core::AsynchronousMessageBox::warning(tr("Invalid Id"), diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitionform.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitionform.cpp index 1770ba63fc5..dcbb0b23b39 100644 --- a/src/plugins/qmldesigner/components/transitioneditor/transitionform.cpp +++ b/src/plugins/qmldesigner/components/transitioneditor/transitionform.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include @@ -45,8 +46,7 @@ TransitionForm::TransitionForm(QWidget *parent) bool error = false; if (!ModelNode::isValidId(newId)) { - Core::AsynchronousMessageBox::warning(tr("Invalid ID"), - tr("%1 is an invalid ID.").arg(newId)); + DialogUtils::showWarningForInvalidId(newId); error = true; } else if (m_transition.view()->hasId(newId)) { Core::AsynchronousMessageBox::warning(tr("Invalid ID"), diff --git a/src/plugins/qmldesigner/designercore/model/modelutils.cpp b/src/plugins/qmldesigner/designercore/model/modelutils.cpp index cb3f482289f..3dd7fe6c32a 100644 --- a/src/plugins/qmldesigner/designercore/model/modelutils.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelutils.cpp @@ -9,6 +9,8 @@ #include #include +#include + #include #include From 9d451d54c83d578043c926f17faf8ca0bee10a53 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Thu, 21 Mar 2024 13:01:15 +0200 Subject: [PATCH 046/202] QmlDesigner: Add and update the fly camera speed config icon Change-Id: I3d956b04fb7f2e44a5d31c1561a2a341d27cc32b Reviewed-by: Miikka Heikkinen --- .../qtcreator/qmldesigner/designericons.json | 2 +- .../CameraSpeedConfigurationDialog.qml | 2 +- .../imports/StudioTheme/InternalConstants.qml | 607 +++++++++--------- .../imports/StudioTheme/icons.ttf | Bin 66460 -> 66776 bytes .../components/componentcore/theme.h | 1 + 5 files changed, 307 insertions(+), 305 deletions(-) diff --git a/share/qtcreator/qmldesigner/designericons.json b/share/qtcreator/qmldesigner/designericons.json index 157012b312a..005494b4715 100644 --- a/share/qtcreator/qmldesigner/designericons.json +++ b/share/qtcreator/qmldesigner/designericons.json @@ -219,7 +219,7 @@ } }, "CameraSpeedConfigIcon": { - "iconName": "camera_medium" + "iconName": "cameraSpeed_medium" }, "EditColorIcon": { "iconName": "colorSelection_medium" diff --git a/share/qtcreator/qmldesigner/edit3dQmlSource/CameraSpeedConfigurationDialog.qml b/share/qtcreator/qmldesigner/edit3dQmlSource/CameraSpeedConfigurationDialog.qml index 2e2ff547002..b51369ffa3f 100644 --- a/share/qtcreator/qmldesigner/edit3dQmlSource/CameraSpeedConfigurationDialog.qml +++ b/share/qtcreator/qmldesigner/edit3dQmlSource/CameraSpeedConfigurationDialog.qml @@ -57,7 +57,7 @@ Rectangle { HelperWidgets.IconIndicator { anchors.fill: parent - icon: StudioTheme.Constants.camera_medium + icon: StudioTheme.Constants.cameraSpeed_medium pixelSize: StudioTheme.Values.myIconFontSize * 1.4 iconColor: StudioTheme.Values.themeLinkIndicatorColorHover enabled: false diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml index ee2df02e04a..3c74243f684 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml @@ -75,309 +75,310 @@ QtObject { readonly property string binding_medium: "\u005C" readonly property string bounds_small: "\u005D" readonly property string branch_medium: "\u005E" - readonly property string camera_medium: "\u005F" - readonly property string camera_small: "\u0060" - readonly property string centerHorizontal: "\u0061" - readonly property string centerVertical: "\u0062" - readonly property string cleanLogs_medium: "\u0063" - readonly property string clearList_large: "\u0064" - readonly property string clearList_medium: "\u0065" - readonly property string closeCross: "\u0066" - readonly property string closeFile_large: "\u0067" - readonly property string closeLink: "\u0068" - readonly property string close_small: "\u0069" - readonly property string code: "\u006A" - readonly property string codeEditor_medium: "\u006B" - readonly property string codeview_medium: "\u006C" - readonly property string colorPopupClose: "\u006D" - readonly property string colorSelection_medium: "\u006E" - readonly property string columnsAndRows: "\u006F" - readonly property string comboBox_medium: "\u0070" - readonly property string cone_medium: "\u0071" - readonly property string cone_small: "\u0072" - readonly property string connection_small: "\u0073" - readonly property string connections_medium: "\u0074" - readonly property string copyLink: "\u0075" - readonly property string copyStyle: "\u0076" - readonly property string copy_small: "\u0077" - readonly property string cornerA: "\u0078" - readonly property string cornerB: "\u0079" - readonly property string cornersAll: "\u007A" - readonly property string createComponent_large: "\u007B" - readonly property string createComponent_small: "\u007C" - readonly property string createObject_medium: "\u007D" - readonly property string create_medium: "\u007E" - readonly property string create_small: "\u007F" - readonly property string cube_medium: "\u0080" - readonly property string cube_small: "\u0081" - readonly property string curveDesigner: "\u0082" - readonly property string curveDesigner_medium: "\u0083" - readonly property string curveEditor: "\u0084" - readonly property string customMaterialEditor: "\u0085" - readonly property string cylinder_medium: "\u0086" - readonly property string cylinder_small: "\u0087" - readonly property string decisionNode: "\u0088" - readonly property string deleteColumn: "\u0089" - readonly property string deleteMaterial: "\u008A" - readonly property string deleteRow: "\u008B" - readonly property string deleteTable: "\u008C" - readonly property string delete_medium: "\u008D" - readonly property string delete_small: "\u008E" - readonly property string deletecolumn_medium: "\u008F" - readonly property string deletepermanently_medium: "\u0090" - readonly property string deleterow_medium: "\u0091" - readonly property string designMode_large: "\u0092" - readonly property string detach: "\u0093" - readonly property string directionalLight_small: "\u0094" - readonly property string distributeBottom: "\u0095" - readonly property string distributeCenterHorizontal: "\u0096" - readonly property string distributeCenterVertical: "\u0097" - readonly property string distributeLeft: "\u0098" - readonly property string distributeOriginBottomRight: "\u0099" - readonly property string distributeOriginCenter: "\u009A" - readonly property string distributeOriginNone: "\u009B" - readonly property string distributeOriginTopLeft: "\u009D" - readonly property string distributeRight: "\u009E" - readonly property string distributeSpacingHorizontal: "\u009F" - readonly property string distributeSpacingVertical: "\u00A0" - readonly property string distributeTop: "\u00A1" - readonly property string download: "\u00A2" - readonly property string downloadUnavailable: "\u00A3" - readonly property string downloadUpdate: "\u00A4" - readonly property string downloaded: "\u00A5" - readonly property string dragmarks: "\u00A6" - readonly property string duplicate_small: "\u00A7" - readonly property string edit: "\u00A8" - readonly property string editComponent_large: "\u00A9" - readonly property string editComponent_small: "\u00AA" - readonly property string editLightOff_medium: "\u00AB" - readonly property string editLightOn_medium: "\u00AC" - readonly property string edit_medium: "\u00AE" - readonly property string edit_small: "\u00AF" - readonly property string effects: "\u00B0" - readonly property string events_small: "\u00B1" - readonly property string export_medium: "\u00B2" - readonly property string eyeDropper: "\u00B3" - readonly property string favorite: "\u00B4" - readonly property string fitAll_medium: "\u00B5" - readonly property string fitSelected_small: "\u00B6" - readonly property string fitSelection_medium: "\u00B7" - readonly property string fitToView_medium: "\u00B8" - readonly property string flowAction: "\u00B9" - readonly property string flowTransition: "\u00BA" - readonly property string fontStyleBold: "\u00BB" - readonly property string fontStyleItalic: "\u00BC" - readonly property string fontStyleStrikethrough: "\u00BD" - readonly property string fontStyleUnderline: "\u00BE" - readonly property string forward_medium: "\u00BF" - readonly property string globalOrient_medium: "\u00C0" - readonly property string gradient: "\u00C1" - readonly property string gridView: "\u00C2" - readonly property string grid_medium: "\u00C3" - readonly property string group_small: "\u00C4" - readonly property string help: "\u00C5" - readonly property string home_large: "\u00C6" - readonly property string idAliasOff: "\u00C7" - readonly property string idAliasOn: "\u00C8" - readonly property string import_medium: "\u00C9" - readonly property string imported: "\u00CA" - readonly property string importedModels_small: "\u00CB" - readonly property string infinity: "\u00CC" - readonly property string invisible_medium: "\u00CD" - readonly property string invisible_small: "\u00CE" - readonly property string jumpToCode_medium: "\u00CF" - readonly property string jumpToCode_small: "\u00D0" - readonly property string keyframe: "\u00D1" - readonly property string languageList_medium: "\u00D2" - readonly property string layouts_small: "\u00D3" - readonly property string lights_small: "\u00D4" - readonly property string linear_medium: "\u00D5" - readonly property string linkTriangle: "\u00D6" - readonly property string linked: "\u00D7" - readonly property string listView: "\u00D8" - readonly property string listView_medium: "\u00D9" - readonly property string list_medium: "\u00DA" - readonly property string localOrient_medium: "\u00DB" - readonly property string lockOff: "\u00DC" - readonly property string lockOn: "\u00DD" - readonly property string loopPlayback_medium: "\u00DE" - readonly property string materialBrowser_medium: "\u00DF" - readonly property string materialPreviewEnvironment: "\u00E0" - readonly property string materialPreviewModel: "\u00E1" - readonly property string material_medium: "\u00E2" - readonly property string maxBar_small: "\u00E3" - readonly property string mergeCells: "\u00E4" - readonly property string merge_small: "\u00E5" - readonly property string minus: "\u00E6" - readonly property string mirror: "\u00E7" - readonly property string more_medium: "\u00E8" - readonly property string mouseArea_small: "\u00E9" - readonly property string moveDown_medium: "\u00EA" - readonly property string moveInwards_medium: "\u00EB" - readonly property string moveUp_medium: "\u00EC" - readonly property string moveUpwards_medium: "\u00ED" - readonly property string move_medium: "\u00EE" - readonly property string newMaterial: "\u00EF" - readonly property string nextFile_large: "\u00F0" - readonly property string normalBar_small: "\u00F1" - readonly property string openLink: "\u00F2" - readonly property string openMaterialBrowser: "\u00F3" - readonly property string orientation: "\u00F4" - readonly property string orthCam_medium: "\u00F5" - readonly property string orthCam_small: "\u00F6" - readonly property string paddingEdge: "\u00F7" - readonly property string paddingFrame: "\u00F8" - readonly property string particleAnimation_medium: "\u00F9" - readonly property string pasteStyle: "\u00FA" - readonly property string paste_small: "\u00FB" - readonly property string pause: "\u00FC" - readonly property string pause_medium: "\u00FD" - readonly property string perspectiveCam_medium: "\u00FE" - readonly property string perspectiveCam_small: "\u00FF" - readonly property string pin: "\u0100" - readonly property string plane_medium: "\u0101" - readonly property string plane_small: "\u0102" - readonly property string play: "\u0103" - readonly property string playFill_medium: "\u0104" - readonly property string playOutline_medium: "\u0105" - readonly property string plus: "\u0106" - readonly property string pointLight_small: "\u0107" - readonly property string positioners_small: "\u0108" - readonly property string previewEnv_medium: "\u0109" - readonly property string previousFile_large: "\u010A" - readonly property string promote: "\u010B" - readonly property string properties_medium: "\u010C" - readonly property string readOnly: "\u010D" - readonly property string recent_medium: "\u010E" - readonly property string recordFill_medium: "\u010F" - readonly property string recordOutline_medium: "\u0110" - readonly property string redo: "\u0111" - readonly property string reload_medium: "\u0112" - readonly property string remove_medium: "\u0113" - readonly property string remove_small: "\u0114" - readonly property string rename_small: "\u0115" - readonly property string replace_small: "\u0116" - readonly property string resetView_small: "\u0117" - readonly property string restartParticles_medium: "\u0118" - readonly property string reverseOrder_medium: "\u0119" - readonly property string roatate_medium: "\u011A" - readonly property string rotationFill: "\u011B" - readonly property string rotationOutline: "\u011C" - readonly property string runProjFill_large: "\u011D" - readonly property string runProjOutline_large: "\u011E" - readonly property string s_anchors: "\u011F" - readonly property string s_annotations: "\u0120" - readonly property string s_arrange: "\u0121" - readonly property string s_boundingBox: "\u0122" - readonly property string s_component: "\u0123" - readonly property string s_connections: "\u0124" - readonly property string s_edit: "\u0125" - readonly property string s_enterComponent: "\u0126" - readonly property string s_eventList: "\u0127" - readonly property string s_group: "\u0128" - readonly property string s_layouts: "\u0129" - readonly property string s_merging: "\u012A" - readonly property string s_mouseArea: "\u012B" - readonly property string s_positioners: "\u012C" - readonly property string s_selection: "\u012D" - readonly property string s_snapping: "\u012E" - readonly property string s_timeline: "\u012F" - readonly property string s_visibility: "\u0130" - readonly property string saveAs_medium: "\u0131" - readonly property string saveLogs_medium: "\u0132" - readonly property string save_medium: "\u0133" - readonly property string scale_medium: "\u0134" - readonly property string search: "\u0135" - readonly property string search_small: "\u0136" - readonly property string sectionToggle: "\u0137" - readonly property string selectFill_medium: "\u0138" - readonly property string selectOutline_medium: "\u0139" - readonly property string selectParent_small: "\u013A" - readonly property string selection_small: "\u013B" - readonly property string settings_medium: "\u013C" - readonly property string signal_small: "\u013D" - readonly property string snapping_conf_medium: "\u013E" - readonly property string snapping_medium: "\u013F" - readonly property string snapping_small: "\u0140" - readonly property string sortascending_medium: "\u0141" - readonly property string sortdescending_medium: "\u0142" - readonly property string sphere_medium: "\u0143" - readonly property string sphere_small: "\u0144" - readonly property string splitColumns: "\u0145" - readonly property string splitRows: "\u0146" - readonly property string splitScreen_medium: "\u0147" - readonly property string spotLight_small: "\u0148" - readonly property string stackedContainer_small: "\u0149" - readonly property string startNode: "\u014A" - readonly property string step_medium: "\u014B" - readonly property string stop_medium: "\u014C" - readonly property string tableView_medium: "\u014D" - readonly property string testIcon: "\u014E" - readonly property string textAlignBottom: "\u014F" - readonly property string textAlignCenter: "\u0150" - readonly property string textAlignJustified: "\u0151" - readonly property string textAlignLeft: "\u0152" - readonly property string textAlignMiddle: "\u0153" - readonly property string textAlignRight: "\u0154" - readonly property string textAlignTop: "\u0155" - readonly property string textBulletList: "\u0156" - readonly property string textFullJustification: "\u0157" - readonly property string textNumberedList: "\u0158" - readonly property string textures_medium: "\u0159" - readonly property string tickIcon: "\u015A" - readonly property string tickMark_small: "\u015B" - readonly property string timeline_small: "\u015C" - readonly property string toEndFrame_medium: "\u015D" - readonly property string toNextFrame_medium: "\u015E" - readonly property string toPrevFrame_medium: "\u015F" - readonly property string toStartFrame_medium: "\u0160" - readonly property string topToolbar_annotations: "\u0161" - readonly property string topToolbar_closeFile: "\u0162" - readonly property string topToolbar_designMode: "\u0163" - readonly property string topToolbar_enterComponent: "\u0164" - readonly property string topToolbar_home: "\u0165" - readonly property string topToolbar_makeComponent: "\u0166" - readonly property string topToolbar_navFile: "\u0167" - readonly property string topToolbar_runProject: "\u0168" - readonly property string translationCreateFiles: "\u0169" - readonly property string translationCreateReport: "\u016A" - readonly property string translationExport: "\u016B" - readonly property string translationImport: "\u016C" - readonly property string translationSelectLanguages: "\u016D" - readonly property string translationTest: "\u016E" - readonly property string transparent: "\u016F" - readonly property string triState: "\u0170" - readonly property string triangleArcA: "\u0171" - readonly property string triangleArcB: "\u0172" - readonly property string triangleCornerA: "\u0173" - readonly property string triangleCornerB: "\u0174" - readonly property string unLinked: "\u0175" - readonly property string undo: "\u0176" - readonly property string unify_medium: "\u0177" - readonly property string unpin: "\u0178" - readonly property string upDownIcon: "\u0179" - readonly property string upDownSquare2: "\u017A" - readonly property string updateAvailable_medium: "\u017B" - readonly property string updateContent_medium: "\u017C" - readonly property string visibilityOff: "\u017D" - readonly property string visibilityOn: "\u017E" - readonly property string visible_medium: "\u017F" - readonly property string visible_small: "\u0180" - readonly property string warning_medium: "\u0181" - readonly property string wildcard: "\u0182" - readonly property string wizardsAutomotive: "\u0183" - readonly property string wizardsDesktop: "\u0184" - readonly property string wizardsGeneric: "\u0185" - readonly property string wizardsMcuEmpty: "\u0186" - readonly property string wizardsMcuGraph: "\u0187" - readonly property string wizardsMobile: "\u0188" - readonly property string wizardsUnknown: "\u0189" - readonly property string zoomAll: "\u018A" - readonly property string zoomIn: "\u018B" - readonly property string zoomIn_medium: "\u018C" - readonly property string zoomOut: "\u018D" - readonly property string zoomOut_medium: "\u018E" - readonly property string zoomSelection: "\u018F" + readonly property string cameraSpeed_medium: "\u005F" + readonly property string camera_medium: "\u0060" + readonly property string camera_small: "\u0061" + readonly property string centerHorizontal: "\u0062" + readonly property string centerVertical: "\u0063" + readonly property string cleanLogs_medium: "\u0064" + readonly property string clearList_large: "\u0065" + readonly property string clearList_medium: "\u0066" + readonly property string closeCross: "\u0067" + readonly property string closeFile_large: "\u0068" + readonly property string closeLink: "\u0069" + readonly property string close_small: "\u006A" + readonly property string code: "\u006B" + readonly property string codeEditor_medium: "\u006C" + readonly property string codeview_medium: "\u006D" + readonly property string colorPopupClose: "\u006E" + readonly property string colorSelection_medium: "\u006F" + readonly property string columnsAndRows: "\u0070" + readonly property string comboBox_medium: "\u0071" + readonly property string cone_medium: "\u0072" + readonly property string cone_small: "\u0073" + readonly property string connection_small: "\u0074" + readonly property string connections_medium: "\u0075" + readonly property string copyLink: "\u0076" + readonly property string copyStyle: "\u0077" + readonly property string copy_small: "\u0078" + readonly property string cornerA: "\u0079" + readonly property string cornerB: "\u007A" + readonly property string cornersAll: "\u007B" + readonly property string createComponent_large: "\u007C" + readonly property string createComponent_small: "\u007D" + readonly property string createObject_medium: "\u007E" + readonly property string create_medium: "\u007F" + readonly property string create_small: "\u0080" + readonly property string cube_medium: "\u0081" + readonly property string cube_small: "\u0082" + readonly property string curveDesigner: "\u0083" + readonly property string curveDesigner_medium: "\u0084" + readonly property string curveEditor: "\u0085" + readonly property string customMaterialEditor: "\u0086" + readonly property string cylinder_medium: "\u0087" + readonly property string cylinder_small: "\u0088" + readonly property string decisionNode: "\u0089" + readonly property string deleteColumn: "\u008A" + readonly property string deleteMaterial: "\u008B" + readonly property string deleteRow: "\u008C" + readonly property string deleteTable: "\u008D" + readonly property string delete_medium: "\u008E" + readonly property string delete_small: "\u008F" + readonly property string deletecolumn_medium: "\u0090" + readonly property string deletepermanently_medium: "\u0091" + readonly property string deleterow_medium: "\u0092" + readonly property string designMode_large: "\u0093" + readonly property string detach: "\u0094" + readonly property string directionalLight_small: "\u0095" + readonly property string distributeBottom: "\u0096" + readonly property string distributeCenterHorizontal: "\u0097" + readonly property string distributeCenterVertical: "\u0098" + readonly property string distributeLeft: "\u0099" + readonly property string distributeOriginBottomRight: "\u009A" + readonly property string distributeOriginCenter: "\u009B" + readonly property string distributeOriginNone: "\u009D" + readonly property string distributeOriginTopLeft: "\u009E" + readonly property string distributeRight: "\u009F" + readonly property string distributeSpacingHorizontal: "\u00A0" + readonly property string distributeSpacingVertical: "\u00A1" + readonly property string distributeTop: "\u00A2" + readonly property string download: "\u00A3" + readonly property string downloadUnavailable: "\u00A4" + readonly property string downloadUpdate: "\u00A5" + readonly property string downloaded: "\u00A6" + readonly property string dragmarks: "\u00A7" + readonly property string duplicate_small: "\u00A8" + readonly property string edit: "\u00A9" + readonly property string editComponent_large: "\u00AA" + readonly property string editComponent_small: "\u00AB" + readonly property string editLightOff_medium: "\u00AC" + readonly property string editLightOn_medium: "\u00AE" + readonly property string edit_medium: "\u00AF" + readonly property string edit_small: "\u00B0" + readonly property string effects: "\u00B1" + readonly property string events_small: "\u00B2" + readonly property string export_medium: "\u00B3" + readonly property string eyeDropper: "\u00B4" + readonly property string favorite: "\u00B5" + readonly property string fitAll_medium: "\u00B6" + readonly property string fitSelected_small: "\u00B7" + readonly property string fitSelection_medium: "\u00B8" + readonly property string fitToView_medium: "\u00B9" + readonly property string flowAction: "\u00BA" + readonly property string flowTransition: "\u00BB" + readonly property string fontStyleBold: "\u00BC" + readonly property string fontStyleItalic: "\u00BD" + readonly property string fontStyleStrikethrough: "\u00BE" + readonly property string fontStyleUnderline: "\u00BF" + readonly property string forward_medium: "\u00C0" + readonly property string globalOrient_medium: "\u00C1" + readonly property string gradient: "\u00C2" + readonly property string gridView: "\u00C3" + readonly property string grid_medium: "\u00C4" + readonly property string group_small: "\u00C5" + readonly property string help: "\u00C6" + readonly property string home_large: "\u00C7" + readonly property string idAliasOff: "\u00C8" + readonly property string idAliasOn: "\u00C9" + readonly property string import_medium: "\u00CA" + readonly property string imported: "\u00CB" + readonly property string importedModels_small: "\u00CC" + readonly property string infinity: "\u00CD" + readonly property string invisible_medium: "\u00CE" + readonly property string invisible_small: "\u00CF" + readonly property string jumpToCode_medium: "\u00D0" + readonly property string jumpToCode_small: "\u00D1" + readonly property string keyframe: "\u00D2" + readonly property string languageList_medium: "\u00D3" + readonly property string layouts_small: "\u00D4" + readonly property string lights_small: "\u00D5" + readonly property string linear_medium: "\u00D6" + readonly property string linkTriangle: "\u00D7" + readonly property string linked: "\u00D8" + readonly property string listView: "\u00D9" + readonly property string listView_medium: "\u00DA" + readonly property string list_medium: "\u00DB" + readonly property string localOrient_medium: "\u00DC" + readonly property string lockOff: "\u00DD" + readonly property string lockOn: "\u00DE" + readonly property string loopPlayback_medium: "\u00DF" + readonly property string materialBrowser_medium: "\u00E0" + readonly property string materialPreviewEnvironment: "\u00E1" + readonly property string materialPreviewModel: "\u00E2" + readonly property string material_medium: "\u00E3" + readonly property string maxBar_small: "\u00E4" + readonly property string mergeCells: "\u00E5" + readonly property string merge_small: "\u00E6" + readonly property string minus: "\u00E7" + readonly property string mirror: "\u00E8" + readonly property string more_medium: "\u00E9" + readonly property string mouseArea_small: "\u00EA" + readonly property string moveDown_medium: "\u00EB" + readonly property string moveInwards_medium: "\u00EC" + readonly property string moveUp_medium: "\u00ED" + readonly property string moveUpwards_medium: "\u00EE" + readonly property string move_medium: "\u00EF" + readonly property string newMaterial: "\u00F0" + readonly property string nextFile_large: "\u00F1" + readonly property string normalBar_small: "\u00F2" + readonly property string openLink: "\u00F3" + readonly property string openMaterialBrowser: "\u00F4" + readonly property string orientation: "\u00F5" + readonly property string orthCam_medium: "\u00F6" + readonly property string orthCam_small: "\u00F7" + readonly property string paddingEdge: "\u00F8" + readonly property string paddingFrame: "\u00F9" + readonly property string particleAnimation_medium: "\u00FA" + readonly property string pasteStyle: "\u00FB" + readonly property string paste_small: "\u00FC" + readonly property string pause: "\u00FD" + readonly property string pause_medium: "\u00FE" + readonly property string perspectiveCam_medium: "\u00FF" + readonly property string perspectiveCam_small: "\u0100" + readonly property string pin: "\u0101" + readonly property string plane_medium: "\u0102" + readonly property string plane_small: "\u0103" + readonly property string play: "\u0104" + readonly property string playFill_medium: "\u0105" + readonly property string playOutline_medium: "\u0106" + readonly property string plus: "\u0107" + readonly property string pointLight_small: "\u0108" + readonly property string positioners_small: "\u0109" + readonly property string previewEnv_medium: "\u010A" + readonly property string previousFile_large: "\u010B" + readonly property string promote: "\u010C" + readonly property string properties_medium: "\u010D" + readonly property string readOnly: "\u010E" + readonly property string recent_medium: "\u010F" + readonly property string recordFill_medium: "\u0110" + readonly property string recordOutline_medium: "\u0111" + readonly property string redo: "\u0112" + readonly property string reload_medium: "\u0113" + readonly property string remove_medium: "\u0114" + readonly property string remove_small: "\u0115" + readonly property string rename_small: "\u0116" + readonly property string replace_small: "\u0117" + readonly property string resetView_small: "\u0118" + readonly property string restartParticles_medium: "\u0119" + readonly property string reverseOrder_medium: "\u011A" + readonly property string roatate_medium: "\u011B" + readonly property string rotationFill: "\u011C" + readonly property string rotationOutline: "\u011D" + readonly property string runProjFill_large: "\u011E" + readonly property string runProjOutline_large: "\u011F" + readonly property string s_anchors: "\u0120" + readonly property string s_annotations: "\u0121" + readonly property string s_arrange: "\u0122" + readonly property string s_boundingBox: "\u0123" + readonly property string s_component: "\u0124" + readonly property string s_connections: "\u0125" + readonly property string s_edit: "\u0126" + readonly property string s_enterComponent: "\u0127" + readonly property string s_eventList: "\u0128" + readonly property string s_group: "\u0129" + readonly property string s_layouts: "\u012A" + readonly property string s_merging: "\u012B" + readonly property string s_mouseArea: "\u012C" + readonly property string s_positioners: "\u012D" + readonly property string s_selection: "\u012E" + readonly property string s_snapping: "\u012F" + readonly property string s_timeline: "\u0130" + readonly property string s_visibility: "\u0131" + readonly property string saveAs_medium: "\u0132" + readonly property string saveLogs_medium: "\u0133" + readonly property string save_medium: "\u0134" + readonly property string scale_medium: "\u0135" + readonly property string search: "\u0136" + readonly property string search_small: "\u0137" + readonly property string sectionToggle: "\u0138" + readonly property string selectFill_medium: "\u0139" + readonly property string selectOutline_medium: "\u013A" + readonly property string selectParent_small: "\u013B" + readonly property string selection_small: "\u013C" + readonly property string settings_medium: "\u013D" + readonly property string signal_small: "\u013E" + readonly property string snapping_conf_medium: "\u013F" + readonly property string snapping_medium: "\u0140" + readonly property string snapping_small: "\u0141" + readonly property string sortascending_medium: "\u0142" + readonly property string sortdescending_medium: "\u0143" + readonly property string sphere_medium: "\u0144" + readonly property string sphere_small: "\u0145" + readonly property string splitColumns: "\u0146" + readonly property string splitRows: "\u0147" + readonly property string splitScreen_medium: "\u0148" + readonly property string spotLight_small: "\u0149" + readonly property string stackedContainer_small: "\u014A" + readonly property string startNode: "\u014B" + readonly property string step_medium: "\u014C" + readonly property string stop_medium: "\u014D" + readonly property string tableView_medium: "\u014E" + readonly property string testIcon: "\u014F" + readonly property string textAlignBottom: "\u0150" + readonly property string textAlignCenter: "\u0151" + readonly property string textAlignJustified: "\u0152" + readonly property string textAlignLeft: "\u0153" + readonly property string textAlignMiddle: "\u0154" + readonly property string textAlignRight: "\u0155" + readonly property string textAlignTop: "\u0156" + readonly property string textBulletList: "\u0157" + readonly property string textFullJustification: "\u0158" + readonly property string textNumberedList: "\u0159" + readonly property string textures_medium: "\u015A" + readonly property string tickIcon: "\u015B" + readonly property string tickMark_small: "\u015C" + readonly property string timeline_small: "\u015D" + readonly property string toEndFrame_medium: "\u015E" + readonly property string toNextFrame_medium: "\u015F" + readonly property string toPrevFrame_medium: "\u0160" + readonly property string toStartFrame_medium: "\u0161" + readonly property string topToolbar_annotations: "\u0162" + readonly property string topToolbar_closeFile: "\u0163" + readonly property string topToolbar_designMode: "\u0164" + readonly property string topToolbar_enterComponent: "\u0165" + readonly property string topToolbar_home: "\u0166" + readonly property string topToolbar_makeComponent: "\u0167" + readonly property string topToolbar_navFile: "\u0168" + readonly property string topToolbar_runProject: "\u0169" + readonly property string translationCreateFiles: "\u016A" + readonly property string translationCreateReport: "\u016B" + readonly property string translationExport: "\u016C" + readonly property string translationImport: "\u016D" + readonly property string translationSelectLanguages: "\u016E" + readonly property string translationTest: "\u016F" + readonly property string transparent: "\u0170" + readonly property string triState: "\u0171" + readonly property string triangleArcA: "\u0172" + readonly property string triangleArcB: "\u0173" + readonly property string triangleCornerA: "\u0174" + readonly property string triangleCornerB: "\u0175" + readonly property string unLinked: "\u0176" + readonly property string undo: "\u0177" + readonly property string unify_medium: "\u0178" + readonly property string unpin: "\u0179" + readonly property string upDownIcon: "\u017A" + readonly property string upDownSquare2: "\u017B" + readonly property string updateAvailable_medium: "\u017C" + readonly property string updateContent_medium: "\u017D" + readonly property string visibilityOff: "\u017E" + readonly property string visibilityOn: "\u017F" + readonly property string visible_medium: "\u0180" + readonly property string visible_small: "\u0181" + readonly property string warning_medium: "\u0182" + readonly property string wildcard: "\u0183" + readonly property string wizardsAutomotive: "\u0184" + readonly property string wizardsDesktop: "\u0185" + readonly property string wizardsGeneric: "\u0186" + readonly property string wizardsMcuEmpty: "\u0187" + readonly property string wizardsMcuGraph: "\u0188" + readonly property string wizardsMobile: "\u0189" + readonly property string wizardsUnknown: "\u018A" + readonly property string zoomAll: "\u018B" + readonly property string zoomIn: "\u018C" + readonly property string zoomIn_medium: "\u018D" + readonly property string zoomOut: "\u018E" + readonly property string zoomOut_medium: "\u018F" + readonly property string zoomSelection: "\u0190" readonly property font iconFont: Qt.font({ "family": controlIcons.name, diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf index 714cd4b7535ce94734ee47cee555def01ecd4ad7..cdce3eeefd96cd16836d674b6d9d2dd9320946cc 100644 GIT binary patch delta 1298 zcmbQ!&T^xZrJjM2fq{XSp@D&!A;8To#CMVB>OKZWmOTs%3^M+~`bLop(rg$Q7$X=M z7!s0m6AQG$b)*;=Se7s_Foh+Tl_)SMGF37#u>E0RU|>m4EH3!}pMjZyk!1@50|Q5T zPGy>Sq9_vs1LqS4hFcODsfj6CXOAQ>FfiOMP3WC) zpNN7;m`IPvA(4Ng0iyfFRK(oGUWhY@`-wM+Zxa6^VI#3g;+3SGWRYZt^XaX(~c0YASXr zVJbN)lT?nV+)-sx6{}Y@QuR|!Qr)4(pys32qjpW*OubJ1kcNoH42^G^PMTes_q3d} zdbDn7ebctmo}$B`Gf8KME|;#AZh-DA-4nVW^t|+z>D|$9(cfm^X0XKIiJ^*NfnlGK zfKigs9%BjPAmd5Kt4y*?Hkh)Q2AO7FN0E@z%(zQTgXqJEvFf@O)7l$DEB zkJUSCHfsfIFY6-f1=gpmf7qzmOtZOU^UK!4w$JvLot51@dnS86dnNmA_RkzV9AX@H zI9fUGabj_@aw>6Za+>3`&*_bGp7SObJ{Jp@ESD9o2CfraSGnGD6LRx$OLD7nyW(!* zp5%VPL%}1?qt9c9$1P8Rde0`$ZC*NF$Gj!HBfJ;*u=!;9%<*~R>)@N=yT*^jFU)V2 z-wpp5{|)}10@4Cz1zZYv5Xchf6xbE`Ea*^hMu)ESCU22mZW>hKFKM`b;&c5w8sNJWZcRO$!yE~mz9%sA=@H*QudV`vz&sQJ-IxaA8-mO)&Kwh z--7uCvjT%WLkM8;{$Gv3t_t&&(T%)`hlWG5soB*e=nWGBSSE6h}A zqVeZ~rm@OiH61x)V^%gc)`kBTaIo=lG3!fn@w0OLTeJWqXe`9b2r`RL$XHHCZLiAa z_u5N$FdH!#PM-e6hSSJU*I3ucz{q5B?9+hBI!_le7H&TG^Z^qSQ~u_4FH%@|#F7(p qQ;QOV3sO^4HnYDi5mRAcU|{_JpMeQPgL3{oHmy7mjVK8YOaK6d2#RI^ delta 1022 zcmcc7$ug&%rJjM2fq{XSp@D&!A;8To#CMT-;9~|x<^~1^1{wcgeWS<)Y1Rx3j1deB z3<=4(i3M8WI+6?wEDIPIn8K3FN)#9rnJO6=*nTiDFtDU278m^g&%n&U$Xvm|z`&87 zQ<=u0?v};C!1;iI;l`hg)Wj66-C0{17#Qv_Fff>9WTe(7a{c_@%D})V!oa|wl95|d zp-?4oi-Cc02Ll7si=6!AM4L9&e+&$4e;62;9_1!h6fj<3_|CwgI~S8w)YieZ|(oJEJRfAbZVjZF0^f;$8s2yqA{2r~#z z5pMPdiUzKE-cr-`qVP?3m{*dXyl;+Le2q|2jgrJJO?M^8e}Pw$()hW-u%F@rFJX$FrB84Q~Y zuNdhWbr~HoW-*pAzGI?fQf6|*?Im%v1_oKVy|HzVZY1%oP(Cb z8b>|HGRHNJ&m6xv@i`ed#W_82R&j1{UgdnpMapH7E0e2?tB-4o>ps^ju5aAj+?Kgr zad&c`T^D*Mj4Lc8>_)gp zcv$$d@LLfs5lIm{B3UB+B4A6!k9JD*9TCLrh9cU(BUgmRPgc`mESFvG?Lc z;#uO0;x8qrB={w?B%DgrOUy}Jm86ywkhCx9TGEGPu4J|3nB-Z>mr^`Z?xg%kP6FUpTTceGexV|ZF7Sdg`gV~V5Wb*c>Hk^hgy2iRj21X{6Yo7&7 x_Ib9Dv0(GNXAhV*UwE0qvf2AxrkEn*|Njh33=H7(&v1`TD-V*|VX1!dE&!@iRJ{NI diff --git a/src/plugins/qmldesigner/components/componentcore/theme.h b/src/plugins/qmldesigner/components/componentcore/theme.h index 73184d391c2..392f6c94f6e 100644 --- a/src/plugins/qmldesigner/components/componentcore/theme.h +++ b/src/plugins/qmldesigner/components/componentcore/theme.h @@ -83,6 +83,7 @@ public: binding_medium, bounds_small, branch_medium, + cameraSpeed_medium, camera_medium, camera_small, centerHorizontal, From 7df7cc21a1b4736824701eaa71a47dfdae1767cd Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 21 Mar 2024 19:12:10 +0100 Subject: [PATCH 047/202] QmlDesigner: Add Qt 6.7 support to 3d wizard Change-Id: I54fc29b6934ca69fc07dc3aaea5fa9f37b69e76c Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- .../studio_templates/projects/application-3d/wizard.json | 8 ++++++++ 1 file changed, 8 insertions(+) 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 42d0bf748c8..5b4d910b718 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json @@ -278,6 +278,14 @@ 'TargetQuickVersion': '6.6', 'TargetQuick3DVersion': '6.6' })" + }, + { + "trKey": "Qt 6.7", + "value": + "({ + 'TargetQuickVersion': '6.7', + 'TargetQuick3DVersion': '6.7' + })" } ] } From 8f0d2117e011f44b40e1bbc540b8849d448d4c09 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Mon, 18 Mar 2024 20:38:37 +0100 Subject: [PATCH 048/202] QmlJS: remove old/unused qmljsindenter Change-Id: Ie21d838fc1c67c1a690869081f86e3fe7b242d2c Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Marco Bubke --- src/libs/qmljs/CMakeLists.txt | 1 - src/libs/qmljs/qmljs.qbs | 1 - src/libs/qmljs/qmljsindenter.cpp | 603 ------------------------------- src/libs/qmljs/qmljsindenter.h | 51 --- 4 files changed, 656 deletions(-) delete mode 100644 src/libs/qmljs/qmljsindenter.cpp delete mode 100644 src/libs/qmljs/qmljsindenter.h diff --git a/src/libs/qmljs/CMakeLists.txt b/src/libs/qmljs/CMakeLists.txt index 4ca15cf7cc2..a4966a8b4a6 100644 --- a/src/libs/qmljs/CMakeLists.txt +++ b/src/libs/qmljs/CMakeLists.txt @@ -33,7 +33,6 @@ add_qtc_library(QmlJS qmljsfindexportedcpptypes.cpp qmljsfindexportedcpptypes.h qmljsicons.cpp qmljsicons.h qmljsimportdependencies.cpp qmljsimportdependencies.h - qmljsindenter.cpp qmljsindenter.h qmljsinterpreter.cpp qmljsinterpreter.h qmljslineinfo.cpp qmljslineinfo.h qmljslink.cpp qmljslink.h diff --git a/src/libs/qmljs/qmljs.qbs b/src/libs/qmljs/qmljs.qbs index 18434c83bad..d5cc21faf15 100644 --- a/src/libs/qmljs/qmljs.qbs +++ b/src/libs/qmljs/qmljs.qbs @@ -28,7 +28,6 @@ QtcLibrary { "qmljsfindexportedcpptypes.cpp", "qmljsfindexportedcpptypes.h", "qmljsicons.cpp", "qmljsicons.h", "qmljsimportdependencies.cpp", "qmljsimportdependencies.h", - "qmljsindenter.cpp", "qmljsindenter.h", "qmljsinterpreter.cpp", "qmljsinterpreter.h", "qmljsdialect.cpp", "qmljsdialect.h", "qmljslineinfo.cpp", "qmljslineinfo.h", diff --git a/src/libs/qmljs/qmljsindenter.cpp b/src/libs/qmljs/qmljsindenter.cpp deleted file mode 100644 index 8055847d907..00000000000 --- a/src/libs/qmljs/qmljsindenter.cpp +++ /dev/null @@ -1,603 +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 - -/* - This file is a self-contained interactive indenter for Qt Script. - - The general problem of indenting a program is ill posed. On - the one hand, an indenter has to analyze programs written in a - free-form formal language that is best described in terms of - tokens, not characters, not lines. On the other hand, indentation - applies to lines and white space characters matter, and otherwise - the programs to indent are formally invalid in general, as they - are begin edited. - - The approach taken here works line by line. We receive a program - consisting of N lines or more, and we want to compute the - indentation appropriate for the Nth line. Lines beyond the Nth - lines are of no concern to us, so for simplicity we pretend the - program has exactly N lines and we call the Nth line the "bottom - line". Typically, we have to indent the bottom line when it's - still empty, so we concentrate our analysis on the N - 1 lines - that precede. - - By inspecting the (N - 1)-th line, the (N - 2)-th line, ... - backwards, we determine the kind of the bottom line and indent it - accordingly. - - * The bottom line is a comment line. See - bottomLineStartsInCComment() and - indentWhenBottomLineStartsInCComment(). - * The bottom line is a continuation line. See isContinuationLine() - and indentForContinuationLine(). - * The bottom line is a standalone line. See - indentForStandaloneLine(). - - Certain tokens that influence the indentation, notably braces, - are looked for in the lines. This is done by simple string - comparison, without a real tokenizer. Confusing constructs such - as comments and string literals are removed beforehand. -*/ - -#include -#include - -#include - -using namespace QmlJS; - -/* - Saves and restores the state of the global linizer. This enables - backtracking. - - Identical to the defines in qmljslineinfo.cpp -*/ -#define YY_SAVE() LinizerState savedState = yyLinizerState -#define YY_RESTORE() yyLinizerState = savedState - - -QmlJSIndenter::QmlJSIndenter() - : caseOrDefault(QRegularExpression(QLatin1String( - "^\\s*(?:" - "case\\b[^:]+|" - "default)" - "\\s*:.*$"))) - -{ - - /* - The indenter supports a few parameters: - - * ppHardwareTabSize is the size of a '\t' in your favorite editor. - * ppIndentSize is the size of an indentation, or software tab - size. - * ppContinuationIndentSize is the extra indent for a continuation - line, when there is nothing to align against on the previous - line. - * ppCommentOffset is the indentation within a C-style comment, - when it cannot be picked up. - */ - - ppHardwareTabSize = 8; - ppIndentSize = 4; - ppContinuationIndentSize = 8; - ppCommentOffset = 2; -} - -QmlJSIndenter::~QmlJSIndenter() -{ -} - -void QmlJSIndenter::setTabSize(int size) -{ - ppHardwareTabSize = size; -} - -void QmlJSIndenter::setIndentSize(int size) -{ - ppIndentSize = size; - ppContinuationIndentSize = 2 * size; -} - -/* - Returns true if string t is made only of white space; otherwise - returns false. -*/ -bool QmlJSIndenter::isOnlyWhiteSpace(const QString &t) const -{ - return firstNonWhiteSpace(t).isNull(); -} - -/* - Assuming string t is a line, returns the column number of a given - index. Column numbers and index are identical for strings that don't - contain '\t's. -*/ -int QmlJSIndenter::columnForIndex(const QString &t, int index) const -{ - int col = 0; - if (index > t.length()) - index = t.length(); - - for (int i = 0; i < index; i++) { - if (t.at(i) == QLatin1Char('\t')) - col = ((col / ppHardwareTabSize) + 1) * ppHardwareTabSize; - else - col++; - } - return col; -} - -/* - Returns the indentation size of string t. -*/ -int QmlJSIndenter::indentOfLine(const QString &t) const -{ - return columnForIndex(t, t.indexOf(firstNonWhiteSpace(t))); -} - -/* - Replaces t[k] by ch, unless t[k] is '\t'. Tab characters are better - left alone since they break the "index equals column" rule. No - provisions are taken against '\n' or '\r', which shouldn't occur in - t anyway. -*/ -void QmlJSIndenter::eraseChar(QString &t, int k, QChar ch) const -{ - if (t.at(k) != QLatin1Char('\t')) - t[k] = ch; -} - -/* - Returns '(' if the last parenthesis is opening, ')' if it is - closing, and QChar() if there are no parentheses in t. -*/ -QChar QmlJSIndenter::lastParen() const -{ - for (int index = yyLinizerState.tokens.size() - 1; index != -1; --index) { - const Token &token = yyLinizerState.tokens.at(index); - - if (token.is(Token::LeftParenthesis)) - return QLatin1Char('('); - - else if (token.is(Token::RightParenthesis)) - return QLatin1Char(')'); - } - - return QChar(); -} - -/* - Returns true if typedIn the same as okayCh or is null; otherwise - returns false. -*/ -bool QmlJSIndenter::okay(QChar typedIn, QChar okayCh) const -{ - return typedIn == QChar() || typedIn == okayCh; -} - -/* - Returns the recommended indent for the bottom line of yyProgram - assuming that it starts in a C-style comment, a condition that is - tested elsewhere. - - Essentially, we're trying to align against some text on the - previous line. -*/ -int QmlJSIndenter::indentWhenBottomLineStartsInMultiLineComment() -{ - QTextBlock block = yyProgram.lastBlock().previous(); - QString blockText; - - for (; block.isValid(); block = block.previous()) { - blockText = block.text(); - - if (! isOnlyWhiteSpace(blockText)) - break; - } - - return indentOfLine(blockText); -} - -/* - Returns the recommended indent for the bottom line of yyProgram, - assuming it's a continuation line. - - We're trying to align the continuation line against some parenthesis - or other bracked left opened on a previous line, or some interesting - operator such as '='. -*/ -int QmlJSIndenter::indentForContinuationLine() -{ - int braceDepth = 0; - int delimDepth = 0; - - bool leftBraceFollowed = *yyLeftBraceFollows; - - for (int i = 0; i < SmallRoof; i++) { - int hook = -1; - - int j = yyLine->length(); - while (j > 0 && hook < 0) { - j--; - QChar ch = yyLine->at(j); - - switch (ch.unicode()) { - case ')': - delimDepth++; - break; - case ']': - braceDepth++; - break; - case '}': - braceDepth++; - break; - case '(': - delimDepth--; - /* - An unclosed delimiter is a good place to align at, - at least for some styles (including Qt's). - */ - if (delimDepth == -1) - hook = j; - break; - - case '[': - braceDepth--; - /* - An unclosed delimiter is a good place to align at, - at least for some styles (including Qt's). - */ - if (braceDepth == -1) - hook = j; - break; - case '{': - braceDepth--; - /* - A left brace followed by other stuff on the same - line is typically for an enum or an initializer. - Such a brace must be treated just like the other - delimiters. - */ - if (braceDepth == -1) { - if (j < yyLine->length() - 1) - hook = j; - else - return 0; // shouldn't happen - } - break; - case '=': - /* - An equal sign is a very natural alignment hook - because it's usually the operator with the lowest - precedence in statements it appears in. Case in - point: - - int x = 1 + - 2; - - However, we have to beware of constructs such as - default arguments and explicit enum constant - values: - - void foo(int x = 0, - int y = 0); - - And not - - void foo(int x = 0, - int y = 0); - - These constructs are caracterized by a ',' at the - end of the unfinished lines or by unbalanced - parentheses. - */ - Q_ASSERT(j - 1 >= 0); - - if (QString::fromLatin1("!=<>").indexOf(yyLine->at(j - 1)) == -1 && - j + 1 < yyLine->length() && yyLine->at(j + 1) != QLatin1Char('=')) { - if (braceDepth == 0 && delimDepth == 0 && - j < yyLine->length() - 1 && - !yyLine->endsWith(QLatin1Char(',')) && - (yyLine->contains(QLatin1Char('(')) == yyLine->contains(QLatin1Char(')')))) - hook = j; - } - } - } - - if (hook >= 0) { - /* - Yes, we have a delimiter or an operator to align - against! We don't really align against it, but rather - against the following token, if any. In this example, - the following token is "11": - - int x = (11 + - 2); - - If there is no such token, we use a continuation indent: - - static QRegExp foo(QString( - "foo foo foo foo foo foo foo foo foo")); - */ - hook++; - while (hook < yyLine->length()) { - if (!yyLine->at(hook).isSpace()) - return columnForIndex(*yyLine, hook); - hook++; - } - return indentOfLine(*yyLine) + ppContinuationIndentSize; - } - - if (braceDepth != 0) - break; - - /* - The line's delimiters are balanced. It looks like a - continuation line or something. - */ - if (delimDepth == 0) { - if (leftBraceFollowed) { - /* - We have - - int main() - { - - or - - Bar::Bar() - : Foo(x) - { - - The "{" should be flush left. - */ - if (!isContinuationLine()) - return indentOfLine(*yyLine); - } else if (isContinuationLine() || yyLine->endsWith(QLatin1Char(','))) { - /* - We have - - x = a + - b + - c; - - or - - int t[] = { - 1, 2, 3, - 4, 5, 6 - - The "c;" should fall right under the "b +", and the - "4, 5, 6" right under the "1, 2, 3,". - */ - return indentOfLine(*yyLine); - } else { - /* - We have - - stream << 1 + - 2; - - We could, but we don't, try to analyze which - operator has precedence over which and so on, to - obtain the excellent result - - stream << 1 + - 2; - - We do have a special trick above for the assignment - operator above, though. - */ - return indentOfLine(*yyLine) + ppContinuationIndentSize; - } - } - - if (!readLine()) - break; - } - return 0; -} - -/* - Returns the recommended indent for the bottom line of yyProgram if - that line is standalone (or should be indented likewise). - - Indenting a standalone line is tricky, mostly because of braceless - control statements. Grossly, we are looking backwards for a special - line, a "hook line", that we can use as a starting point to indent, - and then modify the indentation level according to the braces met - along the way to that hook. - - Let's consider a few examples. In all cases, we want to indent the - bottom line. - - Example 1: - - x = 1; - y = 2; - - The hook line is "x = 1;". We met 0 opening braces and 0 closing - braces. Therefore, "y = 2;" inherits the indent of "x = 1;". - - Example 2: - - if (x) { - y; - - The hook line is "if (x) {". No matter what precedes it, "y;" has - to be indented one level deeper than the hook line, since we met one - opening brace along the way. - - Example 3: - - if (a) - while (b) { - c; - } - d; - - To indent "d;" correctly, we have to go as far as the "if (a)". - Compare with - - if (a) { - while (b) { - c; - } - d; - - Still, we're striving to go back as little as possible to - accommodate people with irregular indentation schemes. A hook line - near at hand is much more reliable than a remote one. -*/ -int QmlJSIndenter::indentForStandaloneLine() -{ - for (int i = 0; i < SmallRoof; i++) { - if (!*yyLeftBraceFollows) { - YY_SAVE(); - - if (matchBracelessControlStatement()) { - /* - The situation is this, and we want to indent "z;": - - if (x && - y) - z; - - yyLine is "if (x &&". - */ - return indentOfLine(*yyLine) + ppIndentSize; - } - YY_RESTORE(); - } - - if (yyLine->endsWith(QLatin1Char(';')) || yyLine->contains(QLatin1Char('{'))) { - /* - The situation is possibly this, and we want to indent - "z;": - - while (x) - y; - z; - - We return the indent of "while (x)". In place of "y;", - any arbitrarily complex compound statement can appear. - */ - - if (*yyBraceDepth > 0) { - do { - if (!readLine()) - break; - } while (*yyBraceDepth > 0); - } - - LinizerState hookState; - - while (isContinuationLine()) - readLine(); - hookState = yyLinizerState; - - readLine(); - if (*yyBraceDepth <= 0) { - do { - if (!matchBracelessControlStatement()) - break; - hookState = yyLinizerState; - } while (readLine()); - } - - yyLinizerState = hookState; - - while (isContinuationLine()) - readLine(); - - int indentChange = - *yyBraceDepth; - if (caseOrDefault.match(*yyLine).hasMatch()) - ++indentChange; - - /* - Never trust lines containing only '{' or '}', as some - people (Richard M. Stallman) format them weirdly. - */ - if (yyLine->trimmed().length() > 1) - return indentOfLine(*yyLine) + indentChange * ppIndentSize; - } - - if (!readLine()) - return -*yyBraceDepth * ppIndentSize; - } - return 0; -} - -/* - Returns the recommended indent for the bottom line of program. - Unless null, typedIn stores the character of yyProgram that - triggered reindentation. - - This function works better if typedIn is set properly; it is - slightly more conservative if typedIn is completely wild, and - slighly more liberal if typedIn is always null. The user might be - annoyed by the liberal behavior. -*/ -int QmlJSIndenter::indentForBottomLine(QTextBlock begin, QTextBlock end, QChar typedIn) -{ - if (begin == end) - return 0; - - const QTextBlock last = end.previous(); - - initialize(begin, last); - - QString bottomLine = last.text(); - QChar firstCh = firstNonWhiteSpace(bottomLine); - int indent = 0; - - if (bottomLineStartsInMultilineComment()) { - /* - The bottom line starts in a C-style comment. Indent it - smartly, unless the user has already played around with it, - in which case it's better to leave her stuff alone. - */ - if (isOnlyWhiteSpace(bottomLine)) - indent = indentWhenBottomLineStartsInMultiLineComment(); - else - indent = indentOfLine(bottomLine); - } else { - if (isUnfinishedLine()) - indent = indentForContinuationLine(); - else - indent = indentForStandaloneLine(); - - if ((okay(typedIn, QLatin1Char('}')) && firstCh == QLatin1Char('}')) - || (okay(typedIn, QLatin1Char(']')) && firstCh == QLatin1Char(']'))) { - /* - A closing brace is one level more to the left than the - code it follows. - */ - indent -= ppIndentSize; - } else if (okay(typedIn, QLatin1Char(':'))) { - if (caseOrDefault.match(bottomLine).hasMatch()) { - /* - Move a case label (or the ':' in front of a - constructor initialization list) one level to the - left, but only if the user did not play around with - it yet. Some users have exotic tastes in the - matter, and most users probably are not patient - enough to wait for the final ':' to format their - code properly. - - We don't attempt the same for goto labels, as the - user is probably the middle of "foo::bar". (Who - uses goto, anyway?) - */ - if (indentOfLine(bottomLine) <= indent) - indent -= ppIndentSize; - else - indent = indentOfLine(bottomLine); - } - } - } - - return qMax(0, indent); -} - diff --git a/src/libs/qmljs/qmljsindenter.h b/src/libs/qmljs/qmljsindenter.h deleted file mode 100644 index 55141c1b3b2..00000000000 --- a/src/libs/qmljs/qmljsindenter.h +++ /dev/null @@ -1,51 +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 - -QT_FORWARD_DECLARE_CLASS(QTextBlock) - -namespace QmlJS { - -class QMLJS_EXPORT QmlJSIndenter : public LineInfo -{ - Q_DISABLE_COPY(QmlJSIndenter) - -public: - QmlJSIndenter(); - ~QmlJSIndenter(); - - void setTabSize(int size); - void setIndentSize(int size); - - int indentForBottomLine(QTextBlock firstBlock, QTextBlock lastBlock, QChar typedIn); - -private: - bool isOnlyWhiteSpace(const QString &t) const; - int columnForIndex(const QString &t, int index) const; - int indentOfLine(const QString &t) const; - - void eraseChar(QString &t, int k, QChar ch) const; - QChar lastParen() const; - bool okay(QChar typedIn, QChar okayCh) const; - - int indentWhenBottomLineStartsInMultiLineComment(); - int indentForContinuationLine(); - int indentForStandaloneLine(); - -private: - int ppHardwareTabSize; - int ppIndentSize; - int ppContinuationIndentSize; - int ppCommentOffset; - -private: - QRegularExpression caseOrDefault; -}; - -} // namespace QmlJS From 431d29859bd28cfc38274eed896a0d813ca86c85 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Mon, 18 Mar 2024 20:40:40 +0100 Subject: [PATCH 049/202] QmlPreview: add newly introduce dependency TextEditor for qml files has a preview action nowadays. Change-Id: I963ef8be8627e14ef56f18bbbc56b9007c5c69d2 Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- src/plugins/qmlpreview/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmlpreview/CMakeLists.txt b/src/plugins/qmlpreview/CMakeLists.txt index 4c1889143aa..440b407b70a 100644 --- a/src/plugins/qmlpreview/CMakeLists.txt +++ b/src/plugins/qmlpreview/CMakeLists.txt @@ -4,7 +4,7 @@ add_qtc_plugin(QmlPreview DEPENDS QmlJS Qt::QmlPrivate PLUGIN_DEPENDS Core ProjectExplorer QmlJSTools QtSupport - ResourceEditor QmlProjectManager + ResourceEditor QmlProjectManager TextEditor SOURCES qmlpreviewclient.cpp qmlpreviewclient.h qmlpreviewconnectionmanager.cpp qmlpreviewconnectionmanager.h From 06f75dbdbfe8c57e602c4fc258ab8934cd208089 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 21 Mar 2024 14:18:53 +0100 Subject: [PATCH 050/202] Nanotrace: Add generic map support Change-Id: I5622697e7c5a34e204d045d61bafa874ce3783d5 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen Reviewed-by: --- src/libs/nanotrace/nanotracehr.h | 41 +++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index 5f2b031d55a..37e06b559b4 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -247,8 +248,10 @@ void convertToString(String &string, const Container &values) { string.append('['); - for (const auto &value : values) + for (const auto &value : values) { convertToString(string, value); + string.append(','); + } if (values.size()) string.pop_back(); @@ -256,6 +259,42 @@ void convertToString(String &string, const Container &values) string.append(']'); } +template +struct is_map : std::false_type +{}; + +template +struct is_map> : std::true_type +{}; + +template +struct is_map> : std::true_type +{}; + +template::value, bool> = true> +void convertToString(String &string, const Map &map) +{ + string.append('{'); + + for (const auto &[key, value] : map) { + convertToString(string, key); + string.append(':'); + convertToString(string, value); + string.append(','); + } + + if (map.begin() != map.end()) + string.pop_back(); + + string.append('}'); +} + +template +void convertToString(String &string, const QMap &dictonary) +{ + convertToString(string, dictonary.asKeyValueRange()); +} + namespace Internal { template From 39a2b8f2e6f32040e824f21c2532853d036fce82 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 21 Mar 2024 15:38:15 +0100 Subject: [PATCH 051/202] Nanotrace: Initialize variables Change-Id: Id80d6324a287e7391a4a7b32c495869a9312bef3 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Tim Jenssen --- src/libs/nanotrace/nanotracehr.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index 37e06b559b4..810e4e3c5d7 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -366,7 +366,7 @@ auto array(Entries &&...entries) return std::make_tuple(isArray, std::forward(entries)...); } -enum class IsFlow : std::size_t { No = 0, Out = 1 << 0, In = 1 << 1, InOut = In | Out }; +enum class IsFlow : char { No = 0, Out = 1 << 0, In = 1 << 1, InOut = In | Out }; inline bool operator&(IsFlow first, IsFlow second) { @@ -393,8 +393,8 @@ struct alignas(4096) TraceEvent TimePoint time; Duration duration; std::size_t id = 0; - std::size_t bindId : 62; - IsFlow flow : 2; + std::size_t bindId = 0; + IsFlow flow = IsFlow::No; char type = ' '; ArgumentsString arguments; }; @@ -1589,8 +1589,8 @@ private: private: StringType m_name; - std::size_t m_bindId; - IsFlow flow; + std::size_t m_bindId = 0; + IsFlow flow = IsFlow::No; CategoryFunctionPointer m_category; }; From 5e126b794c54628f5767fab6616c2d8806c8498c Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 21 Mar 2024 16:24:43 +0100 Subject: [PATCH 052/202] QmlDesigner: Add root module path The root path was missing for the lite designer. Change-Id: I4c24667e1e46c9c2443224d8bae183c7b91a616e Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- src/plugins/qmldesigner/qmldesignerprojectmanager.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp index f21e0f93fa1..c415863e8a5 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp @@ -375,6 +375,10 @@ void collectQmldirPaths(const QString &path, QStringList &qmldirPaths) { QDirIterator dirIterator{path, QDir::Dirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories}; + QString rootQmldirPath = path + "/qmldir"; + if (!skipPath(path) && QFileInfo::exists(rootQmldirPath)) + qmldirPaths.push_back(path); + while (dirIterator.hasNext()) { auto directoryPath = dirIterator.next(); From aa76098dc1322cc10561b86aec9d3540be08c670 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 21 Mar 2024 15:38:44 +0100 Subject: [PATCH 053/202] Nanotrace: Fix static string It could easily overflow. Change-Id: I82c5611aa082bf3e8af394259ad8e15387b3e920 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Tim Jenssen --- src/libs/nanotrace/nanotracehr.cpp | 15 ++++++++++++++- src/libs/nanotrace/staticstring.h | 10 +++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/libs/nanotrace/nanotracehr.cpp b/src/libs/nanotrace/nanotracehr.cpp index 6a1ab441f5a..00062274340 100644 --- a/src/libs/nanotrace/nanotracehr.cpp +++ b/src/libs/nanotrace/nanotracehr.cpp @@ -45,6 +45,18 @@ unsigned int getUnsignedIntegerHash(std::thread::id id) return static_cast(std::hash{}(id) & 0xFFFFFFFF); } +template +constexpr bool isArgumentValid(const StaticString &string) +{ + return string.isValid() && string.size(); +} + +template +constexpr bool isArgumentValid(const String &string) +{ + return string.size(); +} + template void printEvent(std::ostream &out, const TraceEvent &event, qint64 processId, std::thread::id threadId) { @@ -67,8 +79,9 @@ void printEvent(std::ostream &out, const TraceEvent &event, qint64 processId, st out << R"(,"flow_in":true)"; } - if (event.arguments.size() && event.arguments.size() != std::numeric_limits::max()) + if (isArgumentValid(event.arguments)) { out << R"(,"args":)" << event.arguments; + } out << "}"; } diff --git a/src/libs/nanotrace/staticstring.h b/src/libs/nanotrace/staticstring.h index 0d5c8a14007..d787bd2fe30 100644 --- a/src/libs/nanotrace/staticstring.h +++ b/src/libs/nanotrace/staticstring.h @@ -31,13 +31,13 @@ public: { auto newSize = m_size + string.size(); - if (newSize < Capacity) { + if (newSize <= Capacity) { std::char_traits::copy(std::next(data(), static_cast(m_size)), string.data(), string.size()); m_size = newSize; } else { - m_size = std::numeric_limits::max(); + m_size = Capacity + 1; } } @@ -45,13 +45,13 @@ public: { auto newSize = m_size + 1; - if (newSize < Capacity) { + if (newSize <= Capacity) { auto current = std::next(data(), static_cast(m_size)); *current = character; m_size = newSize; } else { - m_size = std::numeric_limits::max(); + m_size = Capacity + 1; } } @@ -97,7 +97,7 @@ public: return *this; } - bool isValid() const { return m_size != std::numeric_limits::max(); } + bool isValid() const { return m_size <= Capacity; } std::size_t size() const { return m_size; } From f3be9eaae36fa30e988f07a3ac3a495ee196e1b3 Mon Sep 17 00:00:00 2001 From: Teea Poldsam Date: Thu, 21 Mar 2024 13:29:34 +0200 Subject: [PATCH 054/202] Doc: Remove the "Qt 5 only" mark for Blending in Custom Shaders Fixes: QDS-12289 Change-Id: Id532c1978327738510fe82dd2f60fa0baa6b66fe Reviewed-by: Leena Miettinen Reviewed-by: Reviewed-by: Qt CI Patch Build Bot --- .../src/qtquick3d-editor/qtdesignstudio-3d-custom-shaders.qdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-custom-shaders.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-custom-shaders.qdoc index b559fd2230e..5675ac92863 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-custom-shaders.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-custom-shaders.qdoc @@ -218,7 +218,7 @@ \row \li \l Blending - \li \inlineimage ok.png + \li \li A pass command that specifies the source blending function. The \uicontrol Source property specifies the source blending From 2128776e16788700bfdc5b681fa9dd04f87bc82b Mon Sep 17 00:00:00 2001 From: Teea Poldsam Date: Thu, 21 Mar 2024 12:26:45 +0200 Subject: [PATCH 055/202] Doc: Add menu bar to shortcuts and ui topics Fixes: QDS-12286 Change-Id: I8f8eb7e443ef94d54c5a9868a6f1f4c00a138832 Reviewed-by: Mats Honkamaa Reviewed-by: Qt CI Patch Build Bot --- doc/qtcreator/src/debugger/qtquick-debugger-example.qdoc | 2 +- doc/qtcreator/src/debugger/qtquick-debugging.qdoc | 2 +- doc/qtcreator/src/qtquick/qtquick-profiler.qdoc | 2 +- doc/qtcreator/src/user-interface/creator-ui.qdoc | 5 ++++- .../src/developers/studio-designer-developer-workflow.qdoc | 2 +- doc/qtdesignstudio/src/overviews/qtquick-export.qdoc | 2 +- doc/qtdesignstudio/src/qtdesignstudio-packaging.qdoc | 2 +- .../src/reference/qtdesignstudio-keyboard-shortcuts.qdoc | 3 +++ 8 files changed, 13 insertions(+), 7 deletions(-) diff --git a/doc/qtcreator/src/debugger/qtquick-debugger-example.qdoc b/doc/qtcreator/src/debugger/qtquick-debugger-example.qdoc index 797788897ac..3dc99583cf1 100644 --- a/doc/qtcreator/src/debugger/qtquick-debugger-example.qdoc +++ b/doc/qtcreator/src/debugger/qtquick-debugger-example.qdoc @@ -30,7 +30,7 @@ \if defined(qtdesignstudio) \note In this section, you are using advanced menu items. These are not visible by default. To toggle the visibility of advanced menu items, see - \l{Customizing the Menu}. + \l{Customizing the Menu Bar}. \endif diff --git a/doc/qtcreator/src/debugger/qtquick-debugging.qdoc b/doc/qtcreator/src/debugger/qtquick-debugging.qdoc index c888927e022..e9be94de485 100644 --- a/doc/qtcreator/src/debugger/qtquick-debugging.qdoc +++ b/doc/qtcreator/src/debugger/qtquick-debugging.qdoc @@ -29,7 +29,7 @@ \if defined(qtdesignstudio) \note In this section, you are using advanced menu items. These are not visible by default. To toggle the visibility of advanced menu items, see - \l{Customizing the Menu}. + \l{Customizing the Menu Bar}. \endif \section1 Setting Up QML Debugging diff --git a/doc/qtcreator/src/qtquick/qtquick-profiler.qdoc b/doc/qtcreator/src/qtquick/qtquick-profiler.qdoc index 0fd232d8b9c..d4c328b1f0c 100644 --- a/doc/qtcreator/src/qtquick/qtquick-profiler.qdoc +++ b/doc/qtcreator/src/qtquick/qtquick-profiler.qdoc @@ -61,7 +61,7 @@ \if defined(qtdesignstudio) \note In this section, you are using advanced menu items. These are not visible by default. To toggle the visibility of advanced menu items, see - \l{Customizing the Menu}. + \l{Customizing the Menu Bar}. \endif \section1 Using QML Profiler diff --git a/doc/qtcreator/src/user-interface/creator-ui.qdoc b/doc/qtcreator/src/user-interface/creator-ui.qdoc index 066e93fbbf6..c1742ab6b6f 100644 --- a/doc/qtcreator/src/user-interface/creator-ui.qdoc +++ b/doc/qtcreator/src/user-interface/creator-ui.qdoc @@ -95,7 +95,7 @@ \endif \if defined(qtdesignstudio) - \section1 Customizing the Menu + \section1 Customizing the Menu Bar By default, top-level menu items \uicontrol Build, \uicontrol Debug, and \uicontrol Analyze are not visible. These menu items have options for @@ -113,6 +113,9 @@ You need to restart \QDS to apply changes made to these settings. + \note To show or hide the \uicontrol {Menu Bar}, select \uicontrol View > + \uicontrol {Show Menu Bar}, or use \key {Ctrl+Alt+M}. + \section1 Customizing the UI The following topics describe how to customize the UI: diff --git a/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc b/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc index 8c13b7b116c..c0146967cad 100644 --- a/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc +++ b/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc @@ -10,7 +10,7 @@ \note In this section, you are using advanced menu items. These are not visible by default. To toggle the visibility of advanced menu items, see - \l{Customizing the Menu}. + \l{Customizing the Menu Bar}. \QDS enables designers and developers to work together on common projects to develop applications. Designers use the \l{Design Views}{views} diff --git a/doc/qtdesignstudio/src/overviews/qtquick-export.qdoc b/doc/qtdesignstudio/src/overviews/qtquick-export.qdoc index f9ae3b8d7b4..f083f85f8e4 100644 --- a/doc/qtdesignstudio/src/overviews/qtquick-export.qdoc +++ b/doc/qtdesignstudio/src/overviews/qtquick-export.qdoc @@ -10,7 +10,7 @@ \note In this section, you are using advanced menu items. These are not visible by default. To toggle the visibility of advanced menu items, see - \l{Customizing the Menu}. + \l{Customizing the Menu Bar}. \l{glossary-component}{Components} contained in \l{UI Files} {UI files} (.ui.qml) can be exported to JSON metadata format and PNG assets. diff --git a/doc/qtdesignstudio/src/qtdesignstudio-packaging.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-packaging.qdoc index 1e426f011e1..4158a7b545d 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-packaging.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-packaging.qdoc @@ -10,7 +10,7 @@ \note In this section, you are using advanced menu items. These are not visible by default. To toggle the visibility of advanced menu items, see - \l{Customizing the Menu}. + \l{Customizing the Menu Bar}. When you are ready to deliver your application to users or upload it to app stores, you can use \QDS to create suitable packages that contain all diff --git a/doc/qtdesignstudio/src/reference/qtdesignstudio-keyboard-shortcuts.qdoc b/doc/qtdesignstudio/src/reference/qtdesignstudio-keyboard-shortcuts.qdoc index ac6354ca90b..dd69b68ecd1 100644 --- a/doc/qtdesignstudio/src/reference/qtdesignstudio-keyboard-shortcuts.qdoc +++ b/doc/qtdesignstudio/src/reference/qtdesignstudio-keyboard-shortcuts.qdoc @@ -111,6 +111,9 @@ \row \li Previous open document in history \li \key{Ctrl+Tab} + \row + \li Toggle \uicontrol {Menu Bar} visibility + \li \key{Ctrl+Alt+M} \row \li Switch to \uicontrol Welcome mode \li \key{Ctrl+1} From 002bfbf80f81d2d41a159442a927b22b6bff0534 Mon Sep 17 00:00:00 2001 From: Shrief Gabr Date: Tue, 19 Mar 2024 14:52:20 +0200 Subject: [PATCH 056/202] EffectComposer: Add 'reset' button for values Task-number: QDS-11719 Change-Id: Ia03366bf109427fbcfe5cc1d4f68ae97fa8dc256 Reviewed-by: Miikka Heikkinen Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../EffectCompositionNode.qml | 2 ++ .../EffectCompositionNodeUniform.qml | 33 +++++++++++++++++-- .../effectComposerQmlSources/ValueColor.qml | 10 ++++-- .../effectComposerQmlSources/ValueFloat.qml | 6 +++- .../effectComposerQmlSources/ValueInt.qml | 6 +++- .../effectComposerQmlSources/ValueVec2.qml | 12 +++++-- .../effectComposerQmlSources/ValueVec3.qml | 18 ++++++++-- .../effectComposerQmlSources/ValueVec4.qml | 24 +++++++++++--- .../imports/HelperWidgets/DoubleSpinBox.qml | 1 + .../effectcomposeruniformsmodel.cpp | 12 ++++++- .../effectcomposeruniformsmodel.h | 1 + src/plugins/effectcomposer/uniform.cpp | 6 ++++ 12 files changed, 115 insertions(+), 16 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNode.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNode.qml index 6defa3b0922..a606461b5c5 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNode.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNode.qml @@ -43,6 +43,8 @@ HelperWidgets.Section { EffectCompositionNodeUniform { width: root.width + + onReset: nodeUniformsModel.resetData(index) } } } diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNodeUniform.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNodeUniform.qml index 71cbf94f8bc..7c3214c5fa7 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNodeUniform.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNodeUniform.qml @@ -13,9 +13,10 @@ Item { id: root height: layout.implicitHeight - visible: !uniformUseCustomValue + signal reset() + Component.onCompleted: { if (uniformType === "int") { if (uniformControlType === "channel") @@ -49,10 +50,11 @@ Item { RowLayout { id: layout - spacing: 20 anchors.fill: parent Text { + id: textName + text: uniformDisplayName color: StudioTheme.Values.themeTextColor font.pixelSize: StudioTheme.Values.baseFontSize @@ -63,11 +65,38 @@ Item { elide: Text.ElideRight HelperWidgets.ToolTipArea { + id: tooltipArea + anchors.fill: parent tooltip: uniformDescription } } + Item { + Layout.preferredHeight: 30 + Layout.preferredWidth: 30 + + MouseArea { + id: mouseArea + + anchors.fill: parent + hoverEnabled: true + } + + HelperWidgets.IconButton { + id: iconButton + + buttonSize: 24 + icon: StudioTheme.Constants.reload_medium + iconSize: 16 + anchors.centerIn: parent + visible: mouseArea.containsMouse || iconButton.containsMouse + tooltip: qsTr("Reset value") + onClicked: root.reset() + } + + } + Loader { id: valueLoader Layout.fillWidth: true diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueColor.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueColor.qml index 4b00bd76135..f7482260b58 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueColor.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueColor.qml @@ -15,8 +15,14 @@ Row { StudioControls.ColorEditor { actionIndicatorVisible: false - Component.onCompleted: color = uniformValue + // color: uniformValue binding can get overwritten by normal operation of the control + property color resetValue: uniformValue - onColorChanged: uniformValue = color + onResetValueChanged: color = uniformValue + Component.onCompleted: color = uniformValue + onColorChanged: { + if (uniformValue !== color) + uniformValue = color + } } } diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueFloat.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueFloat.qml index 7348d6668be..969d7e29492 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueFloat.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueFloat.qml @@ -14,6 +14,10 @@ Row { HelperWidgets.DoubleSpinBox { id: spinBox + // value: uniformValue binding can get overwritten by normal operation of the control + property double resetValue: uniformValue + onResetValueChanged: value = resetValue + width: 60 spinBoxIndicatorVisible: false inputHAlignment: Qt.AlignHCenter @@ -22,7 +26,7 @@ Row { value: uniformValue stepSize: .01 decimals: 2 - onValueChanged: uniformValue = value + onValueModified: uniformValue = value } StudioControls.Slider { diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueInt.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueInt.qml index 89f571c8cb7..d67929168a9 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueInt.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueInt.qml @@ -14,6 +14,10 @@ Row { HelperWidgets.DoubleSpinBox { id: spinBox + // value: uniformValue binding can get overwritten by normal operation of the control + property double resetValue: uniformValue + onResetValueChanged: value = resetValue + width: 60 spinBoxIndicatorVisible: false inputHAlignment: Qt.AlignHCenter @@ -22,7 +26,7 @@ Row { value: uniformValue stepSize: 1 decimals: 0 - onValueChanged: uniformValue = Math.round(value) + onValueModified: uniformValue = Math.round(value) } StudioControls.Slider { diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec2.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec2.qml index b703d5f184a..adb4fe99056 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec2.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec2.qml @@ -15,6 +15,10 @@ RowLayout { HelperWidgets.DoubleSpinBox { id: vX + // value: uniformValue binding can get overwritten by normal operation of the control + property double resetValue: uniformValue.x + onResetValueChanged: value = resetValue + Layout.fillWidth: true Layout.minimumWidth: 30 Layout.maximumWidth: 60 @@ -26,7 +30,7 @@ RowLayout { value: uniformValue.x stepSize: .01 decimals: 2 - onValueChanged: uniformValue.x = value + onValueModified: uniformValue.x = value } Item { // spacer @@ -51,6 +55,10 @@ RowLayout { HelperWidgets.DoubleSpinBox { id: vY + // value: uniformValue binding can get overwritten by normal operation of the control + property double resetValue: uniformValue.y + onResetValueChanged: value = resetValue + Layout.fillWidth: true Layout.minimumWidth: 30 Layout.maximumWidth: 60 @@ -62,7 +70,7 @@ RowLayout { value: uniformValue.y stepSize: .01 decimals: 2 - onValueChanged: uniformValue.y = value + onValueModified: uniformValue.y = value } Item { // spacer diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec3.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec3.qml index d59b63a514f..78573c48f67 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec3.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec3.qml @@ -15,6 +15,10 @@ RowLayout { HelperWidgets.DoubleSpinBox { id: vX + // value: uniformValue binding can get overwritten by normal operation of the control + property double resetValue: uniformValue.x + onResetValueChanged: value = resetValue + Layout.fillWidth: true Layout.minimumWidth: 30 Layout.maximumWidth: 60 @@ -26,7 +30,7 @@ RowLayout { value: uniformValue.x stepSize: .01 decimals: 2 - onValueChanged: uniformValue.x = value + onValueModified: uniformValue.x = value } Item { // spacer @@ -51,6 +55,10 @@ RowLayout { HelperWidgets.DoubleSpinBox { id: vY + // value: uniformValue binding can get overwritten by normal operation of the control + property double resetValue: uniformValue.y + onResetValueChanged: value = resetValue + Layout.fillWidth: true Layout.minimumWidth: 30 Layout.maximumWidth: 60 @@ -62,7 +70,7 @@ RowLayout { value: uniformValue.y stepSize: .01 decimals: 2 - onValueChanged: uniformValue.y = value + onValueModified: uniformValue.y = value } Item { // spacer @@ -87,6 +95,10 @@ RowLayout { HelperWidgets.DoubleSpinBox { id: vZ + // value: uniformValue binding can get overwritten by normal operation of the control + property double resetValue: uniformValue.z + onResetValueChanged: value = resetValue + Layout.fillWidth: true Layout.minimumWidth: 30 Layout.maximumWidth: 60 @@ -98,7 +110,7 @@ RowLayout { value: uniformValue.z stepSize: .01 decimals: 2 - onValueChanged: uniformValue.z = value + onValueModified: uniformValue.z = value } Item { // spacer diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec4.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec4.qml index 7e930abf815..61ce8e63893 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec4.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec4.qml @@ -15,6 +15,10 @@ RowLayout { HelperWidgets.DoubleSpinBox { id: vX + // value: uniformValue binding can get overwritten by normal operation of the control + property double resetValue: uniformValue.x + onResetValueChanged: value = resetValue + Layout.fillWidth: true Layout.minimumWidth: 30 Layout.maximumWidth: 60 @@ -26,7 +30,7 @@ RowLayout { value: uniformValue.x stepSize: .01 decimals: 2 - onValueChanged: uniformValue.x = value + onValueModified: uniformValue.x = value } Item { // spacer @@ -51,6 +55,10 @@ RowLayout { HelperWidgets.DoubleSpinBox { id: vY + // value: uniformValue binding can get overwritten by normal operation of the control + property double resetValue: uniformValue.y + onResetValueChanged: value = resetValue + Layout.fillWidth: true Layout.minimumWidth: 30 Layout.maximumWidth: 60 @@ -62,7 +70,7 @@ RowLayout { value: uniformValue.y stepSize: .01 decimals: 2 - onValueChanged: uniformValue.y = value + onValueModified: uniformValue.y = value } Item { // spacer @@ -87,6 +95,10 @@ RowLayout { HelperWidgets.DoubleSpinBox { id: vZ + // value: uniformValue binding can get overwritten by normal operation of the control + property double resetValue: uniformValue.z + onResetValueChanged: value = resetValue + Layout.fillWidth: true Layout.minimumWidth: 30 Layout.maximumWidth: 60 @@ -98,7 +110,7 @@ RowLayout { value: uniformValue.z stepSize: .01 decimals: 2 - onValueChanged: uniformValue.z = value + onValueModified: uniformValue.z = value } Item { // spacer @@ -123,6 +135,10 @@ RowLayout { HelperWidgets.DoubleSpinBox { id: vW + // value: uniformValue binding can get overwritten by normal operation of the control + property double resetValue: uniformValue.w + onResetValueChanged: value = resetValue + Layout.fillWidth: true Layout.minimumWidth: 30 Layout.maximumWidth: 60 @@ -134,7 +150,7 @@ RowLayout { value: uniformValue.w stepSize: .01 decimals: 2 - onValueChanged: uniformValue.w = value + onValueModified: uniformValue.w = value } Item { // spacer diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DoubleSpinBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DoubleSpinBox.qml index a44e8c690be..11ce0e1e753 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DoubleSpinBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DoubleSpinBox.qml @@ -53,5 +53,6 @@ Item { decimals: 2 onRealValueModified: wrapper.valueModified() + onCompressedRealValueModified: wrapper.valueModified() } } diff --git a/src/plugins/effectcomposer/effectcomposeruniformsmodel.cpp b/src/plugins/effectcomposer/effectcomposeruniformsmodel.cpp index 492d5a9e806..c2c162a87ee 100644 --- a/src/plugins/effectcomposer/effectcomposeruniformsmodel.cpp +++ b/src/plugins/effectcomposer/effectcomposeruniformsmodel.cpp @@ -59,7 +59,9 @@ bool EffectComposerUniformsModel::setData(const QModelIndex &index, const QVaria int idx = value.toString().indexOf("file:"); QString path = idx > 0 ? updatedValue.right(updatedValue.size() - idx - 5) : updatedValue; - updatedValue = QUrl::fromLocalFile(path).toString(); + + if (idx == -1) + updatedValue = QUrl::fromLocalFile(path).toString(); uniform->setValue(updatedValue); g_propertyData.insert(uniform->name(), updatedValue); @@ -73,6 +75,14 @@ bool EffectComposerUniformsModel::setData(const QModelIndex &index, const QVaria return true; } +bool EffectComposerUniformsModel::resetData(int row) +{ + QModelIndex idx = index(row, 0); + QTC_ASSERT(idx.isValid(), return false); + + return setData(idx, idx.data(DefaultValueRole), ValueRole); +} + void EffectComposerUniformsModel::resetModel() { beginResetModel(); diff --git a/src/plugins/effectcomposer/effectcomposeruniformsmodel.h b/src/plugins/effectcomposer/effectcomposeruniformsmodel.h index 65b2d7b2f00..fc82194fdf3 100644 --- a/src/plugins/effectcomposer/effectcomposeruniformsmodel.h +++ b/src/plugins/effectcomposer/effectcomposeruniformsmodel.h @@ -20,6 +20,7 @@ public: int rowCount(const QModelIndex & parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex &index, const QVariant &value, int role) override; + Q_INVOKABLE bool resetData(int row); void resetModel(); diff --git a/src/plugins/effectcomposer/uniform.cpp b/src/plugins/effectcomposer/uniform.cpp index 98d5ffd336f..590b38b423b 100644 --- a/src/plugins/effectcomposer/uniform.cpp +++ b/src/plugins/effectcomposer/uniform.cpp @@ -94,7 +94,13 @@ void Uniform::setValue(const QVariant &newValue) { if (m_value != newValue) { m_value = newValue; + emit uniformValueChanged(); + + if (m_type == Type::Sampler) { + m_backendValue->setValue(newValue); + emit uniformBackendValueChanged(); + } } } From c22ad945185b72d2f7f6fad45745413cec22634d Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 21 Mar 2024 22:12:22 +0100 Subject: [PATCH 057/202] QmlDesigner: Invalid check should always come first Change-Id: I312b60dd3bf3ea043b0e0a63a56eaf1d686fa5cf Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/designercore/model/bindingproperty.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp b/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp index 141548047e5..23c17dc61a3 100644 --- a/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp @@ -37,10 +37,11 @@ BindingProperty::BindingProperty(const PropertyName &propertyName, const Interna void BindingProperty::setExpression(const QString &expression) { - Internal::WriteLocker locker(model()); if (!isValid()) return; + Internal::WriteLocker locker(model()); + if (isDynamic()) qWarning() << "Calling BindingProperty::setExpression on dynamic property."; From 5f055fed2d05aab8571efb53ab910ebc85dd2824 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 22 Mar 2024 12:26:18 +0200 Subject: [PATCH 058/202] QmlDesigner: Render effected items with extra margin correctly Replaced old hardcoding of 40px margin for effects with actual sourceRect of the layer. Also check for effect first before clip, as that clip doesn't actually affect the clipped item itself, it only affects the children of the clipped item. Fixes: QDS-12303 Change-Id: Ia962c74cdefb16ed17bea34f0fc50649256d78fe Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../qml2puppet/instances/quickitemnodeinstance.cpp | 6 +++--- .../qml2puppet/instances/servernodeinstance.cpp | 11 +++++++++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/tools/qml2puppet/qml2puppet/instances/quickitemnodeinstance.cpp b/src/tools/qml2puppet/qml2puppet/instances/quickitemnodeinstance.cpp index 9d432fe0471..f2fbb97abab 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/quickitemnodeinstance.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/quickitemnodeinstance.cpp @@ -280,10 +280,10 @@ static bool layerEnabledAndEffect(QQuickItem *item) QRectF QuickItemNodeInstance::boundingRect() const { if (quickItem()) { - if (quickItem()->clip()) { - return quickItem()->boundingRect(); - } else if (layerEnabledAndEffect(quickItem())) { + if (layerEnabledAndEffect(quickItem())) { return ServerNodeInstance::effectAdjustedBoundingRect(quickItem()); + } else if (quickItem()->clip()) { + return quickItem()->boundingRect(); } else { QSize maximumSize(4000, 4000); auto isValidSize = [maximumSize] (const QRectF& rect) { diff --git a/src/tools/qml2puppet/qml2puppet/instances/servernodeinstance.cpp b/src/tools/qml2puppet/qml2puppet/instances/servernodeinstance.cpp index 758e18df359..ca05a5ef039 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/servernodeinstance.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/servernodeinstance.cpp @@ -26,6 +26,8 @@ #include +#include + #include #include #include @@ -116,8 +118,13 @@ bool ServerNodeInstance::isSubclassOf(QObject *object, const QByteArray &superTy QRectF ServerNodeInstance::effectAdjustedBoundingRect(QQuickItem *item) { - if (item) - return item->boundingRect().adjusted(-40, -40, 40, 40); + if (item) { + QQuickItemPrivate *pItem = QQuickItemPrivate::get(item); + if (pItem && pItem->layer() && pItem->layer()->sourceRect().isValid()) + return pItem->layer()->sourceRect(); + else + return item->boundingRect(); + } return {}; } From e4429401d51d0476fb5361f88245d43424f9702f Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 21 Mar 2024 15:14:57 +0200 Subject: [PATCH 059/202] EffectComposer: Add extraMargin property for generated effects Extra margin property is added to effects that can spill outside the source item. This property specifies the amount of space outside the item that the effect is allowed to use for rendering. Fixes: QDS-11607 Change-Id: I36d7392593faa6deb99726eaa02184aa87aa3571 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../EffectComposerPreview.qml | 7 +- .../effectcomposer/compositionnode.cpp | 3 + src/plugins/effectcomposer/compositionnode.h | 3 + .../effectcomposer/effectcomposermodel.cpp | 94 +++++++++++++++++-- .../effectcomposer/effectcomposermodel.h | 2 + 5 files changed, 99 insertions(+), 10 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerPreview.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerPreview.qml index c832b2f3708..aefffcc6bcc 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerPreview.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerPreview.qml @@ -21,6 +21,7 @@ Column { readonly property int updateDelay: 100 readonly property int previewMargin: 5 + readonly property int extraMargin: 200 property real previewScale: 1 @@ -246,6 +247,8 @@ Column { layer.enabled: true layer.mipmap: true layer.smooth: true + layer.sourceRect: Qt.rect(-root.extraMargin, -root.extraMargin, + width + root.extraMargin * 2, height + root.extraMargin * 2) visible: false Image { @@ -347,10 +350,6 @@ Column { width: source.width height: source.height anchors.centerIn: parent - // Cache the layer. This way heavy shaders rendering doesn't - // slow down code editing & rest of the UI. - layer.enabled: true - layer.smooth: true } } diff --git a/src/plugins/effectcomposer/compositionnode.cpp b/src/plugins/effectcomposer/compositionnode.cpp index 108eb5801d8..d939e2283af 100644 --- a/src/plugins/effectcomposer/compositionnode.cpp +++ b/src/plugins/effectcomposer/compositionnode.cpp @@ -113,6 +113,9 @@ void CompositionNode::parse(const QString &effectName, const QString &qenPath, c m_fragmentCode = EffectUtils::codeFromJsonArray(json.value("fragmentCode").toArray()); m_vertexCode = EffectUtils::codeFromJsonArray(json.value("vertexCode").toArray()); + if (json.contains("extraMargin")) + m_extraMargin = json.value("extraMargin").toInt(); + if (json.contains("enabled")) m_isEnabled = json["enabled"].toBool(); diff --git a/src/plugins/effectcomposer/compositionnode.h b/src/plugins/effectcomposer/compositionnode.h index b3348bb38fc..433468688a2 100644 --- a/src/plugins/effectcomposer/compositionnode.h +++ b/src/plugins/effectcomposer/compositionnode.h @@ -52,6 +52,8 @@ public: int decRefCount(); void setRefCount(int count); + int extraMargin() const { return m_extraMargin; } + signals: void uniformsModelChanged(); void isEnabledChanged(); @@ -70,6 +72,7 @@ private: QString m_id; bool m_isEnabled = true; int m_refCount = 0; + int m_extraMargin = 0; QList m_uniforms; diff --git a/src/plugins/effectcomposer/effectcomposermodel.cpp b/src/plugins/effectcomposer/effectcomposermodel.cpp index 092df2b3e4a..394b85aa552 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.cpp +++ b/src/plugins/effectcomposer/effectcomposermodel.cpp @@ -490,6 +490,8 @@ QJsonObject nodeToJson(const CompositionNode &node) nodeObject.insert("enabled", node.isEnabled()); nodeObject.insert("version", 1); nodeObject.insert("id", node.id()); + if (node.extraMargin()) + nodeObject.insert("extraMargin", node.extraMargin()); // Add properties QJsonArray propertiesArray; @@ -676,10 +678,44 @@ R"( )"; s += frameProp.arg(tr("Frame"), tr("This property allows explicit control of current animation frame.")); } + s += " }\n"; s += " }\n"; } + if (m_shaderFeatures.enabled(ShaderFeatures::Source) && m_extraMargin) { + QString generalSection = + R"( + Section { + caption: "%1" + width: parent.width + + SectionLayout { + PropertyLabel { + text: "%2" + tooltip: "%3" + } + + SecondColumnLayout { + SpinBox { + minimumValue: 0 + maximumValue: 1000 + decimals: 0 + stepSize: 1 + sliderIndicatorVisible: true + backendValue: backendValues.extraMargin + implicitWidth: StudioTheme.Values.singleControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + } + ExpandingSpacer {} + } + } + } +)"; + s += generalSection.arg(tr("General"), tr("Extra Margin"), + tr("This property specifies how much of extra space is reserved for the effect outside the parent geometry.")); + } + for (const auto &node : std::as_const(m_nodes)) { const QList uniforms = static_cast( node->uniformsModel())->uniforms(); @@ -739,8 +775,34 @@ Item { s += header; if (m_shaderFeatures.enabled(ShaderFeatures::Source)) { - s += " // This is the main source for the effect. Set internally to the current parent item. Do not modify.\n"; - s += " property Item source: null\n"; + QString sourceStr{ +R"( + // This is the main source for the effect. Set internally to the current parent item. Do not modify. + property Item source: null +)" + }; + + QString extraMarginStr{ +R"( + // This property specifies how much of extra space is reserved for the effect outside the parent geometry. + // It should be sufficient for most use cases but if the application uses extreme values it may be necessary to + // increase this value. + property int extraMargin: %1 + + onExtraMarginChanged: setupSourceRect() + + function setupSourceRect() { + if (rootItem.source) { + var width = source.width + extraMargin * 2 + var height = source.height + extraMargin * 2 + source.layer.sourceRect = Qt.rect(-extraMargin, -extraMargin, width, height) + } + } +)" + }; + s += sourceStr; + if (m_extraMargin) + s += extraMarginStr.arg(m_extraMargin); } if (m_shaderFeatures.enabled(ShaderFeatures::Time) || m_shaderFeatures.enabled(ShaderFeatures::Frame)) { @@ -773,6 +835,7 @@ R"( parent.layer.effect = effectComponent } %1 + %3 } } @@ -781,6 +844,7 @@ R"( parent.layer.enabled = true parent.layer.effect = effectComponent source = parent + %3 } else { parent.layer.enabled = false parent.layer.effect = null @@ -805,10 +869,13 @@ R"( parentChanged = parentChanged.arg(mipmap1, mipmap2); } - parentChanged = parentChanged.arg(m_shaderFeatures.enabled(ShaderFeatures::Source) - ? QString("source = parent") : QString(), - m_shaderFeatures.enabled(ShaderFeatures::Source) - ? QString("source = null") : QString()); + if (m_shaderFeatures.enabled(ShaderFeatures::Source)) { + parentChanged = parentChanged.arg(QString("source = parent"), + QString("source = null"), + m_extraMargin ? QString("setupSourceRect()") : QString()); + } else { + parentChanged = parentChanged.arg(QString(), QString(), QString()); + } s += parentChanged; // Custom properties @@ -854,6 +921,8 @@ void EffectComposerModel::saveComposition(const QString &name) return; } + updateExtraMargin(); + QJsonObject json; // File format version json.insert("version", 1); @@ -1844,6 +1913,12 @@ QString EffectComposerModel::getQmlComponentString(bool localFiles) s += l2 + "vertexShader: 'file:///" + vertFile + "'\n"; s += l2 + "fragmentShader: 'file:///" + fragFile + "'\n"; s += l2 + "anchors.fill: " + (localFiles ? "rootItem.source" : "parent") + "\n"; + if (localFiles) { + if (m_extraMargin) + s += l2 + "anchors.margins: -rootItem.extraMargin\n"; + } else { + s += l2 + "anchors.margins: -root.extraMargin\n"; + } if (m_shaderFeatures.enabled(ShaderFeatures::GridMesh)) { QString gridSize = QString("%1, %2").arg(m_shaderFeatures.gridMeshWidth()) .arg(m_shaderFeatures.gridMeshHeight()); @@ -1879,6 +1954,13 @@ void EffectComposerModel::connectCompositionNode(CompositionNode *node) }); } +void EffectComposerModel::updateExtraMargin() +{ + m_extraMargin = 0; + for (CompositionNode *node : std::as_const(m_nodes)) + m_extraMargin = qMax(node->extraMargin(), m_extraMargin); +} + QString EffectComposerModel::currentComposition() const { return m_currentComposition; diff --git a/src/plugins/effectcomposer/effectcomposermodel.h b/src/plugins/effectcomposer/effectcomposermodel.h index 3dca41abf9e..e92213d12a5 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.h +++ b/src/plugins/effectcomposer/effectcomposermodel.h @@ -181,6 +181,7 @@ private: QString getDesignerSpecifics() const; void connectCompositionNode(CompositionNode *node); + void updateExtraMargin(); QList m_nodes; @@ -218,6 +219,7 @@ private: bool m_hasValidTarget = false; QString m_currentComposition; QTimer m_rebakeTimer; + int m_extraMargin = 0; const QRegularExpression m_spaceReg = QRegularExpression("\\s+"); }; From 668c3cfb35ae5993a6343ef632566e7496b0b2de Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Fri, 22 Mar 2024 12:23:23 +0200 Subject: [PATCH 060/202] QmlDesigner: Modify tooltips and texts of the model editor Fixes: QDS-11758 Fixes: QDS-11895 Change-Id: I9d5f562b19fc9fff2a6a9d012ecdb6099c19409f Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../CollectionDetailsToolbar.qml | 10 +++++----- .../CollectionDetailsView.qml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml index 8b4c08f8458..9800a31f94b 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml @@ -49,7 +49,7 @@ Rectangle { id: addColumnLeftButton buttonIcon: StudioTheme.Constants.addcolumnleft_medium - tooltip: qsTr("Add property left") + tooltip: qsTr("Add column left") enabled: root.model.selectedColumn > -1 onClicked: addColumnDialog.popUp(root.model.selectedColumn) } @@ -58,7 +58,7 @@ Rectangle { id: addColumnRightButton buttonIcon: StudioTheme.Constants.addcolumnright_medium - tooltip: qsTr("Add property right") + tooltip: qsTr("Add column right") enabled: root.model.selectedColumn > -1 onClicked: addColumnDialog.popUp(root.model.selectedColumn + 1) } @@ -67,7 +67,7 @@ Rectangle { id: deleteColumnButton buttonIcon: StudioTheme.Constants.deletecolumn_medium - tooltip: qsTr("Delete selected property") + tooltip: qsTr("Delete selected column") enabled: root.model.selectedColumn > -1 onClicked: root.model.removeColumn(root.model.selectedColumn) } @@ -81,7 +81,7 @@ Rectangle { id: addRowBelowButton buttonIcon: StudioTheme.Constants.addrowbelow_medium - tooltip: qsTr("Insert row below") + tooltip: qsTr("Add row below") enabled: root.model.selectedRow > -1 onClicked: root.model.insertRow(root.model.selectedRow + 1) } @@ -90,7 +90,7 @@ Rectangle { id: addRowAboveButton buttonIcon: StudioTheme.Constants.addrowabove_medium - tooltip: qsTr("Insert row above") + tooltip: qsTr("Add row above") enabled: root.model.selectedRow > -1 onClicked: root.model.insertRow(root.model.selectedRow) } diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml index c9a6d2dbcbd..3cde00d0a48 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml @@ -369,7 +369,7 @@ Rectangle { Text { anchors.centerIn: parent - text: qsTr("Select a model to continue") + text: qsTr("There are no models in this project.\nAdd or import a model.") visible: !topRow.visible color: StudioTheme.Values.themeTextColor font.pixelSize: StudioTheme.Values.mediumFontSize From 130d7c69ecc13f698b35e5062afc484f585f03ab Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Fri, 22 Mar 2024 11:14:39 +0200 Subject: [PATCH 061/202] QmlDesigner: Improve selecting and viewing the added rows/columns Fixes: QDS-11762 Fixes: QDS-12058 Fixes: QDS-12066 Fixes: QDS-12159 Change-Id: I81efce31fd6f6e48d4109cb397435972f562ae14 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../CollectionDetailsView.qml | 138 ++++++++++++++++-- .../collectiondetailsmodel.cpp | 11 +- 2 files changed, 128 insertions(+), 21 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml index 3cde00d0a48..fc8f92f9b77 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml @@ -26,6 +26,7 @@ Rectangle { Column { id: topRow + readonly property real maxAvailableHeight: root.height visible: root.model.collectionName !== "" width: parent.width @@ -39,6 +40,11 @@ Rectangle { } GridLayout { + id: gridLayout + readonly property real maxAvailableHeight: topRow.maxAvailableHeight + - topRow.spacing + - toolbar.height + columns: 3 rowSpacing: 1 columnSpacing: 1 @@ -184,18 +190,32 @@ Rectangle { TableView { id: tableView - model: root.sortedModel + model: root.model clip: true - property point tableStart: tableTopLeftCorner.mapToItem(root, Qt.point(x, y)); + readonly property real maxAvailableHeight: gridLayout.maxAvailableHeight + - addRowButton.height + - headerView.height + - (2 * gridLayout.rowSpacing) + readonly property real maxAvailableWidth: gridLayout.width + - StudioTheme.Values.collectionTableHorizontalMargin + - rowIdView.width + - addColumnButton.width + - gridLayout.columnSpacing + + property real childrenWidth: tableView.contentItem.childrenRect.width + property real childrenHeight: tableView.contentItem.childrenRect.height + + property int targetRow + property int targetColumn Layout.alignment: Qt.AlignTop + Qt.AlignLeft Layout.preferredWidth: tableView.contentWidth Layout.preferredHeight: tableView.contentHeight Layout.minimumWidth: 100 Layout.minimumHeight: 20 - Layout.maximumWidth: root.width - (tableStart.x + addColumnContainer.width) - Layout.maximumHeight: root.height - (tableStart.y + addRowContainer.height) + Layout.maximumWidth: maxAvailableWidth + Layout.maximumHeight: maxAvailableHeight columnWidthProvider: function(column) { if (!isColumnLoaded(column)) @@ -215,6 +235,47 @@ Rectangle { return Math.max(h, StudioTheme.Values.collectionCellMinimumHeight) } + function ensureRowIsVisible(row) { + let rows = tableView.model.rowCount() + if (row < 0 || row >= rows) { + tableView.targetRow = -1 + return + } + + if (tableView.isRowLoaded(row)) { + tableView.positionViewAtRow(row, Qt.AlignLeft | Qt.AlignTop) + tableView.targetRow = -1 + return + } + + tableView.targetRow = row + verticalScrollBar.position = row / rows + ensureTimer.start() + } + + function ensureColumnIsVisible(column) { + let columns = tableView.model.columnCount() + if (column < 0 || column >= columns) { + tableView.targetColumn = -1 + return + } + + if (tableView.isColumnLoaded(column)) { + tableView.positionViewAtColumn(column, Qt.AlignLeft | Qt.AlignTop) + tableView.targetColumn = -1 + return + } + + tableView.targetColumn = column + horizontalScrollBar.position = column / columns + ensureTimer.start() + } + + onMaxAvailableHeightChanged: resetSizeTimer.start() + onMaxAvailableWidthChanged: resetSizeTimer.start() + onChildrenWidthChanged: resetSizeTimer.start() + onChildrenHeightChanged: resetSizeTimer.start() + delegate: Rectangle { id: itemCell @@ -298,14 +359,67 @@ Rectangle { left: itemCell.left } } + } - Connections { - target: tableView.model + Timer { + id: resetSizeTimer - function onModelReset() { - tableView.clearColumnWidths() - tableView.clearRowHeights() - } + interval: 100 + repeat: false + onTriggered: { + let cWidth = Math.min(tableView.maxAvailableWidth, tableView.childrenWidth) + let cHeight = Math.min(tableView.maxAvailableHeight, tableView.childrenHeight) + + if (tableView.contentWidth != cWidth || tableView.contentHeight != cHeight) + tableView.returnToBounds() + } + } + + Timer { + id: ensureTimer + + interval: 100 + repeat: false + onTriggered: { + tableView.ensureRowIsVisible(tableView.targetRow) + tableView.ensureColumnIsVisible(tableView.targetColumn) + } + } + + Connections { + target: tableView.model + + function onModelReset() { + tableView.clearColumnWidths() + tableView.clearRowHeights() + } + + function onRowsInserted(parent, first, last) { + tableView.closeEditor() + tableView.model.selectRow(first) + tableView.ensureRowIsVisible(first) + } + + function onColumnsInserted(parent, first, last) { + tableView.closeEditor() + tableView.model.selectColumn(first) + tableView.ensureColumnIsVisible(first) + } + + function onRowsRemoved(parent, first, last) { + let nextRow = first - 1 + if (nextRow < 0 && tableView.model.rowCount(parent) > 0) + nextRow = 0 + + tableView.model.selectRow(nextRow) + } + + function onColumnsRemoved(parent, first, last) { + let nextColumn = first - 1 + if (nextColumn < 0 && tableView.model.columnCount(parent) > 0) + nextColumn = 0 + + tableView.model.selectColumn(nextColumn) } } @@ -331,7 +445,7 @@ Rectangle { } HelperWidgets.IconButton { - id: addColumnContainer + id: addColumnButton iconSize:16 Layout.preferredWidth: 24 @@ -346,7 +460,7 @@ Rectangle { } HelperWidgets.IconButton { - id: addRowContainer + id: addRowButton iconSize:16 Layout.preferredWidth: tableView.width diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp index b26b1a845e9..1643dfc23e2 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp @@ -128,11 +128,10 @@ bool CollectionDetailsModel::insertRows(int row, int count, [[maybe_unused]] con row = qBound(0, row, rowCount()); - beginResetModel(); + beginInsertRows({}, row, row + count - 1); m_currentCollection.insertEmptyRows(row, count); - endResetModel(); + endInsertRows(); - selectRow(row); return true; } @@ -151,12 +150,6 @@ bool CollectionDetailsModel::removeColumns(int column, int count, const QModelIn if (!columnCount(parent)) removeRows(0, rowCount(parent), parent); - int nextColumn = column - 1; - if (nextColumn < 0 && columnCount(parent) > 0) - nextColumn = 0; - - selectColumn(nextColumn); - ensureSingleCell(); return columnsRemoved; } From 536e78789154348061f84eedb3a826ca4c814f77 Mon Sep 17 00:00:00 2001 From: Shrief Gabr Date: Fri, 22 Mar 2024 16:04:54 +0200 Subject: [PATCH 062/202] EffectComposer: Fix "Save comp with new name" button behavior Task-number: QDS-12023 Change-Id: Icc941c8c31738b06e6f898562236eca50fc82180 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../effectComposerQmlSources/EffectComposerTopBar.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml index 1912592f2fc..8f98ae25b25 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml @@ -48,7 +48,7 @@ Rectangle { style: StudioTheme.Values.viewBarButtonStyle buttonIcon: StudioTheme.Constants.saveAs_medium tooltip: qsTr("Save current composition with a new name") - enabled: root.backendModel ? root.backendModel.isEnabled && !root.backendModel.isEmpty + enabled: root.backendModel ? root.backendModel.isEnabled && root.backendModel.currentComposition !== "" : false onClicked: root.saveAsClicked() From ef1f4793e2582c8fd1a93c18704b1a981f1afc5a Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 25 Mar 2024 10:33:57 +0100 Subject: [PATCH 063/202] QmlDesigner: Make Qt 6.7 the default for 3D Change-Id: I8ef47e3457d0c55ef47a4a98389da4aa2b015634 Reviewed-by: Tim Jenssen --- .../studio_templates/projects/application-3d/wizard.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 5b4d910b718..cb885c55e33 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json @@ -236,7 +236,7 @@ "type": "ComboBox", "data": { - "index": 3, + "index": 5, "items": [ { From 96cd6bd73803d714de7ccb19ce5bb839d9073cc1 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 25 Mar 2024 14:07:07 +0100 Subject: [PATCH 064/202] QmlDesigner: Allow any QtQuick version Change-Id: Iec353fb6103ae39a1c75ed2695692541a42265df Reviewed-by: Tim Jenssen --- .../designercore/model/texttomodelmerger.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index f7be6cf5e46..4aff7020fe0 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -69,13 +69,11 @@ bool isSupportedAttachedProperties(const QString &propertyName) bool isSupportedVersion(QmlDesigner::Version version) { - if (version.major == 2) - return version.minor <= 15; + /*In Qt Design Studio we control the proposed versions in the wizard and the kit. + * Therefore we can assume that the version is always supported. + * / - if (version.major == 6) - return version.minor <= 6; - - return false; + return true; } bool isGlobalQtEnums(QStringView value) From b008794156f449b2e15e419138144b1439f43fbe Mon Sep 17 00:00:00 2001 From: Shrief Gabr Date: Mon, 25 Mar 2024 14:22:05 +0200 Subject: [PATCH 065/202] QmlDesigner: Change cell text color for better legibility - Cell text color now matches that of the column name. Task-number: QDS-12168 Change-Id: Ia3e53a448c57e2f76347e83076271944936f6c05 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../collectionEditorQmlSource/CollectionDetailsView.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml index fc8f92f9b77..f5e062b7a1b 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml @@ -325,7 +325,7 @@ Rectangle { Text { text: display ?? "" color: itemSelected ? StudioTheme.Values.themeInteraction - : StudioTheme.Values.themePlaceholderTextColorInteraction + : StudioTheme.Values.themeTextColor leftPadding: 5 topPadding: 3 bottomPadding: 3 From 2894cea520c70199fa3d80555769fe267004f944 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Fri, 22 Mar 2024 17:36:41 +0200 Subject: [PATCH 066/202] QmlDesigner: Improve Model editor dialogs * Dialogs will be closed after model changes * Some menus and dialogs moved for improving the performance issues Fixes: QDS-12057 Change-Id: Ie298732ec1d3c9e4623663cd539abfa1b119ff98 Reviewed-by: Mahmoud Badri --- .../CollectionDetailsToolbar.qml | 5 + .../CollectionDetailsView.qml | 7 +- .../CollectionItem.qml | 153 +------------- .../CollectionListView.qml | 198 ++++++++++++++++++ .../CollectionView.qml | 16 ++ 5 files changed, 233 insertions(+), 146 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml index 9800a31f94b..876e8456371 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml @@ -29,6 +29,11 @@ Rectangle { root.model.insertRow(root.model.rowCount()) } + function closeDialogs() { + addColumnDialog.reject() + fileDialog.reject() + } + RowLayout { id: container diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml index f5e062b7a1b..6420f197bd9 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml @@ -21,7 +21,9 @@ Rectangle { color: StudioTheme.Values.themeControlBackground function closeDialogs() { - editPropertyDialog.close() + editPropertyDialog.reject() + deleteColumnDialog.reject() + toolbar.closeDialogs() } Column { @@ -370,7 +372,7 @@ Rectangle { let cWidth = Math.min(tableView.maxAvailableWidth, tableView.childrenWidth) let cHeight = Math.min(tableView.maxAvailableHeight, tableView.childrenHeight) - if (tableView.contentWidth != cWidth || tableView.contentHeight != cHeight) + if (tableView.contentWidth !== cWidth || tableView.contentHeight !== cHeight) tableView.returnToBounds() } } @@ -390,6 +392,7 @@ Rectangle { target: tableView.model function onModelReset() { + root.closeDialogs() tableView.clearColumnWidths() tableView.clearRowHeights() } diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml index 31ced43c1dd..d963070536f 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml @@ -16,9 +16,17 @@ Item { implicitHeight: boundingRect.height + 3 property color textColor + readonly property string name: collectionName ?? "" + readonly property bool isSelected: collectionIsSelected + readonly property int id: index + + function rename(newName) { + collectionName = newName + } signal selectItem(int itemIndex) signal deleteItem() + signal contextMenuRequested() Item { id: boundingRect @@ -90,155 +98,12 @@ Item { MouseArea { anchors.fill: parent acceptedButtons: Qt.RightButton | Qt.LeftButton - onClicked: collectionMenu.popup() + onClicked: contextMenuRequested() } } } } - StudioControls.Menu { - id: collectionMenu - - closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside - - StudioControls.MenuItem { - text: qsTr("Delete") - shortcut: StandardKey.Delete - onTriggered: deleteDialog.open() - } - - StudioControls.MenuItem { - text: qsTr("Rename") - shortcut: StandardKey.Replace - onTriggered: renameDialog.open() - } - - StudioControls.MenuItem { - text: qsTr("Assign to the selected node") - enabled: CollectionEditorBackend.rootView.targetNodeSelected - onTriggered: rootView.assignCollectionToSelectedNode(collectionName) - } - } - - component Spacer: Item { - implicitWidth: 1 - implicitHeight: StudioTheme.Values.columnGap - } - - StudioControls.Dialog { - id: deleteDialog - - title: qsTr("Deleting the model") - clip: true - implicitWidth: 300 - - contentItem: ColumnLayout { - spacing: 2 - - Text { - Layout.fillWidth: true - - wrapMode: Text.WordWrap - color: StudioTheme.Values.themeTextColor - text: qsTr("Are you sure that you want to delete model \"%1\"?" - + "\nThe model will be deleted permanently.").arg(collectionName) - - } - - Spacer {} - - RowLayout { - spacing: StudioTheme.Values.sectionRowSpacing - Layout.alignment: Qt.AlignRight | Qt.AlignVCenter - - HelperWidgets.Button { - text: qsTr("Delete") - onClicked: root.deleteItem() - } - - HelperWidgets.Button { - text: qsTr("Cancel") - onClicked: deleteDialog.reject() - } - } - } - } - - StudioControls.Dialog { - id: renameDialog - - title: qsTr("Rename model") - - onAccepted: { - if (newNameField.text !== "") - collectionName = newNameField.text - } - - onOpened: { - newNameField.text = collectionName - } - - contentItem: ColumnLayout { - spacing: 2 - - Text { - text: qsTr("Previous name: " + collectionName) - color: StudioTheme.Values.themeTextColor - } - - Spacer {} - - Text { - Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - text: qsTr("New name:") - color: StudioTheme.Values.themeTextColor - } - - StudioControls.TextField { - id: newNameField - - Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - Layout.fillWidth: true - - actionIndicator.visible: false - translationIndicator.visible: false - validator: newNameValidator - - Keys.onEnterPressed: renameDialog.accept() - Keys.onReturnPressed: renameDialog.accept() - Keys.onEscapePressed: renameDialog.reject() - - onTextChanged: { - btnRename.enabled = newNameField.text !== "" - } - } - - Spacer {} - - RowLayout { - Layout.alignment: Qt.AlignRight | Qt.AlignVCenter - spacing: StudioTheme.Values.sectionRowSpacing - - HelperWidgets.Button { - id: btnRename - - text: qsTr("Rename") - onClicked: renameDialog.accept() - } - - HelperWidgets.Button { - text: qsTr("Cancel") - onClicked: renameDialog.reject() - } - } - } - } - - RegularExpressionValidator { - id: newNameValidator - regularExpression: /^\w+$/ - } - states: [ State { name: "default" diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionListView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionListView.qml index ef06a2e7a04..2b95abfc4f4 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionListView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionListView.qml @@ -2,6 +2,11 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import HelperWidgets 2.0 as HelperWidgets +import StudioControls 1.0 as StudioControls +import StudioTheme as StudioTheme import CollectionEditorBackend ListView { @@ -10,8 +15,201 @@ ListView { model: CollectionEditorBackend.model clip: true + function closeDialogs() { + currentCollection.dereference() + collectionMenu.close() + deleteDialog.reject() + renameDialog.reject() + } + delegate: CollectionItem { implicitWidth: root.width onDeleteItem: root.model.removeRow(index) + onContextMenuRequested: collectionMenu.openMenu(this) + } + + QtObject { + id: currentCollection + + property CollectionItem item + readonly property string name: item ? item.name : "" + readonly property bool selected: item ? item.isSelected : false + readonly property int index: item ? item.id : -1 + + function rename(newName) { + if (item) + item.rename(newName) + } + + function deleteItem() { + if (item) + item.deleteItem() + } + + function dereference() { + item = null + } + } + + StudioControls.Menu { + id: collectionMenu + + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside + + function openMenu(item) { + currentCollection.item = item + popup() + } + + StudioControls.MenuItem { + text: qsTr("Delete") + shortcut: StandardKey.Delete + onTriggered: deleteDialog.open() + } + + StudioControls.MenuItem { + text: qsTr("Rename") + shortcut: StandardKey.Replace + onTriggered: renameDialog.open() + } + + StudioControls.MenuItem { + text: qsTr("Assign to the selected node") + enabled: CollectionEditorBackend.rootView.targetNodeSelected + onTriggered: rootView.assignCollectionToSelectedNode(currentCollection.name) + } + } + + StudioControls.Dialog { + id: deleteDialog + + title: qsTr("Deleting the model") + clip: true + + onAccepted: currentCollection.deleteItem() + + contentItem: ColumnLayout { + id: deleteDialogContent // Keep the id here even if it's not used, because the dialog might lose implicitSize + + width: 300 + spacing: 2 + + Text { + Layout.fillWidth: true + + wrapMode: Text.WordWrap + color: StudioTheme.Values.themeTextColor + text: qsTr("Are you sure that you want to delete model \"%1\"?" + + "\nThe model will be deleted permanently.").arg(currentCollection.name) + + } + + Spacer {} + + RowLayout { + spacing: StudioTheme.Values.sectionRowSpacing + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + Layout.fillWidth: true + Layout.preferredHeight: 40 + + HelperWidgets.Button { + text: qsTr("Delete") + onClicked: deleteDialog.accept() + } + + HelperWidgets.Button { + text: qsTr("Cancel") + onClicked: deleteDialog.reject() + } + } + } + } + + StudioControls.Dialog { + id: renameDialog + + title: qsTr("Rename model") + + onAccepted: { + if (newNameField.text !== "") + currentCollection.rename(newNameField.text) + } + + onOpened: { + newNameField.text = currentCollection.name + } + + contentItem: ColumnLayout { + spacing: 2 + + Text { + text: qsTr("Previous name: " + currentCollection.name) + color: StudioTheme.Values.themeTextColor + } + + Spacer {} + + Text { + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + text: qsTr("New name:") + color: StudioTheme.Values.themeTextColor + } + + StudioControls.TextField { + id: newNameField + + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + Layout.fillWidth: true + + actionIndicator.visible: false + translationIndicator.visible: false + validator: newNameValidator + + Keys.onEnterPressed: renameDialog.accept() + Keys.onReturnPressed: renameDialog.accept() + Keys.onEscapePressed: renameDialog.reject() + + onTextChanged: { + btnRename.enabled = newNameField.text !== "" + } + } + + Spacer {} + + RowLayout { + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + spacing: StudioTheme.Values.sectionRowSpacing + + HelperWidgets.Button { + id: btnRename + + text: qsTr("Rename") + onClicked: renameDialog.accept() + } + + HelperWidgets.Button { + text: qsTr("Cancel") + onClicked: renameDialog.reject() + } + } + } + } + + Connections { + target: root.model + + function onModelReset() { + root.closeDialogs() + } + } + + RegularExpressionValidator { + id: newNameValidator + regularExpression: /^\w+$/ + } + + component Spacer: Item { + implicitWidth: 1 + implicitHeight: StudioTheme.Values.columnGap } } diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml index acf82fe4527..9d483037ac8 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml @@ -28,6 +28,12 @@ Item { print("TODO: deleteSelectedCollection") } + function closeDialogs() { + importDialog.reject() + newCollection.reject() + warningDialog.reject() + } + ImportDialog { id: importDialog @@ -147,6 +153,8 @@ Item { } CollectionListView { // Model Groups + id: collectionListView + Layout.fillWidth: true Layout.minimumHeight: bottomSpacer.isExpanded ? 150 : 0 Layout.fillHeight: !bottomSpacer.isExpanded @@ -187,4 +195,12 @@ Item { SplitView.fillWidth: true } } + + Connections { + target: root.model + + function onModelReset() { + root.closeDialogs() + } + } } From 12785d4eee58570e0b93349a023aef892b021ea8 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 25 Mar 2024 16:23:23 +0100 Subject: [PATCH 067/202] QmlDesigner: Fix syntax error by removing function It is not needed anymore and can be removed. Change-Id: I610529e8e02797813d2cf76c047b89525505178f Reviewed-by: Tim Jenssen --- .../designercore/model/texttomodelmerger.cpp | 56 +++++-------------- 1 file changed, 15 insertions(+), 41 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index 4aff7020fe0..3820322997a 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -67,15 +67,6 @@ bool isSupportedAttachedProperties(const QString &propertyName) || propertyName.startsWith(QLatin1String("InsightCategory.")); } -bool isSupportedVersion(QmlDesigner::Version version) -{ - /*In Qt Design Studio we control the proposed versions in the wizard and the kit. - * Therefore we can assume that the version is always supported. - * / - - return true; -} - bool isGlobalQtEnums(QStringView value) { static constexpr auto list = Utils::to_array( @@ -120,12 +111,6 @@ bool isKnownEnumScopes(QStringView value) != std::end(list); } -bool supportedQtQuickVersion(const QmlDesigner::Import &import) -{ - auto version = import.toVersion(); - return version.isEmpty() || isSupportedVersion(version); -} - QString stripQuotes(const QString &str) { if ((str.startsWith(QLatin1Char('"')) && str.endsWith(QLatin1Char('"'))) @@ -2230,42 +2215,31 @@ void TextToModelMerger::collectImportErrors(QList *errors) bool hasQtQuick = false; for (const QmlDesigner::Import &import : m_rewriterView->model()->imports()) { if (import.isLibraryImport() && import.url() == u"QtQuick") { - if (supportedQtQuickVersion(import)) { - hasQtQuick = true; + hasQtQuick = true; - auto &externalDependencies = m_rewriterView->externalDependencies(); - if (externalDependencies.hasStartupTarget()) { - const bool qt6import = !import.hasVersion() || import.majorVersion() == 6; + auto &externalDependencies = m_rewriterView->externalDependencies(); + if (externalDependencies.hasStartupTarget()) { + const bool qt6import = !import.hasVersion() || import.majorVersion() == 6; - if (!externalDependencies.isQt6Import() && (m_hasVersionlessImport || qt6import)) { - const QmlJS::DiagnosticMessage diagnosticMessage( - QmlJS::Severity::Error, - SourceLocation(0, 0, 0, 0), - QCoreApplication::translate( - "QmlDesigner::TextToModelMerger", - "Qt Quick 6 is not supported with a Qt 5 kit.")); - errors->prepend( - DocumentMessage(diagnosticMessage, - QUrl::fromLocalFile(m_document->fileName().path()))); - } - } else { + if (!externalDependencies.isQt6Import() && (m_hasVersionlessImport || qt6import)) { const QmlJS::DiagnosticMessage diagnosticMessage( QmlJS::Severity::Error, SourceLocation(0, 0, 0, 0), - QCoreApplication::translate("QmlDesigner::TextToModelMerger", - "The Design Mode requires a valid Qt kit.")); + QCoreApplication::translate( + "QmlDesigner::TextToModelMerger", + "Qt Quick 6 is not supported with a Qt 5 kit.")); errors->prepend( DocumentMessage(diagnosticMessage, QUrl::fromLocalFile(m_document->fileName().path()))); } } else { - const QmlJS::DiagnosticMessage - diagnosticMessage(QmlJS::Severity::Error, - SourceLocation(0, 0, 0, 0), - QCoreApplication::translate("QmlDesigner::TextToModelMerger", - "Unsupported Qt Quick version.")); - errors->append(DocumentMessage(diagnosticMessage, - QUrl::fromLocalFile(m_document->fileName().path()))); + const QmlJS::DiagnosticMessage diagnosticMessage( + QmlJS::Severity::Error, + SourceLocation(0, 0, 0, 0), + QCoreApplication::translate("QmlDesigner::TextToModelMerger", + "The Design Mode requires a valid Qt kit.")); + errors->prepend(DocumentMessage(diagnosticMessage, + QUrl::fromLocalFile(m_document->fileName().path()))); } } } From c76c90b6019441e2ea6133411f2b4d14a8041669 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 20 Mar 2024 12:10:41 +0100 Subject: [PATCH 068/202] QmlDesigner: Add tracing for project storage updater Change-Id: I4579a256e4402a045d906a36940582b8e0ff196b Reviewed-by: Tim Jenssen Reviewed-by: --- .../projectstorage/projectstorage.h | 8 +- .../projectstoragepathwatchertypes.h | 43 ++ .../projectstorage/projectstoragetypes.h | 6 +- .../projectstorage/projectstorageupdater.cpp | 373 ++++++++++++++---- .../projectstorage/projectstorageupdater.h | 12 + .../projectstorage/qmldocumentparser.cpp | 18 + .../projectstorage/qmltypesparser.cpp | 53 ++- .../designercore/projectstorage/sourcepath.h | 6 + .../tracing/qmldesignertracing.cpp | 20 +- .../designercore/tracing/qmldesignertracing.h | 8 +- 10 files changed, 443 insertions(+), 104 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index aac67a3007d..45a349c22c2 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -3491,7 +3491,7 @@ private: NanotraceHR::Tracer tracer{"fetch imported type name id"_t, projectStorageCategory(), keyValue("imported type name", typeName), - keyValue("kind", to_underlying(kind))}; + keyValue("kind", kind)}; auto importedTypeNameId = selectImportedTypeNameIdStatement .template value(kind, id, typeName); @@ -3517,7 +3517,7 @@ private: auto typeId = fetchTypeId(typeNameId, kind); - tracer.end(keyValue("type id", typeId), keyValue("type name kind", to_underlying(kind))); + tracer.end(keyValue("type id", typeId), keyValue("type name kind", kind)); return typeId; } @@ -3533,7 +3533,7 @@ private: NanotraceHR::Tracer tracer{"fetch type id"_t, projectStorageCategory(), keyValue("type name id", typeNameId), - keyValue("type name kind", to_underlying(kind))}; + keyValue("type name kind", kind)}; TypeId typeId; if (kind == Storage::Synchronization::TypeNameKind::Exported) { @@ -3566,7 +3566,7 @@ private: using NanotraceHR::keyValue; auto dict = dictonary(keyValue("property type id", result.propertyTypeId), keyValue("property declaration id", result.propertyDeclarationId), - keyValue("property traits", to_underlying(result.propertyTraits))); + keyValue("property traits", result.propertyTraits)); convertToString(string, dict); } diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatchertypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatchertypes.h index a9185d91e83..04f11096bdf 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatchertypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatchertypes.h @@ -12,6 +12,28 @@ namespace QmlDesigner { enum class SourceType : int { Qml, QmlUi, QmlTypes, QmlDir, Directory }; +template +void convertToString(String &string, SourceType sourceType) +{ + switch (sourceType) { + case SourceType::Qml: + convertToString(string, "Qml"); + break; + case SourceType::QmlUi: + convertToString(string, "QmlUi"); + break; + case SourceType::QmlTypes: + convertToString(string, "QmlTypes"); + break; + case SourceType::QmlDir: + convertToString(string, "QmlDir"); + break; + case SourceType::Directory: + convertToString(string, "Directory"); + break; + } +} + class ProjectChunkId { public: @@ -46,6 +68,17 @@ public: friend bool operator<(ProjectChunkId first, ProjectPartId second) { return first.id < second; } friend bool operator<(ProjectPartId first, ProjectChunkId second) { return first < second.id; } + + template + friend void convertToString(String &string, const ProjectChunkId &id) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("project part id", id.id), + keyValue("source type", id.sourceType)); + + convertToString(string, dict); + } }; using ProjectChunkIds = std::vector; @@ -67,6 +100,16 @@ public: return first.id == second.id && first.sourceIds == second.sourceIds; } + template + friend void convertToString(String &string, const IdPaths &idPaths) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("id", idPaths.id), keyValue("source ids", idPaths.sourceIds)); + + convertToString(string, dict); + } + public: ProjectChunkId id; SourceIds sourceIds; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h index 76c7722d6cf..5f80505da3d 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h @@ -899,7 +899,7 @@ public: keyValue("traits", propertyDeclaration.traits), keyValue("type id", propertyDeclaration.typeId), keyValue("property type id", propertyDeclaration.propertyTypeId), - keyValue("kind", to_underlying(propertyDeclaration.kind))); + keyValue("kind", propertyDeclaration.kind)); convertToString(string, dict); } @@ -1100,7 +1100,7 @@ public: keyValue("enumeration declarations", type.enumerationDeclarations), keyValue("traits", type.traits), keyValue("source id", type.sourceId), - keyValue("change level", to_underlying(type.changeLevel)), + keyValue("change level", type.changeLevel), keyValue("default property name", type.defaultPropertyName)); convertToString(string, dict); @@ -1185,7 +1185,7 @@ public: auto dict = dictonary(keyValue("project source id", projectData.projectSourceId), keyValue("source id", projectData.sourceId), keyValue("module id", projectData.moduleId), - keyValue("file type", to_underlying(projectData.fileType))); + keyValue("file type", projectData.fileType)); convertToString(string, dict); } diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp index f197aad060d..6948117db69 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp @@ -12,6 +12,8 @@ #include "sourcepath.h" #include "sourcepathcache.h" +#include + #include #include @@ -21,6 +23,26 @@ #include namespace QmlDesigner { +constexpr auto category = ProjectStorageTracing::projectStorageUpdaterCategory; +using NanotraceHR::keyValue; +using Tracer = ProjectStorageTracing::Category::TracerType; + +template +void convertToString(String &string, const ProjectStorageUpdater::FileState &state) +{ + switch (state) { + case ProjectStorageUpdater::FileState::Changed: + convertToString(string, "Changed"); + break; + case ProjectStorageUpdater::FileState::NotChanged: + convertToString(string, "NotChanged"); + break; + case ProjectStorageUpdater::FileState::NotExists: + convertToString(string, "NotExists"); + break; + } +} + namespace { QStringList filterMultipleEntries(QStringList qmlTypes) @@ -110,10 +132,15 @@ SourceIds filterNotUpdatedSourceIds(SourceIds updatedSourceIds, SourceIds notUpd return filteredUpdatedSourceIds; } -void addSourceIds(SourceIds &sourceIds, const Storage::Synchronization::ProjectDatas &projectDatas) +void addSourceIds(SourceIds &sourceIds, + const Storage::Synchronization::ProjectDatas &projectDatas, + TracerLiteral message, + Tracer &tracer) { - for (const auto &projectData : projectDatas) + for (const auto &projectData : projectDatas) { + tracer.tick(message, keyValue("source id", projectData.sourceId)); sourceIds.push_back(projectData.sourceId); + } } Storage::Version convertVersion(LanguageUtils::ComponentVersion version) @@ -131,34 +158,71 @@ Storage::Synchronization::IsAutoVersion convertToIsAutoVersion(QmlDirParser::Imp void addDependencies(Storage::Imports &dependencies, SourceId sourceId, const QList &qmldirDependencies, - ProjectStorageInterface &projectStorage) + ProjectStorageInterface &projectStorage, + TracerLiteral message, + Tracer &tracer) { for (const QmlDirParser::Import &qmldirDependency : qmldirDependencies) { ModuleId moduleId = projectStorage.moduleId(Utils::PathString{qmldirDependency.module} + "-cppnative"); - dependencies.emplace_back(moduleId, Storage::Version{}, sourceId); + auto &import = dependencies.emplace_back(moduleId, Storage::Version{}, sourceId); + tracer.tick(message, keyValue("import", import)); } } +void addModuleExportedImport(Storage::Synchronization::ModuleExportedImports &imports, + ModuleId moduleId, + ModuleId exportedModuleId, + Storage::Version version, + Storage::Synchronization::IsAutoVersion isAutoVersion, + std::string_view moduleName, + std::string_view exportedModuleName) +{ + NanotraceHR::Tracer tracer{"add module exported imports"_t, + category(), + keyValue("module id", moduleId), + keyValue("exported module id", exportedModuleId), + keyValue("version", version), + keyValue("is auto version", isAutoVersion), + keyValue("module name", moduleName), + keyValue("exported module name", exportedModuleName)}; + + imports.emplace_back(moduleId, exportedModuleId, version, isAutoVersion); +} + void addModuleExportedImports(Storage::Synchronization::ModuleExportedImports &imports, ModuleId moduleId, ModuleId cppModuleId, + std::string_view moduleName, + std::string_view cppModuleName, const QList &qmldirImports, ProjectStorageInterface &projectStorage) { - for (const QmlDirParser::Import &qmldirImport : qmldirImports) { - ModuleId exportedModuleId = projectStorage.moduleId(Utils::PathString{qmldirImport.module}); - imports.emplace_back(moduleId, - exportedModuleId, - convertVersion(qmldirImport.version), - convertToIsAutoVersion(qmldirImport.flags)); + NanotraceHR::Tracer tracer{"add module exported imports"_t, + category(), + keyValue("cpp module id", cppModuleId), + keyValue("module id", moduleId)}; - ModuleId exportedCppModuleId = projectStorage.moduleId( - Utils::PathString{qmldirImport.module} + "-cppnative"); - imports.emplace_back(cppModuleId, - exportedCppModuleId, - Storage::Version{}, - Storage::Synchronization::IsAutoVersion::No); + for (const QmlDirParser::Import &qmldirImport : qmldirImports) { + Utils::PathString exportedModuleName{qmldirImport.module}; + ModuleId exportedModuleId = projectStorage.moduleId(exportedModuleName); + addModuleExportedImport(imports, + moduleId, + exportedModuleId, + convertVersion(qmldirImport.version), + convertToIsAutoVersion(qmldirImport.flags), + moduleName, + exportedModuleName); + + exportedModuleName += "-cppnative"; + ModuleId exportedCppModuleId = projectStorage.moduleId(exportedModuleName); + addModuleExportedImport(imports, + cppModuleId, + exportedCppModuleId, + Storage::Version{}, + Storage::Synchronization::IsAutoVersion::No, + cppModuleName, + exportedModuleName); } } @@ -184,6 +248,11 @@ void ProjectStorageUpdater::update(QStringList directories, QStringList qmlTypesPaths, const QString &propertyEditorResourcesPath) { + NanotraceHR::Tracer tracer{"update"_t, + category(), + keyValue("directories", directories), + keyValue("qml types paths", qmlTypesPaths)}; + Storage::Synchronization::SynchronizationPackage package; WatchedSourceIdsIds watchedSourceIds{Utils::span{directories}.size()}; NotUpdatedSourceIds notUpdatedSourceIds{Utils::span{directories}.size()}; @@ -215,11 +284,16 @@ void ProjectStorageUpdater::updateQmlTypes(const QStringList &qmlTypesPaths, if (qmlTypesPaths.empty()) return; + NanotraceHR::Tracer tracer{"update qmltypes file"_t, category()}; + ModuleId moduleId = m_projectStorage.moduleId("QML-cppnative"); for (const QString &qmlTypesPath : qmlTypesPaths) { SourceId sourceId = m_pathCache.sourceId(SourcePath{qmlTypesPath}); watchedSourceIdsIds.qmltypesSourceIds.push_back(sourceId); + tracer.tick("append watched qml types source id"_t, + keyValue("source id", sourceId), + keyValue("qml types path", qmlTypesPath)); Storage::Synchronization::ProjectData projectData{sourceId, sourceId, @@ -232,7 +306,9 @@ void ProjectStorageUpdater::updateQmlTypes(const QStringList &qmlTypesPaths, notUpdatedSourceIds); if (state == FileState::Changed) { + tracer.tick("append project data"_t, keyValue("project data", projectData)); package.projectDatas.push_back(std::move(projectData)); + tracer.tick("append updated project source ids"_t, keyValue("source id", sourceId)); package.updatedProjectSourceIds.push_back(sourceId); } } @@ -250,13 +326,86 @@ ProjectStorageUpdater::FileState combineState(FileStates... fileStates) return ProjectStorageUpdater::FileState::NotExists; } + } // namespace +void ProjectStorageUpdater::updateDirectoryChanged(std::string_view directoryPath, + FileState qmldirState, + SourcePath qmldirSourcePath, + SourceId qmldirSourceId, + SourceId directorySourceId, + SourceContextId directoryId, + Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIdsIds, + Tracer &tracer) +{ + QmlDirParser parser; + if (qmldirState != FileState::NotExists) + parser.parse(m_fileSystem.contentAsQString(QString{qmldirSourcePath})); + + if (qmldirState != FileState::NotChanged) { + tracer.tick("append updated source id"_t, keyValue("module id", qmldirSourceId)); + package.updatedSourceIds.push_back(qmldirSourceId); + } + + Utils::PathString moduleName{parser.typeNamespace()}; + ModuleId moduleId = m_projectStorage.moduleId(moduleName); + Utils::PathString cppModuleName = moduleName + "-cppnative"; + ModuleId cppModuleId = m_projectStorage.moduleId(cppModuleName); + ModuleId pathModuleId = m_projectStorage.moduleId(directoryPath); + + auto imports = filterMultipleEntries(parser.imports()); + + addModuleExportedImports(package.moduleExportedImports, + moduleId, + cppModuleId, + moduleName, + cppModuleName, + imports, + m_projectStorage); + tracer.tick("append updated module id"_t, keyValue("module id", moduleId)); + package.updatedModuleIds.push_back(moduleId); + + const auto qmlProjectDatas = m_projectStorage.fetchProjectDatas(directorySourceId); + addSourceIds(package.updatedSourceIds, qmlProjectDatas, "append updated source id"_t, tracer); + addSourceIds(package.updatedFileStatusSourceIds, + qmlProjectDatas, + "append updated file status source id"_t, + tracer); + + auto qmlTypes = filterMultipleEntries(parser.typeInfos()); + + if (!qmlTypes.isEmpty()) { + parseTypeInfos(qmlTypes, + filterMultipleEntries(parser.dependencies()), + imports, + directorySourceId, + directoryPath, + cppModuleId, + package, + notUpdatedSourceIds, + watchedSourceIdsIds); + } + parseQmlComponents( + createComponents(parser.components(), moduleId, pathModuleId, m_fileSystem, directoryPath), + directorySourceId, + directoryId, + package, + notUpdatedSourceIds, + watchedSourceIdsIds, + qmldirState); + tracer.tick("append updated project source id"_t, keyValue("module id", moduleId)); + package.updatedProjectSourceIds.push_back(directorySourceId); +} + void ProjectStorageUpdater::updateDirectories(const QStringList &directories, Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds, WatchedSourceIdsIds &watchedSourceIdsIds) { + NanotraceHR::Tracer tracer{"update directories"_t, category()}; + for (const QString &directory : directories) updateDirectory({directory}, package, notUpdatedSourceIds, watchedSourceIdsIds); } @@ -266,8 +415,10 @@ void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPa NotUpdatedSourceIds ¬UpdatedSourceIds, WatchedSourceIdsIds &watchedSourceIdsIds) { + NanotraceHR::Tracer tracer{"update directory"_t, category(), keyValue("directory", directoryPath)}; + SourcePath qmldirSourcePath{directoryPath + "/qmldir"}; - auto [directoryId, qmlDirSourceId] = m_pathCache.sourceContextAndSourceId(qmldirSourcePath); + auto [directoryId, qmldirSourceId] = m_pathCache.sourceContextAndSourceId(qmldirSourcePath); SourcePath directorySourcePath{directoryPath + "/."}; auto directorySourceId = m_pathCache.sourceId(directorySourcePath); @@ -275,62 +426,28 @@ void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPa if (directoryState != FileState::NotExists) watchedSourceIdsIds.directorySourceIds.push_back(directorySourceId); - auto qmldirState = fileState(qmlDirSourceId, package, notUpdatedSourceIds); + auto qmldirState = fileState(qmldirSourceId, package, notUpdatedSourceIds); if (qmldirState != FileState::NotExists) - watchedSourceIdsIds.qmldirSourceIds.push_back(qmlDirSourceId); + watchedSourceIdsIds.qmldirSourceIds.push_back(qmldirSourceId); switch (combineState(directoryState, qmldirState)) { case FileState::Changed: { - QmlDirParser parser; - if (qmldirState != FileState::NotExists) - parser.parse(m_fileSystem.contentAsQString(QString{qmldirSourcePath})); - - if (qmldirState != FileState::NotChanged) - package.updatedSourceIds.push_back(qmlDirSourceId); - - Utils::PathString moduleName{parser.typeNamespace()}; - ModuleId moduleId = m_projectStorage.moduleId(moduleName); - ModuleId cppModuleId = m_projectStorage.moduleId(moduleName + "-cppnative"); - ModuleId pathModuleId = m_projectStorage.moduleId(directoryPath); - - auto imports = filterMultipleEntries(parser.imports()); - - addModuleExportedImports(package.moduleExportedImports, - moduleId, - cppModuleId, - imports, - m_projectStorage); - package.updatedModuleIds.push_back(moduleId); - - const auto qmlProjectDatas = m_projectStorage.fetchProjectDatas(directorySourceId); - addSourceIds(package.updatedSourceIds, qmlProjectDatas); - addSourceIds(package.updatedFileStatusSourceIds, qmlProjectDatas); - - auto qmlTypes = filterMultipleEntries(parser.typeInfos()); - - if (!qmlTypes.isEmpty()) { - parseTypeInfos(qmlTypes, - filterMultipleEntries(parser.dependencies()), - imports, - directorySourceId, - directoryPath, - cppModuleId, - package, - notUpdatedSourceIds, - watchedSourceIdsIds); - } - parseQmlComponents( - createComponents(parser.components(), moduleId, pathModuleId, m_fileSystem, directoryPath), - directorySourceId, - directoryId, - package, - notUpdatedSourceIds, - watchedSourceIdsIds, - qmldirState); - package.updatedProjectSourceIds.push_back(directorySourceId); + tracer.tick("update directory changed"_t); + updateDirectoryChanged(directoryPath, + qmldirState, + qmldirSourcePath, + qmldirSourceId, + directorySourceId, + directoryId, + package, + notUpdatedSourceIds, + watchedSourceIdsIds, + tracer); break; } case FileState::NotChanged: { + tracer.tick("update directory not changed"_t); + parseProjectDatas(m_projectStorage.fetchProjectDatas(directorySourceId), package, notUpdatedSourceIds, @@ -338,19 +455,32 @@ void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPa break; } case FileState::NotExists: { + tracer.tick("update directory don't exits"_t); + package.updatedFileStatusSourceIds.push_back(directorySourceId); - package.updatedFileStatusSourceIds.push_back(qmlDirSourceId); + package.updatedFileStatusSourceIds.push_back(qmldirSourceId); package.updatedProjectSourceIds.push_back(directorySourceId); - package.updatedSourceIds.push_back(qmlDirSourceId); + package.updatedSourceIds.push_back(qmldirSourceId); auto qmlProjectDatas = m_projectStorage.fetchProjectDatas(directorySourceId); for (const Storage::Synchronization::ProjectData &projectData : qmlProjectDatas) { + tracer.tick("append updated source id"_t, keyValue("source id", projectData.sourceId)); package.updatedSourceIds.push_back(projectData.sourceId); + tracer.tick("append updated file status source id"_t, + keyValue("source id", projectData.sourceId)); package.updatedFileStatusSourceIds.push_back(projectData.sourceId); } break; } } + + tracer.end(keyValue("qmldir source path", qmldirSourcePath), + keyValue("directory source path", directorySourcePath), + keyValue("directory id", directoryId), + keyValue("qmldir source id", qmldirSourceId), + keyValue("directory source source id", directorySourceId), + keyValue("qmldir state", qmldirState), + keyValue("directory state", directoryState)); } void ProjectStorageUpdater::updatePropertyEditorPaths( @@ -358,6 +488,10 @@ void ProjectStorageUpdater::updatePropertyEditorPaths( Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds) { + NanotraceHR::Tracer tracer{"update property editor paths"_t, + category(), + keyValue("property editor resources path", propertyEditorResourcesPath)}; + if (propertyEditorResourcesPath.isEmpty()) return; @@ -390,6 +524,13 @@ void ProjectStorageUpdater::updatePropertyEditorPath( Storage::Synchronization::SynchronizationPackage &package, SourceId directorySourceId) { + NanotraceHR::Tracer tracer{"update property editor path"_t, + category(), + keyValue("directory path", directoryPath), + keyValue("directory source id", directorySourceId)}; + + tracer.tick("append updated property editor qml path source id"_t, + keyValue("source id", directorySourceId)); package.updatedPropertyEditorQmlPathSourceIds.push_back(directorySourceId); auto dir = QDir{directoryPath}; const auto fileInfos = dir.entryInfoList({"*Pane.qml", "*Specifics.qml"}, QDir::Files); @@ -402,6 +543,11 @@ void ProjectStorageUpdater::updatePropertyEditorFilePath( Storage::Synchronization::SynchronizationPackage &package, SourceId directorySourceId) { + NanotraceHR::Tracer tracer{"update property editor file path"_t, + category(), + keyValue("directory path", path), + keyValue("directory source id", directorySourceId)}; + QRegularExpression regex{R"xo(.+\/(\w+)\/(\w+)(Specifics|Pane).qml)xo"}; auto match = regex.match(path); QString oldModuleName; @@ -414,7 +560,12 @@ void ProjectStorageUpdater::updatePropertyEditorFilePath( } Storage::TypeNameString typeName{match.capturedView(2)}; SourceId pathId = m_pathCache.sourceId(SourcePath{path}); - package.propertyEditorQmlPaths.emplace_back(moduleId, typeName, pathId, directorySourceId); + const auto &paths = package.propertyEditorQmlPaths.emplace_back(moduleId, + typeName, + pathId, + directorySourceId); + tracer.tick("append property editor qml paths"_t, + keyValue("property editor qml paths", paths)); } } @@ -451,6 +602,10 @@ bool contains(const Container &container, Id id) void ProjectStorageUpdater::pathsWithIdsChanged(const std::vector &changedIdPaths) { + NanotraceHR::Tracer tracer{"paths with ids changed"_t, + category(), + keyValue("id paths", changedIdPaths)}; + m_changedIdPaths.insert(m_changedIdPaths.end(), changedIdPaths.begin(), changedIdPaths.end()); Storage::Synchronization::SynchronizationPackage package; @@ -543,21 +698,35 @@ void ProjectStorageUpdater::parseTypeInfos(const QStringList &typeInfos, NotUpdatedSourceIds ¬UpdatedSourceIds, WatchedSourceIdsIds &watchedSourceIds) { + NanotraceHR::Tracer tracer{"parse type infos"_t, + category(), + keyValue("directory source id", directorySourceId), + keyValue("directory path", directoryPath), + keyValue("module id", moduleId)}; + for (const QString &typeInfo : typeInfos) { + NanotraceHR::Tracer tracer{"parse type info"_t, category(), keyValue("type info", typeInfo)}; + Utils::PathString qmltypesPath = Utils::PathString::join( {directoryPath, "/", Utils::SmallString{typeInfo}}); SourceId sourceId = m_pathCache.sourceId(SourcePathView{qmltypesPath}); + tracer.tick("append qmltypes source id"_t, keyValue("source id", sourceId)); watchedSourceIds.qmltypesSourceIds.push_back(sourceId); addDependencies(package.moduleDependencies, sourceId, joinImports(qmldirDependencies, qmldirImports), - m_projectStorage); + m_projectStorage, + "append module dependency"_t, + tracer); + + tracer.tick("append module dependenct source source id"_t, keyValue("source id", sourceId)); package.updatedModuleDependencySourceIds.push_back(sourceId); auto projectData = package.projectDatas.emplace_back( directorySourceId, sourceId, moduleId, Storage::Synchronization::FileType::QmlTypes); + tracer.tick("append project data"_t, keyValue("source id", sourceId)); parseTypeInfo(projectData, qmltypesPath, package, notUpdatedSourceIds); } @@ -568,6 +737,8 @@ void ProjectStorageUpdater::parseProjectDatas(const Storage::Synchronization::Pr NotUpdatedSourceIds ¬UpdatedSourceIds, WatchedSourceIdsIds &watchedSourceIds) { + NanotraceHR::Tracer tracer{"parse project datas"_t, category()}; + for (const Storage::Synchronization::ProjectData &projectData : projectDatas) { switch (projectData.fileType) { case Storage::Synchronization::FileType::QmlTypes: { @@ -591,9 +762,14 @@ auto ProjectStorageUpdater::parseTypeInfo(const Storage::Synchronization::Projec Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds) -> FileState { + NanotraceHR::Tracer tracer{"parse type info"_t, + category(), + keyValue("qmltypes path", qmltypesPath)}; + auto state = fileState(projectData.sourceId, package, notUpdatedSourceIds); switch (state) { case FileState::Changed: { + tracer.tick("append updated source ids"_t, keyValue("source id", projectData.sourceId)); package.updatedSourceIds.push_back(projectData.sourceId); const auto content = m_fileSystem.contentAsQString(QString{qmltypesPath}); @@ -601,6 +777,7 @@ auto ProjectStorageUpdater::parseTypeInfo(const Storage::Synchronization::Projec break; } case FileState::NotChanged: { + tracer.tick("append not updated source ids"_t, keyValue("source id", projectData.sourceId)); notUpdatedSourceIds.sourceIds.push_back(projectData.sourceId); break; } @@ -608,6 +785,8 @@ auto ProjectStorageUpdater::parseTypeInfo(const Storage::Synchronization::Projec throw CannotParseQmlTypesFile{}; } + tracer.end(keyValue("state", state)); + return state; } @@ -620,6 +799,14 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil WatchedSourceIdsIds &watchedSourceIds, FileState qmldirState) { + NanotraceHR::Tracer tracer{"parse qml component"_t, + category(), + keyValue("relative file path", relativeFilePath), + keyValue("directory path", directoryPath), + keyValue("exported types", exportedTypes), + keyValue("directory source id", directorySourceId), + keyValue("qmldir state", qmldirState)}; + if (std::find(relativeFilePath.begin(), relativeFilePath.end(), '+') != relativeFilePath.end()) return; @@ -629,16 +816,18 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil Storage::Synchronization::Type type; auto state = fileState(sourceId, package, notUpdatedSourceIds); + tracer.tick("append watched qml source id"_t, keyValue("source id", sourceId)); watchedSourceIds.qmlSourceIds.push_back(sourceId); switch (state) { case FileState::NotChanged: if (qmldirState == FileState::NotExists) { + tracer.tick("append not updated source id"_t, keyValue("source id", sourceId)); notUpdatedSourceIds.sourceIds.emplace_back(sourceId); - package.projectDatas.emplace_back(directorySourceId, - sourceId, - ModuleId{}, - Storage::Synchronization::FileType::QmlDocument); + + const auto &projectData = package.projectDatas.emplace_back( + directorySourceId, sourceId, ModuleId{}, Storage::Synchronization::FileType::QmlDocument); + tracer.tick("append project data"_t, keyValue("project data", projectData)); return; } @@ -652,11 +841,11 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil break; } - package.projectDatas.emplace_back(directorySourceId, - sourceId, - ModuleId{}, - Storage::Synchronization::FileType::QmlDocument); + const auto &projectData = package.projectDatas.emplace_back( + directorySourceId, sourceId, ModuleId{}, Storage::Synchronization::FileType::QmlDocument); + tracer.tick("append project data"_t, keyValue("project data", projectData)); + tracer.tick("append updated source id"_t, keyValue("source id", sourceId)); package.updatedSourceIds.push_back(sourceId); type.typeName = SourcePath{qmlFilePath}.name(); @@ -664,6 +853,8 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil type.sourceId = sourceId; type.exportedTypes = std::move(exportedTypes); + tracer.end(keyValue("type", type)); + package.types.push_back(std::move(type)); } @@ -671,10 +862,13 @@ void ProjectStorageUpdater::parseQmlComponent(SourceId sourceId, Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds) { + NanotraceHR::Tracer tracer{"parse qml component"_t, category(), keyValue("source id", sourceId)}; + auto state = fileState(sourceId, package, notUpdatedSourceIds); if (state == FileState::NotChanged) return; + tracer.tick("append updated source id"_t, keyValue("source id", sourceId)); package.updatedSourceIds.push_back(sourceId); if (state == FileState::NotExists) @@ -690,6 +884,8 @@ void ProjectStorageUpdater::parseQmlComponent(SourceId sourceId, type.sourceId = sourceId; type.changeLevel = Storage::Synchronization::ChangeLevel::ExcludeExportedTypes; + tracer.end(keyValue("type", type)); + package.types.push_back(std::move(type)); } @@ -736,6 +932,12 @@ void ProjectStorageUpdater::parseQmlComponents(Components components, WatchedSourceIdsIds &watchedSourceIdsIds, FileState qmldirState) { + NanotraceHR::Tracer tracer{"parse qml components"_t, + category(), + keyValue("directory source id", directorySourceId), + keyValue("directory id", directoryId), + keyValue("qmldir state", qmldirState)}; + std::sort(components.begin(), components.end(), [](auto &&first, auto &&second) { return first.fileName < second.fileName; }); @@ -763,22 +965,37 @@ ProjectStorageUpdater::FileState ProjectStorageUpdater::fileState( Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds) const { + NanotraceHR::Tracer tracer{"update property editor paths"_t, + category(), + keyValue("source id", sourceId)}; + auto currentFileStatus = m_fileStatusCache.find(sourceId); if (!currentFileStatus.isValid()) { + tracer.tick("append updated file status source id"_t, keyValue("source id", sourceId)); package.updatedFileStatusSourceIds.push_back(sourceId); + + tracer.end(keyValue("state", FileState::NotExists)); return FileState::NotExists; } auto projectStorageFileStatus = m_projectStorage.fetchFileStatus(sourceId); if (!projectStorageFileStatus.isValid() || projectStorageFileStatus != currentFileStatus) { + tracer.tick("append file status"_t, keyValue("file status", sourceId)); package.fileStatuses.push_back(currentFileStatus); + + tracer.tick("append updated file status source id"_t, keyValue("source id", sourceId)); package.updatedFileStatusSourceIds.push_back(sourceId); + + tracer.end(keyValue("state", FileState::Changed)); return FileState::Changed; } + tracer.tick("append not updated file status source id"_t, keyValue("source id", sourceId)); notUpdatedSourceIds.fileStatusSourceIds.push_back(sourceId); + + tracer.end(keyValue("state", FileState::NotChanged)); return FileState::NotChanged; } diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h index 187a2219d09..c7794035659 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h @@ -11,6 +11,8 @@ #include "projectstoragetypes.h" #include "sourcepath.h" +#include + #include #include @@ -141,6 +143,16 @@ private: Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds, WatchedSourceIdsIds &watchedSourceIdsIds); + void updateDirectoryChanged(std::string_view directoryPath, + FileState qmldirState, + SourcePath qmldirSourcePath, + SourceId qmldirSourceId, + SourceId directorySourceId, + SourceContextId directoryId, + Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIdsIds, + ProjectStorageTracing::Category::TracerType &tracer); void updatePropertyEditorPaths(const QString &propertyEditorResourcesPath, Storage::Synchronization::SynchronizationPackage &package, diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp index f9eb8080f78..daa9062a571 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp @@ -10,6 +10,8 @@ #include +#include + #ifdef QDS_BUILD_QMLPARSER #include #endif @@ -21,6 +23,10 @@ namespace QmlDesigner { #ifdef QDS_BUILD_QMLPARSER +constexpr auto category = ProjectStorageTracing::projectStorageUpdaterCategory; +using NanotraceHR::keyValue; +using Tracer = ProjectStorageTracing::Category::TracerType; + namespace QmlDom = QQmlJS::Dom; namespace Synchronization = Storage::Synchronization; @@ -84,6 +90,11 @@ QualifiedImports createQualifiedImports(const QList &qmlImports, Utils::SmallStringView directoryPath, QmlDocumentParser::ProjectStorage &storage) { + NanotraceHR::Tracer tracer{"create qualified imports"_t, + category(), + keyValue("sourceId", sourceId), + keyValue("directoryPath", directoryPath)}; + QualifiedImports qualifiedImports; for (const QmlDom::Import &qmlImport : qmlImports) { @@ -92,6 +103,8 @@ QualifiedImports createQualifiedImports(const QList &qmlImports, createImport(qmlImport, sourceId, directoryPath, storage)); } + tracer.end(keyValue("qualified imports", qualifiedImports)); + return qualifiedImports; } @@ -280,6 +293,11 @@ Storage::Synchronization::Type QmlDocumentParser::parse(const QString &sourceCon SourceId sourceId, Utils::SmallStringView directoryPath) { + NanotraceHR::Tracer tracer{"qml document parser parse"_t, + category(), + keyValue("sourceId", sourceId), + keyValue("directoryPath", directoryPath)}; + Storage::Synchronization::Type type; using Option = QmlDom::DomEnvironment::Option; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp index 3768535299a..8179ff625b8 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp @@ -5,6 +5,8 @@ #include "projectstorage.h" +#include + #include #ifdef QDS_BUILD_QMLPARSER @@ -21,6 +23,10 @@ namespace QmlDesigner { #ifdef QDS_BUILD_QMLPARSER +constexpr auto category = ProjectStorageTracing::projectStorageUpdaterCategory; +using NanotraceHR::keyValue; +using Tracer = ProjectStorageTracing::Category::TracerType; + namespace QmlDom = QQmlJS::Dom; namespace { @@ -31,6 +37,8 @@ using Storage::TypeNameString; ComponentWithoutNamespaces createComponentNameWithoutNamespaces(const QList &objects) { + NanotraceHR::Tracer tracer{"parse qmltypes file"_t, category()}; + ComponentWithoutNamespaces componentWithoutNamespaces; for (const auto &object : objects) { @@ -46,13 +54,15 @@ ComponentWithoutNamespaces createComponentNameWithoutNamespaces(const QList components; QStringList dependencies; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/sourcepath.h b/src/plugins/qmldesigner/designercore/projectstorage/sourcepath.h index 837e58d48a7..b655c5cc345 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/sourcepath.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/sourcepath.h @@ -117,6 +117,12 @@ public: std::ptrdiff_t slashIndex() const { return m_slashIndex; } + template + friend void convertToString(String &string, const SourcePath &path) + { + convertToString(string, path.toStringView()); + } + private: std::ptrdiff_t m_slashIndex = -1; }; diff --git a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp index a22ef0ef746..b5798b713d2 100644 --- a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp +++ b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp @@ -68,14 +68,22 @@ Category &category() namespace ProjectStorageTracing { -NanotraceHR::StringViewWithStringArgumentsCategory &projectStorageCategory() +Category &projectStorageCategory() { - thread_local NanotraceHR::StringViewWithStringArgumentsCategory - projectStorageCategory_{"project storage"_t, - Tracing::eventQueueWithStringArguments(), - projectStorageCategory}; + thread_local Category category{"project storage"_t, + Tracing::eventQueueWithStringArguments(), + projectStorageCategory}; - return projectStorageCategory_; + return category; +} + +Category &projectStorageUpdaterCategory() +{ + thread_local Category category{"project storage updater"_t, + Tracing::eventQueueWithStringArguments(), + projectStorageCategory}; + + return category; } } // namespace ProjectStorageTracing diff --git a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h index 3a592d8a0c3..3a33834c708 100644 --- a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h +++ b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h @@ -55,7 +55,11 @@ constexpr NanotraceHR::Tracing projectStorageTracingStatus() #endif } -[[gnu::pure]] NanotraceHR::StringViewWithStringArgumentsCategory & -projectStorageCategory(); +using Category = NanotraceHR::StringViewWithStringArgumentsCategory; + +[[gnu::pure]] Category &projectStorageCategory(); + +[[gnu::pure]] Category &projectStorageUpdaterCategory(); + } // namespace ProjectStorageTracing } // namespace QmlDesigner From 41f3eaa3a9d8ef089f0dc7525f7a1d8ccb9c453e Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 21 Mar 2024 18:29:01 +0100 Subject: [PATCH 069/202] Sqlite: Fix prepare tracing Change-Id: I2b0c36789886d0924ac8130a4d46f3c13fc7518b Reviewed-by: Tim Jenssen Reviewed-by: Reviewed-by: Qt CI Patch Build Bot --- src/libs/sqlite/sqlitebasestatement.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/sqlite/sqlitebasestatement.cpp b/src/libs/sqlite/sqlitebasestatement.cpp index c904f3ae3ed..8557bf6ad2c 100644 --- a/src/libs/sqlite/sqlitebasestatement.cpp +++ b/src/libs/sqlite/sqlitebasestatement.cpp @@ -363,7 +363,7 @@ void BaseStatement::prepare(Utils::SmallStringView sqlStatement) { NanotraceHR::Tracer tracer{"prepare"_t, sqliteLowLevelCategory(), - keyValue("sqlite statement", handle())}; + keyValue("sql statement", sqlStatement)}; if (!m_database.isLocked()) throw DatabaseIsNotLocked{}; @@ -390,7 +390,7 @@ void BaseStatement::prepare(Utils::SmallStringView sqlStatement) if (resultCode != SQLITE_OK) Sqlite::throwError(resultCode, sqliteDatabaseHandle()); - tracer.end(keyValue("sql statement", handle())); + tracer.end(keyValue("sqlite statement", handle())); } sqlite3 *BaseStatement::sqliteDatabaseHandle() const From 4e63bcb744df8324551f28472ef6ff48d6bd1613 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Mon, 25 Mar 2024 15:45:47 +0200 Subject: [PATCH 070/202] QmlDesigner: Fix the bad access for the listModel of the model editor Change-Id: I9954e11135ab5913a82f12067291b04f22fbe5d2 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Ali Kianian Reviewed-by: Tim Jenssen --- .../collectioneditor/collectionview.cpp | 49 +++++++++++++------ .../collectioneditor/collectionview.h | 10 ++-- .../collectioneditor/collectionwidget.cpp | 5 +- .../collectioneditor/collectionwidget.h | 1 + 4 files changed, 45 insertions(+), 20 deletions(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp index f6ec821fded..b47fb6a51fe 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp @@ -60,14 +60,10 @@ CollectionView::CollectionView(ExternalDependenciesInterface &externalDependenci , m_dataStore(std::make_unique()) { - connect(ProjectExplorer::ProjectManager::instance(), - &ProjectExplorer::ProjectManager::startupProjectChanged, this, [this] { - resetDataStoreNode(); - if (m_widget.get()) - m_widget->collectionDetailsModel()->removeAllCollections(); - }); } +CollectionView::~CollectionView() = default; + bool CollectionView::hasWidget() const { return true; @@ -75,11 +71,16 @@ bool CollectionView::hasWidget() const QmlDesigner::WidgetInfo CollectionView::widgetInfo() { - if (m_widget.isNull()) { - m_widget = new CollectionWidget(this); + if (!m_widget) { + m_widget = Utils::makeUniqueObjectPtr(this); m_widget->setMinimumSize(m_widget->minimumSizeHint()); + connect(ProjectExplorer::ProjectManager::instance(), + &ProjectExplorer::ProjectManager::startupProjectChanged, m_widget.get(), [&] { + resetDataStoreNode(); + m_widget->collectionDetailsModel()->removeAllCollections(); + }); - auto collectionEditorContext = new Internal::CollectionEditorContext(m_widget.data()); + auto collectionEditorContext = new Internal::CollectionEditorContext(m_widget.get()); Core::ICore::addContextObject(collectionEditorContext); CollectionListModel *listModel = m_widget->listModel().data(); @@ -128,7 +129,7 @@ QmlDesigner::WidgetInfo CollectionView::widgetInfo() }); } - return createWidgetInfo(m_widget.data(), + return createWidgetInfo(m_widget.get(), "CollectionEditor", WidgetInfo::LeftPane, 0, @@ -150,12 +151,16 @@ void CollectionView::modelAboutToBeDetached([[maybe_unused]] Model *model) m_dataStoreTypeFound = false; disconnect(m_documentUpdateConnection); QTC_ASSERT(m_delayedTasks.isEmpty(), m_delayedTasks.clear()); - m_widget->listModel()->setDataStoreNode(); + if (m_widget) + m_widget->listModel()->setDataStoreNode(); } void CollectionView::selectedNodesChanged(const QList &selectedNodeList, [[maybe_unused]] const QList &lastSelectedNodeList) { + if (!m_widget) + return; + QList selectedCollectionNodes = Utils::filtered(selectedNodeList, &isStudioCollectionModel); @@ -170,10 +175,6 @@ void CollectionView::selectedNodesChanged(const QList &selectedNodeLi } m_widget->setTargetNodeSelected(singleSelectedHasModelProperty); - - // More than one model is selected. So ignore them - if (selectedCollectionNodes.size() > 1) - return; } void CollectionView::customNotification(const AbstractView *, @@ -181,6 +182,9 @@ void CollectionView::customNotification(const AbstractView *, const QList &nodeList, const QList &data) { + if (!m_widget) + return; + if (identifier == QLatin1String("item_library_created_by_drop") && !nodeList.isEmpty()) onItemLibraryNodeCreated(nodeList.first()); else if (identifier == QLatin1String("open_collection_by_id") && !data.isEmpty()) @@ -221,6 +225,9 @@ void CollectionView::addResource(const QUrl &url, const QString &name) void CollectionView::assignCollectionToNode(const QString &collectionName, const ModelNode &node) { + if (!m_widget) + return; + using DataType = CollectionDetails::DataType; executeInTransaction("CollectionView::assignCollectionToNode", [&]() { m_dataStore->assignCollectionToNode( @@ -279,12 +286,18 @@ void CollectionView::assignCollectionToSelectedNode(const QString &collectionNam void CollectionView::addNewCollection(const QString &collectionName, const QJsonObject &localCollection) { + if (!m_widget) + return; + addTask(QSharedPointer( new AddCollectionTask(this, m_widget->listModel(), localCollection, collectionName))); } void CollectionView::openCollection(const QString &collectionName) { + if (!m_widget) + return; + m_widget->openCollection(collectionName); } @@ -296,6 +309,9 @@ void CollectionView::registerDeclarativeType() void CollectionView::resetDataStoreNode() { + if (!m_widget) + return; + m_dataStore->reloadModel(); ModelNode dataStore = m_dataStore->modelNode(); @@ -395,6 +411,9 @@ void CollectionView::ensureStudioModelImport() void CollectionView::onItemLibraryNodeCreated(const ModelNode &node) { + if (!m_widget) + return; + if (node.metaInfo().isQtQuickListView()) { addTask(QSharedPointer( new DropListViewTask(this, m_widget->listModel(), node))); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h index a4b16c4c276..d8be8b7055c 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h @@ -3,9 +3,12 @@ #pragma once -#include "abstractview.h" #include "datastoremodelnode.h" -#include "modelnode.h" + +#include +#include + +#include #include @@ -27,6 +30,7 @@ class CollectionView : public AbstractView public: explicit CollectionView(ExternalDependenciesInterface &externalDependencies); + ~CollectionView(); bool hasWidget() const override; WidgetInfo widgetInfo() override; @@ -66,8 +70,8 @@ private: void onDocumentUpdated(const QSharedPointer &doc); void addTask(QSharedPointer task); - QPointer m_widget; std::unique_ptr m_dataStore; + Utils::UniqueObjectPtr m_widget; QSet m_expectedDocumentUpdates; QList> m_delayedTasks; QMetaObject::Connection m_documentUpdateConnection; diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp index 093729dc67b..7ecd54174aa 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp @@ -54,8 +54,7 @@ QString getPreferredCollectionName(const QUrl &url, const QString &collectionNam namespace QmlDesigner { CollectionWidget::CollectionWidget(CollectionView *view) - : QFrame() - , m_view(view) + : m_view(view) , m_listModel(new CollectionListModel) , m_collectionDetailsModel(new CollectionDetailsModel) , m_collectionDetailsSortFilterModel(std::make_unique()) @@ -104,6 +103,8 @@ CollectionWidget::CollectionWidget(CollectionView *view) QmlDesignerPlugin::trackWidgetFocusTime(this, Constants::EVENT_MODELEDITOR_TIME); } +CollectionWidget::~CollectionWidget() = default; + void CollectionWidget::contextHelp(const Core::IContext::HelpCallback &callback) const { if (m_view) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h index 0957bd81e0f..f06edd23231 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h @@ -25,6 +25,7 @@ class CollectionWidget : public QFrame public: CollectionWidget(CollectionView *view); + ~CollectionWidget(); void contextHelp(const Core::IContext::HelpCallback &callback) const; QPointer listModel() const; From aa64a62e2f28b466de9a8fff2fa02bb8fe3bb2b5 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Mon, 25 Mar 2024 17:26:01 +0200 Subject: [PATCH 071/202] QmlDesigner: Prevent rounding real numbers in Model Editor Fixes: QDS-12021 Change-Id: I28215cc7a6ae9c388b3654799ef848a8002b0f13 Reviewed-by: Mahmoud Badri Reviewed-by: --- .../CollectionDetailsEditDelegate.qml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml index 90b3021c960..fd969382e63 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml @@ -96,6 +96,16 @@ Item { if (realField.activeFocus) realField.contentItem.focus = true } + + textFromValue: function (value, locale) { + locale.numberOptions = Locale.OmitGroupSeparator + var decimals = realField.trailingZeroes ? realField.decimals : decimalCounter(realField.realValue) + if (decimals > 0) { + var text = Number(realField.realValue).toLocaleString(locale, 'f', decimals + 1) + return text.substring(0, text.length - 1) + } + return Number(realField.realValue).toLocaleString(locale, 'f', decimals) + } } } } From 01999a87dde8b64517a11dccee3d8abee4566ea9 Mon Sep 17 00:00:00 2001 From: Teea Poldsam Date: Thu, 21 Mar 2024 09:26:42 +0200 Subject: [PATCH 072/202] Doc: Add fly mode to 3D doc Added fly mode and organized information into subsections. Fixes: QDS-12033 Change-Id: Ib4fefa6df8be15821e727c7a676bf7389fceac4e Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../images/icons/camera_speed.png | Bin 0 -> 1641 bytes .../qtdesignstudio-3d-editor.qdoc | 52 +++++++++++++++++- 2 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 doc/qtdesignstudio/images/icons/camera_speed.png diff --git a/doc/qtdesignstudio/images/icons/camera_speed.png b/doc/qtdesignstudio/images/icons/camera_speed.png new file mode 100644 index 0000000000000000000000000000000000000000..6fcabb05c85d70af26310c5f353a971599f37b0b GIT binary patch literal 1641 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7I14-?iy0WWg+Z8+Vb&Z8 z1_qXQnIRD+5xzcF$@#f@i7EL>sd^Q;1q>iyV_#8_n4FzjqL7rDo|$K>^nUk#C56ls zTcvPQUjyF)=hTc$kE){7;3~h6MhI|Z8xtBTx$+|-gpg^Jvqyke^gTP3i$ zR(Zu%AYpwa1+bEmY+EHqkcA2nz5xo(`9-M;mU@P|$p!{S3g(u2DM^XODT&593PuJ- z#`=av`i9232Bua9W>zMa3Q(YAr(jc*l4cd;;s&*>C?(BSDWjyMz)D}gyu4hm+*mKa zC|%#s($Z4jz)0W7NVg~@O}Dr*uOzWTH?LS3W`avc7m0xRdD)WnkfqLBRj99T>Rz?`gLWU6PTpb?&#my%yztcj!{)g`ec)mF*K zz{o_`z*yJFG{nHd%E;Kt&_LV3$jZO~qSQA(B{QuOt5Q=dV>Ib3SVC<&%EN2#JuEGPZwLI zirfOd%*+%kGtXEz% zmP5+3Fy%omZXhPOd;yg;b_$3>EHy91R;ftI-Y&wrDxHCWQPI=IF~s8Z(#w1GTmnUo ze!TB0(!$B5BpENfMQGs%h7e)@1tRMU=Q%G9?%wZpzq+9pkNX;MtchOX=s#a{<%=TsNAm99yYVUP66ls_!^mvR0>vj%q^)&!p0 zGy0!T2{;+##jUvGFo)IQon7B!nobJD9WU2CD8^>kqAv1k&g@X$4Go$(39Rj<=jxfh zDLa-QiOx~da4lZ{it%X0ckQ^Op582x>KUDG*Jp9W?r@Pjztiy9>W0g1$s(Lu7rWCq zKXse*-M;Udl{)qGi-Sk&>y{;pM6sS>OL)(o#}c>u)Z9lhpvu70)z4*}Q$iB}R}mpM literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc index 65a63da3f2a..376f6bbf1f3 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc @@ -71,6 +71,8 @@ \section1 Controlling the 3D View Camera + \section2 Toggling Camera Mode + To switch to perspective camera mode, select \inlineimage perspective_camera.png (\uicontrol {Toggle Perspective/Orthographic Edit Camera}). @@ -78,14 +80,16 @@ \inlineimage orthographic_camera.png . You can also Toggle the camera mode by using the keyboard shortcut \key T. + \section2 Navigating in the 3D Scene + You can navigate the scene by panning, rotating, and zooming the 3D view camera: \list \li To pan, press \key Alt (or \key Option on \macos) and use the middle mouse button to click and drag anywhere in the rendered - view to slide the view around. - \note It is not possible to pan using Magic Mouse. + view to slide the view around. Alternatively, press and hold \key {right mouse + button} and \key {left mouse button} and drag anywhere in the view to pan. \li To orbit, press \key Alt and click and drag anywhere in the rendered view to rotate the view. \li To zoom, use the mouse wheel or press \key Alt and right-click @@ -125,6 +129,45 @@ independently. Navigate each split by panning, rotating, and zooming, as described above. + \section2 Using Fly Mode + + You can move around freely in the 3D scene with fly mode. To navigate the scene with + fly mode, keep the \key {right mouse button} pressed and use the listed keys to move + around the scene. + + \table + \header + \li Key + \li Action + \row + \li \key W or \key {Up arrow} + \li Move forward. + \row + \li \key S or \key {Down arrow} + \li Move backward. + \row + \li \key A or \key {Left arrow} + \li Move left. + \row + \li \key D or \key {Right arrow} + \li Move right. + \row + \li \key E or \key {Page up} + \li Move up. + \row + \li \key Q or \key {Page down} + \li Move down. + \endtable + + To adjust the movement speed, select \inlineimage icons/camera_speed.png in the + \uicontrol 3D view toolbar to open the configuration dialog. + + In the configuration dialog, you can: + \list + \li Adjust the movement speed of the camera with a slider. + \li Set a value multiplier for the speed slider. + \endlist + \section1 Using Global and Local Orientation To switch between local and global orientation, select @@ -498,6 +541,11 @@ \li Align View to Selected Camera \li \li \l{Aligning Views and Cameras} + \row + \li \inlineimage icons/camera_speed.png + \li Open camera speed configuration dialog + \li + \li\l{Using Fly Mode} \row \li \inlineimage icons/visibilityon.png \li Visibility Toggles From 33ec9d9690fb8bbaedbf3606b1e6b2e49f32ff83 Mon Sep 17 00:00:00 2001 From: Mats Honkamaa Date: Mon, 25 Mar 2024 10:22:27 +0200 Subject: [PATCH 073/202] Doc: Add topic on camera follow function - Added a topic on how to make a camera follow an object - Some minor fixes to the Cameras topic Fixes: QDS-11700 Change-Id: Ieafa9c57d5649d3de6f823cc907734ab6f24091d Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Johanna Vanhatapio --- .../images/camera-look-at-node.webp | Bin 0 -> 17124 bytes .../qtdesignstudio-3d-camera.qdoc | 31 ++++++++++++++---- 2 files changed, 25 insertions(+), 6 deletions(-) create mode 100644 doc/qtdesignstudio/images/camera-look-at-node.webp diff --git a/doc/qtdesignstudio/images/camera-look-at-node.webp b/doc/qtdesignstudio/images/camera-look-at-node.webp new file mode 100644 index 0000000000000000000000000000000000000000..e5a92308a32b35205d8dc38353b313fcc701848c GIT binary patch literal 17124 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-camera.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-camera.qdoc index 202249188d9..35fc2338c2e 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-camera.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-camera.qdoc @@ -8,13 +8,13 @@ \title Cameras - A camera is always necessary to view the content of a 3D scene. A camera + A camera is necessary to view the content of a 3D scene. A camera defines how to project the content of a 3D scene into a 2D coordinate space, which can then be used on a 2D surface. When a camera is present in the scene, it can be used to direct what is displayed in a \l {3D Views} {3D view}. - \image studio-qtquick-3d-components.png "Qt Quick 3D components in Components" + \image studio-qtquick-3d-components.png "Qt Quick 3D components in Components" To add a camera component to your UI, do one of the following: \list @@ -31,7 +31,7 @@ \uicontrol QtQuick3D module to your project, as described in \l {Adding and Removing Modules}. - You can use the following components in your scenes to determine camera + The following components in your scenes determine the camera projection: \list @@ -73,6 +73,8 @@ when \l {Creating Projects}{creating your project}, the camera properties will be slightly different. + \note Orthographic cameras don't have the FOV property. + The camera frustum can be obtained by taking a frustum (that is, a truncation with parallel planes) of the cone of vision that a camera or eye would have to the rectangular viewports typically used in computer graphics. @@ -112,10 +114,10 @@ \note The \uicontrol {Horizontal magnification} and \uicontrol {Vertical magnification} properties are not available in Qt 5. - The \uicontrol {Frustum culling enabled} property determines whether the + The \uicontrol {Frustum culling} property determines whether the objects outside the camera frustum will be culled, which means they will not be passed to the renderer. - \note The \uicontrol {Frustum culling enabled} property is not available in + \note The \uicontrol {Frustum culling} property is not available in Qt 5. The default values are intended to cause anything within the view @@ -124,5 +126,22 @@ better results with ambient occlusion or with effects that use the depth buffer of the camera, such as the \e {depth of field} effect. - \note Orthographic cameras don't have the FOV property. + \section1 Setting a Camera to Look at an Object + + Setting the camera to look at a specific object can be useful, for example, + if you want to create a cinematic effect or have the camera follow a user-controlled object. + + To set a camera to look at an object: + + \list 1 + \li In \uicontrol Navigator, select the camera. + \li In \uicontrol Properties > \uicontrol {Look-at-Node}, select the object that + the camera should look at. + \image camera-look-at-node.webp + \endlist + + When you set a camera to look at an object, the camera automatically rotates to + point toward the assigned object, even if the object or the camera moves. The rotation only + happens around the x and y axes. + */ From f63bd7f933829ca49d19d02cba294d7e0a3384a1 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 21 Mar 2024 14:46:40 +0100 Subject: [PATCH 074/202] QmlDesigner: Crash fix * Add QTC_CHECK for crash * ActionAreas should not be "garbage" collected if they have no target. * Initial ActionAreas do not have a target and ActionAreas without target make sense to a user. We do not want to delete them together with the target. Task-number: QDS-12181 Change-Id: Ie520c47aad990a8ff07fc3346e6772226d334ce5 Reviewed-by: Marco Bubke Reviewed-by: --- src/plugins/qmldesigner/designercore/include/qmlitemnode.h | 1 + .../qmldesigner/designercore/model/modelresourcemanagement.cpp | 3 +-- src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp | 1 - .../tests/unittests/model/modelresourcemanagement-test.cpp | 1 - 4 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/include/qmlitemnode.h b/src/plugins/qmldesigner/designercore/include/qmlitemnode.h index dde5515a5a0..e11f201cdb2 100644 --- a/src/plugins/qmldesigner/designercore/include/qmlitemnode.h +++ b/src/plugins/qmldesigner/designercore/include/qmlitemnode.h @@ -174,6 +174,7 @@ public: ModelNode targetTransition() const; void assignTargetFlowItem(const QmlFlowTargetNode &flowItem); QmlFlowItemNode flowItemParent() const; +private: void destroyTarget(); }; diff --git a/src/plugins/qmldesigner/designercore/model/modelresourcemanagement.cpp b/src/plugins/qmldesigner/designercore/model/modelresourcemanagement.cpp index a61f1001f96..27ac2e67abb 100644 --- a/src/plugins/qmldesigner/designercore/model/modelresourcemanagement.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelresourcemanagement.cpp @@ -515,8 +515,7 @@ struct BindingFilter struct TargetFilter { TargetFilter(NodeDependencies &dependencies, Model *model) - : flowViewFlowActionAreaMetaInfo{model->flowViewFlowActionAreaMetaInfo()} - , flowViewFlowTransitionMetaInfo{model->flowViewFlowTransitionMetaInfo()} + : flowViewFlowTransitionMetaInfo{model->flowViewFlowTransitionMetaInfo()} , qtQuickPropertyChangesMetaInfo{model->qtQuickPropertyChangesMetaInfo()} , qtQuickTimelineKeyframeGroupMetaInfo{model->qtQuickTimelineKeyframeGroupMetaInfo()} , qtQuickPropertyAnimationMetaInfo{model->qtQuickPropertyAnimationMetaInfo()} diff --git a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp index 826856428bf..c211a9e8fe0 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp @@ -748,7 +748,6 @@ void QmlFlowActionAreaNode::assignTargetFlowItem(const QmlFlowTargetNode &flowIt ModelNode transition = flowView.addTransition(flowParent.modelNode(), flowItem.modelNode()); - modelNode().bindingProperty("target").setExpression(transition.validId()); } diff --git a/tests/unit/tests/unittests/model/modelresourcemanagement-test.cpp b/tests/unit/tests/unittests/model/modelresourcemanagement-test.cpp index 40b94f872d5..fd3d3c70c35 100644 --- a/tests/unit/tests/unittests/model/modelresourcemanagement-test.cpp +++ b/tests/unit/tests/unittests/model/modelresourcemanagement-test.cpp @@ -280,7 +280,6 @@ INSTANTIATE_TEST_SUITE_P( ForTarget, testing::Values(TargetData{"QtQuick.Item", "QtQuick.PropertyChanges", "target"}, TargetData{"QtQuick.Item", "QtQuick.Timeline.KeyframeGroup", "target"}, - TargetData{"FlowView.FlowTransition", "FlowView.FlowActionArea", "target"}, TargetData{"QtQuick.Item", "QtQuick.PropertyAnimation", "target"}, TargetData{"FlowView.FlowItem", "FlowView.FlowTransition", "to"}, TargetData{"FlowView.FlowItem", "FlowView.FlowTransition", "from"})); From 3b1e3c56b0eb0c9e6f17fcd22cc947def3e505c5 Mon Sep 17 00:00:00 2001 From: Pranta Dastider Date: Mon, 25 Mar 2024 17:06:51 +0100 Subject: [PATCH 075/202] QmlDesigner: Update the Qt runtime versions document Qt Design Studio uses a specific Qt Runtime version to run the projects. This update include the Qt runtime versions used for QDS 4.5 releases in the document. Fixes: QDS-12201 Change-Id: I90bf2c952bef70840af382f03bc0ae47b43e3a0d Reviewed-by: Mats Honkamaa Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../src/qtdesignstudio-finding-qt-runtime-version.qdoc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/qtdesignstudio/src/qtdesignstudio-finding-qt-runtime-version.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-finding-qt-runtime-version.qdoc index cb9c7e86e99..5f2bc9bdada 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-finding-qt-runtime-version.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-finding-qt-runtime-version.qdoc @@ -34,5 +34,8 @@ \row \li 4.4 \li 6.6.2 + \row + \li 4.5 + \li 6.7.0 (with additional Quick3D features) \endtable */ From 3bbe1c0452aae9cae4577a51a72f75965b6102ea Mon Sep 17 00:00:00 2001 From: Pranta Dastider Date: Mon, 25 Mar 2024 15:31:39 +0100 Subject: [PATCH 076/202] QmlDesigner: Update Tooltips for Qt Quick Layout components This patch update tooltips for Qt Quick Layout compoents. It also update tooltips of the relative properties. Fixes: QDS-12283 Change-Id: I6c4d76602668dc7258cce7ff4fab4b547d8f5d0f Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Thomas Hartmann Reviewed-by: Mats Honkamaa --- .../QtQuick/Layouts/ColumnLayoutSpecifics.qml | 6 +++++- .../QtQuick/Layouts/GridLayoutSpecifics.qml | 21 +++++++++++++++---- .../QtQuick/Layouts/RowLayoutSpecifics.qml | 6 +++++- .../QtQuick/Layouts/StackLayoutSpecifics.qml | 5 ++++- .../componentsplugin/components.metainfo | 6 +++++- 5 files changed, 36 insertions(+), 8 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Layouts/ColumnLayoutSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Layouts/ColumnLayoutSpecifics.qml index 2072f13a8e3..aeaeacbd4ec 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Layouts/ColumnLayoutSpecifics.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Layouts/ColumnLayoutSpecifics.qml @@ -12,7 +12,10 @@ Section { caption: qsTr("Column Layout") SectionLayout { - PropertyLabel { text: qsTr("Column spacing") } + PropertyLabel { + text: qsTr("Column spacing") + tooltip: qsTr("Sets the space between the items in pixels in the Column Layout.") + } SecondColumnLayout { SpinBox { @@ -30,6 +33,7 @@ Section { PropertyLabel { text: qsTr("Layout direction") blockedByTemplate: !backendValues.layoutDirection.isAvailable + tooltip: qsTr("Sets the direction of the item flow in the Column Layout.") } SecondColumnLayout { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Layouts/GridLayoutSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Layouts/GridLayoutSpecifics.qml index 8eea1435422..e6606ffe00f 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Layouts/GridLayoutSpecifics.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Layouts/GridLayoutSpecifics.qml @@ -12,7 +12,10 @@ Section { caption: qsTr("Grid Layout") SectionLayout { - PropertyLabel { text: qsTr("Columns & Rows") } + PropertyLabel { + text: qsTr("Columns & Rows") + tooltip: qsTr("Sets the number of columns and rows in the Grid Layout.") + } SecondColumnLayout { SpinBox { @@ -49,7 +52,10 @@ Section { ExpandingSpacer {} } - PropertyLabel { text: qsTr("Spacing") } + PropertyLabel { + text: qsTr("Spacing") + tooltip: qsTr("Sets the space between the items in pixels in the rows and columns in the Grid Layout.") + } SecondColumnLayout { SpinBox { @@ -86,7 +92,10 @@ Section { ExpandingSpacer {} } - PropertyLabel { text: qsTr("Flow") } + PropertyLabel { + text: qsTr("Flow") + tooltip: qsTr("Set the direction of dynamic items to flow in rows or columns in the Grid Layout.") + } SecondColumnLayout { ComboBox { @@ -100,7 +109,11 @@ Section { ExpandingSpacer {} } - PropertyLabel { text: qsTr("Layout direction") } + PropertyLabel { + text: qsTr("Layout direction") + tooltip: qsTr("Sets the direction of the dynamic items left to right or right to left in the Grid Layout.") + + } SecondColumnLayout { ComboBox { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Layouts/RowLayoutSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Layouts/RowLayoutSpecifics.qml index b4a2ced1cdd..726b3783fbc 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Layouts/RowLayoutSpecifics.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Layouts/RowLayoutSpecifics.qml @@ -12,7 +12,10 @@ Section { caption: qsTr("Row Layout") SectionLayout { - PropertyLabel { text: qsTr("Row spacing") } + PropertyLabel { + text: qsTr("Row spacing") + tooltip: qsTr("Sets the space between the items in pixels in the Row Layout.") + } SecondColumnLayout { SpinBox { @@ -30,6 +33,7 @@ Section { PropertyLabel { text: qsTr("Layout direction") blockedByTemplate: !backendValues.layoutDirection.isAvailable + tooltip: qsTr("Sets the direction of the item flow in the Row Layout.") } SecondColumnLayout { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Layouts/StackLayoutSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Layouts/StackLayoutSpecifics.qml index 110a8cbf1a9..dc865bf1280 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Layouts/StackLayoutSpecifics.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Layouts/StackLayoutSpecifics.qml @@ -12,7 +12,10 @@ Section { caption: qsTr("Stack Layout") SectionLayout { - PropertyLabel { text: qsTr("Current index") } + PropertyLabel { + text: qsTr("Current index") + tooltip: qsTr("Sets the index of the child item currently visible in the Stack Layout.") + } SecondColumnLayout { SpinBox { diff --git a/src/plugins/qmldesigner/componentsplugin/components.metainfo b/src/plugins/qmldesigner/componentsplugin/components.metainfo index 8a1e365266e..b3d069dc1a2 100644 --- a/src/plugins/qmldesigner/componentsplugin/components.metainfo +++ b/src/plugins/qmldesigner/componentsplugin/components.metainfo @@ -12,6 +12,7 @@ MetaInfo { Property { name: "width"; type: "int"; value: 100; } Property { name: "height"; type: "int"; value: 100; } + tooltip: qsTr("Organizes items in a row.") } } @@ -28,6 +29,7 @@ MetaInfo { Property { name: "width"; type: "int"; value: 100; } Property { name: "height"; type: "int"; value: 100; } + tooltip: qsTr("Organizes items in a column.") } } @@ -44,6 +46,7 @@ MetaInfo { Property { name: "width"; type: "int"; value: 100; } Property { name: "height"; type: "int"; value: 100; } + tooltip: qsTr("Organizes items in a grid.") } } @@ -57,7 +60,7 @@ MetaInfo { } ItemLibraryEntry { - name: "StackLayout" + name: "Stack Layout" category: "Qt Quick - Layouts" libraryIcon: ":/componentsplugin/images/stack-layouts-icon.png" version: "1.0" @@ -65,6 +68,7 @@ MetaInfo { Property { name: "width"; type: "int"; value: 100; } Property { name: "height"; type: "int"; value: 100; } + tooltip: qsTr("Organizes items in a grid. Only the top item is visible.") } } } From fd3047bac2c551e6c1bcfa1a4a6d5562e8e67d35 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 22 Mar 2024 14:42:00 +0200 Subject: [PATCH 077/202] QmlDesigner: Join sleeping task queue thread Threads are still considered joinable after their execution. So sleeping thread have to be joinded first. Add test Fixes: QDS-12308 Change-Id: Ie01588293e3b7ce9fe149d52a25ba03d174cca88 Reviewed-by: Reviewed-by: Tim Jenssen --- .../designercore/imagecache/taskqueue.h | 16 ++++++++++++++-- .../unittests/imagecache/taskqueue-test.cpp | 18 ++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h b/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h index fac7e7d9bfe..cffd300bbae 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h +++ b/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h @@ -54,6 +54,15 @@ public: clearTasks(oldTasks); } + void putThreadToSleep() + { + { + std::unique_lock lock{m_mutex}; + m_sleeping = true; + } + m_condition.notify_all(); + } + private: void destroy() { @@ -70,10 +79,10 @@ private: return {std::move(lock), true}; if (m_tasks.empty()) { auto timedOutWithoutEntriesOrFinishing = !m_condition.wait_for(lock, 10min, [&] { - return m_tasks.size() || m_finishing; + return m_tasks.size() || m_finishing || m_sleeping; }); - if (timedOutWithoutEntriesOrFinishing || m_finishing) { + if (timedOutWithoutEntriesOrFinishing || m_finishing || m_sleeping) { m_sleeping = true; return {std::move(lock), true}; } @@ -102,6 +111,9 @@ private: if (m_finishing || !m_sleeping) return; + if (m_sleeping) + joinThread(); + if (m_backgroundThread.joinable()) return; diff --git a/tests/unit/tests/unittests/imagecache/taskqueue-test.cpp b/tests/unit/tests/unittests/imagecache/taskqueue-test.cpp index 537f4b3ea32..ea295c57332 100644 --- a/tests/unit/tests/unittests/imagecache/taskqueue-test.cpp +++ b/tests/unit/tests/unittests/imagecache/taskqueue-test.cpp @@ -100,4 +100,22 @@ TEST_F(TaskQueue, clean_task_in_queue) queue.clean(); } +TEST_F(TaskQueue, sleeping_queue_is_recovering) +{ + Queue queue{mockDispatchCallback.AsStdFunction(), mockCleanCallback.AsStdFunction()}; + EXPECT_CALL(mockDispatchCallback, Call(IsTask(5))).WillRepeatedly([&](Task) { + notification.notify(); + }); + queue.addTask(5); + notification.wait(); + queue.putThreadToSleep(); + + EXPECT_CALL(mockDispatchCallback, Call(IsTask(22))).WillRepeatedly([&](Task) { + notification.notify(); + }); + + queue.addTask(22); + notification.wait(); +} + } // namespace From 89af0ad08a87fec1f6bb2a248a351c8c5fc0fd0a Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 27 Mar 2024 14:28:19 +0100 Subject: [PATCH 078/202] QmlDesigner: Fix capitalisation Change-Id: Iba1927368be4f06dea87132fe8a4e81db819a293 Reviewed-by: Thomas Hartmann --- .../qmldesigner/componentsplugin/components.metainfo | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/qmldesigner/componentsplugin/components.metainfo b/src/plugins/qmldesigner/componentsplugin/components.metainfo index b3d069dc1a2..2e070aa322c 100644 --- a/src/plugins/qmldesigner/componentsplugin/components.metainfo +++ b/src/plugins/qmldesigner/componentsplugin/components.metainfo @@ -12,7 +12,7 @@ MetaInfo { Property { name: "width"; type: "int"; value: 100; } Property { name: "height"; type: "int"; value: 100; } - tooltip: qsTr("Organizes items in a row.") + toolTip: qsTr("Organizes items in a row.") } } @@ -29,7 +29,7 @@ MetaInfo { Property { name: "width"; type: "int"; value: 100; } Property { name: "height"; type: "int"; value: 100; } - tooltip: qsTr("Organizes items in a column.") + toolTip: qsTr("Organizes items in a column.") } } @@ -46,7 +46,7 @@ MetaInfo { Property { name: "width"; type: "int"; value: 100; } Property { name: "height"; type: "int"; value: 100; } - tooltip: qsTr("Organizes items in a grid.") + toolTip: qsTr("Organizes items in a grid.") } } @@ -68,7 +68,7 @@ MetaInfo { Property { name: "width"; type: "int"; value: 100; } Property { name: "height"; type: "int"; value: 100; } - tooltip: qsTr("Organizes items in a grid. Only the top item is visible.") + toolTip: qsTr("Organizes items in a grid. Only the top item is visible.") } } } From 0150302bbbddb6414b1656acdd4e47eb56a16dfb Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 27 Mar 2024 16:10:04 +0100 Subject: [PATCH 079/202] QmlDesigner: Add Qt 6.7 to all wizards and make it the default Change-Id: I67a1870486c4f87e86e135af7619843a36e591a6 Reviewed-by: Tim Jenssen Reviewed-by: Tanja Remes Reviewed-by: Qt CI Patch Build Bot --- .../projects/application-extended-3d/wizard.json | 10 +++++++++- .../studio_templates/projects/application/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 ++++++++- 5 files changed, 41 insertions(+), 5 deletions(-) diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/wizard.json index 101d5c69032..7a0333e1f1e 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/wizard.json @@ -236,7 +236,7 @@ "type": "ComboBox", "data": { - "index": 3, + "index": 5, "items": [ { @@ -278,6 +278,14 @@ 'TargetQuickVersion': '6.6', 'TargetQuick3DVersion': '6.6' })" + }, + { + "trKey": "Qt 6.7", + "value": + "({ + 'TargetQuickVersion': '6.7', + 'TargetQuick3DVersion': '6.7' + })" } ] } diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json index c8b74dec498..613770b646c 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json @@ -236,7 +236,7 @@ "type": "ComboBox", "data": { - "index": 4, + "index": 6, "items": [ { @@ -280,6 +280,13 @@ "({ 'TargetQuickVersion': '6.6' })" + }, + { + "trKey": "Qt 6.7", + "value": + "({ + 'TargetQuickVersion': '6.7' + })" } ] } 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 d521cc201e8..7ad8d1d9d43 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json @@ -193,7 +193,7 @@ "type": "ComboBox", "data": { - "index": 4, + "index": 6, "items": [ { @@ -237,6 +237,13 @@ "({ 'TargetQuickVersion': '6.6' })" + }, + { + "trKey": "Qt 6.7", + "value": + "({ + 'TargetQuickVersion': '6.7' + })" } ] } 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 17cd30d507f..0a3aaf26b6e 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json @@ -191,7 +191,7 @@ "type": "ComboBox", "data": { - "index": 4, + "index": 6, "items": [ { @@ -235,6 +235,13 @@ "({ 'TargetQuickVersion': '6.6' })" + }, + { + "trKey": "Qt 6.7", + "value": + "({ + 'TargetQuickVersion': '6.7' + })" } ] } 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 37710ae91dd..58c4783f097 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json @@ -191,7 +191,7 @@ "type": "ComboBox", "data": { - "index": 4, + "index": 6, "items": [ { @@ -235,6 +235,13 @@ "({ 'TargetQuickVersion': '6.6' })" + }, + { + "trKey": "Qt 6.7", + "value": + "({ + 'TargetQuickVersion': '6.7' + })" } ] } From 385cf4d89c547c35d1691581acd956f10b32bbc9 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 27 Mar 2024 18:20:47 +0100 Subject: [PATCH 080/202] QmlDesigner: Fix taskqueue The joinable thread was under the locked mutex. So the sleep notifier could not arrive. Change-Id: I548c89904b09f8dcc02cb7ae0486880f519915d2 Reviewed-by: Tim Jenssen --- .../designercore/imagecache/taskqueue.h | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h b/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h index cffd300bbae..dc5ed3de23f 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h +++ b/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h @@ -31,7 +31,7 @@ public: { std::unique_lock lock{m_mutex}; - ensureThreadIsRunning(std::move(traceToken)); + ensureThreadIsRunning(lock, std::move(traceToken)); m_tasks.emplace_back(std::forward(arguments)...); } @@ -75,19 +75,20 @@ private: { using namespace std::literals::chrono_literals; std::unique_lock lock{m_mutex}; - if (m_finishing) + + if (m_finishing || m_sleeping) return {std::move(lock), true}; + if (m_tasks.empty()) { auto timedOutWithoutEntriesOrFinishing = !m_condition.wait_for(lock, 10min, [&] { return m_tasks.size() || m_finishing || m_sleeping; }); - if (timedOutWithoutEntriesOrFinishing || m_finishing || m_sleeping) { + if (timedOutWithoutEntriesOrFinishing) m_sleeping = true; - return {std::move(lock), true}; - } } - return {std::move(lock), false}; + + return {std::move(lock), m_finishing || m_sleeping}; } [[nodiscard]] std::optional getTask(std::unique_lock lock) @@ -103,32 +104,38 @@ private: return {std::move(task)}; } - template - void ensureThreadIsRunning(TraceToken traceToken) + template + void ensureThreadIsRunning(Lock &lock, TraceToken traceToken) { using namespace NanotraceHR::Literals; if (m_finishing || !m_sleeping) return; - if (m_sleeping) + if (m_sleeping) { + lock.unlock(); joinThread(); + lock.lock(); + + m_sleeping = false; + } if (m_backgroundThread.joinable()) return; - m_sleeping = false; - auto [threadCreateToken, flowToken] = traceToken.beginDurationWithFlow( "thread is created in the task queue"_t); m_backgroundThread = std::thread{[this](auto traceToken) { auto duration = traceToken.beginDuration( "thread is ready"_t); + while (true) { auto [lock, abort] = waitForTasks(); duration.end(); + if (abort) return; + auto getTaskToken = duration.beginDuration( "get task from queue"_t); if (auto task = getTask(std::move(lock)); task) { From ff4e33dc0a2224e35c969ef34879e6ef98d22aae Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 27 Mar 2024 15:45:57 +0100 Subject: [PATCH 081/202] QmlDesigner: Enable rendering of DesignerEffects * Effects are items and we have to use __effect/source and allEffects to idenfify them and get the correct bounding rectangle. * For now we use a custom parser to work around issues when editig the model Change-Id: I78690498c44f8285d3bb6ce78eafcafb9c26b2f1 Reviewed-by: Tim Jenssen --- .../designercore/metainfo/nodemetainfo.cpp | 12 ++++++------ .../qml2puppet/instances/qt5nodeinstanceserver.cpp | 12 ++++++++++-- .../qml2puppet/instances/servernodeinstance.cpp | 14 ++++++++++++-- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index 85f904666c8..742a093400d 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -2444,12 +2444,12 @@ bool NodeMetaInfo::usesCustomParser() const if (!isValid()) return false; - auto type = typeName(); - return type == "QtQuick.VisualItemModel" || type == "Qt.VisualItemModel" - || type == "QtQuick.VisualDataModel" || type == "Qt.VisualDataModel" - || type == "QtQuick.ListModel" || type == "Qt.ListModel" - || type == "QtQml.Models.ListModel" || type == "QtQuick.XmlListModel" - || type == "Qt.XmlListModel" || type == "QtQml.XmlListModel.XmlListModel"; + auto type = simplifiedTypeName(); + return type == "VisualItemModel" + || type == "VisualDataModel" + || type == "ListModel" + || type == "XmlListModel" + || type == "DesignEffect"; } } diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceserver.cpp index fca884e94b3..765b52321bf 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceserver.cpp @@ -436,10 +436,18 @@ QQuickItem *Qt5NodeInstanceServer::parentEffectItem(QQuickItem *item) return nullptr; } -static bool isEffectItem(QQuickItem *item, QQuickShaderEffectSource *sourceItem) +static bool isEffectItem(QQuickItem *item, QQuickShaderEffectSource *sourceItem, QQuickItem *target) { QQuickItemPrivate *pItem = QQuickItemPrivate::get(sourceItem); + if (item) { + QQmlProperty prop(item, "__effect"); + if (prop.read().toBool()) { + prop = QQmlProperty(item, "source"); + return prop.read().value() == target; + } + } + if (!pItem || !pItem->layer()) return false; @@ -477,7 +485,7 @@ QImage Qt5NodeInstanceServer::grabItem([[maybe_unused]] QQuickItem *item) if (auto parent = item->parentItem()) { const auto siblings = parent->childItems(); for (auto sibling : siblings) { - if (isEffectItem(sibling, pItem->layer()->effectSource())) + if (isEffectItem(sibling, pItem->layer()->effectSource(), item)) return grabItem(sibling); } } diff --git a/src/tools/qml2puppet/qml2puppet/instances/servernodeinstance.cpp b/src/tools/qml2puppet/qml2puppet/instances/servernodeinstance.cpp index ca05a5ef039..9600a0b2b0d 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/servernodeinstance.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/servernodeinstance.cpp @@ -120,10 +120,20 @@ QRectF ServerNodeInstance::effectAdjustedBoundingRect(QQuickItem *item) { if (item) { QQuickItemPrivate *pItem = QQuickItemPrivate::get(item); - if (pItem && pItem->layer() && pItem->layer()->sourceRect().isValid()) + + QQmlProperty prop(item, "__effect"); + + if (pItem && pItem->layer() && pItem->layer()->sourceRect().isValid()) { return pItem->layer()->sourceRect(); - else + } else if (prop.read().toBool()) { + prop = QQmlProperty(item, "allEffects"); + QRectF rect = prop.read().toRectF().adjusted(-20, -20, 20, 20); + if (rect.isValid()) + return rect; return item->boundingRect(); + } else { + return item->boundingRect(); + } } return {}; } From c04eb4444d3e7990ed41896c0937d32b4e24c9fa Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Wed, 27 Mar 2024 19:02:50 +0200 Subject: [PATCH 082/202] QmlDesigner: Use ExamplesModelV2 V2 adds minQDSVersion property for model items Change-Id: I27d36428f73b27b1d52d429e23654d094d6ff86d Reviewed-by: Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../qmldesigner/welcomepage/MainGridStack.qml | 12 ++++++- .../studiowelcome/studiowelcomeplugin.cpp | 31 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/welcomepage/MainGridStack.qml b/share/qtcreator/qmldesigner/welcomepage/MainGridStack.qml index 73b023bddc0..555596223b9 100644 --- a/share/qtcreator/qmldesigner/welcomepage/MainGridStack.qml +++ b/share/qtcreator/qmldesigner/welcomepage/MainGridStack.qml @@ -88,7 +88,17 @@ Item { Layout.fillWidth: true Layout.fillHeight: true hover: hoverHandler.hovered - model: ExamplesModel { id: examplesModel} + + Component.onCompleted: { + // remove items with old versions from the examples model + for (let i = examplesModel.count - 1; i >= 0; --i) { + if (!projectModel.exampleVersionOk(examplesModel.get(i).minQDSVersion)) + examplesModel.remove(i) + } + } + + model: ExamplesModelV2 { id: examplesModel } + delegate: ThumbnailDelegate { type: ThumbnailDelegate.Type.Example downloadable: showDownload diff --git a/src/plugins/studiowelcome/studiowelcomeplugin.cpp b/src/plugins/studiowelcome/studiowelcomeplugin.cpp index ac6a17de874..fe209b5fd0b 100644 --- a/src/plugins/studiowelcome/studiowelcomeplugin.cpp +++ b/src/plugins/studiowelcome/studiowelcomeplugin.cpp @@ -333,6 +333,37 @@ public: Core::EditorManager::openEditor(qmlFile); } + Q_INVOKABLE bool exampleVersionOk(const QString &exampleVersion) + { + if (exampleVersion.isEmpty()) + return true; + + const QStringList exampleVersionParts = exampleVersion.split('.'); + const QStringList qdsVersionParts = QCoreApplication::applicationVersion().split('.'); + + QList exampleVerInts; + QList qdsVerInts; + for (const QString &part : exampleVersionParts) + exampleVerInts.append(part.toInt()); + + for (const QString &part : qdsVersionParts) + qdsVerInts.append(part.toInt()); + + // pad zeros so both lists are same size + while (qdsVerInts.size() < exampleVerInts.size()) + qdsVerInts.append(0); + + while (exampleVerInts.size() < qdsVerInts.size()) + exampleVerInts.append(0); + + for (int i = 0; i < qdsVerInts.size(); ++i) { + if (exampleVerInts[i] < qdsVerInts[i]) + return false; + } + + return true; + } + public slots: void resetProjects(); void delayedResetProjects(); From fd93290fbdf0064ccaf88a43e6a57d4a5d0150ad Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 28 Mar 2024 10:03:42 +0100 Subject: [PATCH 083/202] QmlDesigner: Update branch of the components to qds-4.5. Change-Id: I13e763c8a0941ee9af3f63c657dbccd71fb8f010 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- .../studio_templates/projects/common/qmlcomponents.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/common/qmlcomponents.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/common/qmlcomponents.tpl index 2e940be0329..a9f20243a69 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.4 + GIT_TAG qds-4.5 GIT_REPOSITORY https://code.qt.io/qt-labs/qtquickdesigner-components.git ) From 933958e1748dee771670b69bf36dfecf3c1d5e26 Mon Sep 17 00:00:00 2001 From: Burak Hancerli Date: Tue, 26 Mar 2024 13:59:35 +0100 Subject: [PATCH 084/202] ResourceGenerator: Increase the compression level Change-Id: Ifa099590e4a0cdd8d07b1865ddc1d64eb7d32af1 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann Reviewed-by: --- .../components/componentcore/resourcegenerator.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp b/src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp index 4a229564c64..24047f650f1 100644 --- a/src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp +++ b/src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp @@ -222,6 +222,10 @@ bool createQmlrcFile(const FilePath &qmlrcFilePath) rccProcess.setWorkingDirectory(project->projectDirectory()); const QStringList arguments = {"--binary", + "--compress", + "9", + "--threshold", + "30", "--output", qmlrcFilePath.toString(), tempQrcFile.toString()}; From 510c15083bd6793f7509d14d3f9cd93240399734 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Wed, 20 Mar 2024 15:32:00 +0100 Subject: [PATCH 085/202] QmlDesigner: optimize Model::generateNewId Change-Id: I3b4949133b16a955678528dd8a139fde5c51edd2 Reviewed-by: Reviewed-by: Marco Bubke Reviewed-by: Qt CI Patch Build Bot --- src/plugins/qmldesigner/designercore/model/model.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index a4cd31b2a89..03824a7b188 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -1869,8 +1869,9 @@ QString Model::generateNewId(const QString &prefixName, int counter = 0; - QString newBaseId = QStringView(u"%1").arg(firstCharToLower(prefixName)); - newBaseId.remove(QRegularExpression(QStringLiteral("[^a-zA-Z0-9_]"))); + static const QRegularExpression nonWordCharsRegex("\\W"); + QString newBaseId = firstCharToLower(prefixName); + newBaseId.remove(nonWordCharsRegex); if (!newBaseId.isEmpty()) { QChar firstChar = newBaseId.at(0); From beb9fc2fde620f43e1f4df4493f239f5301b5e45 Mon Sep 17 00:00:00 2001 From: Shrief Gabr Date: Wed, 27 Mar 2024 14:28:18 +0200 Subject: [PATCH 086/202] QmlDesigner: Prevent data scramble after sorting Task-number: QDS-12160 Change-Id: I3a90583a097b3074d9502668e4b3670fa81f34a2 Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri Reviewed-by: Ali Kianian --- .../CollectionDetailsView.qml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml index 6420f197bd9..3bb48ef3a6a 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml @@ -155,12 +155,18 @@ Rectangle { StudioControls.MenuItem { text: qsTr("Sort Ascending") - onTriggered: sortedModel.sort(headerMenu.clickedHeaderIndex, Qt.AscendingOrder) + onTriggered: { + tableView.closeEditor() + tableView.model.sort(headerMenu.clickedHeaderIndex, Qt.AscendingOrder) + } } StudioControls.MenuItem { text: qsTr("Sort Descending") - onTriggered: sortedModel.sort(headerMenu.clickedHeaderIndex, Qt.DescendingOrder) + onTriggered: { + tableView.closeEditor() + tableView.model.sort(headerMenu.clickedHeaderIndex, Qt.DescendingOrder) + } } } } @@ -192,7 +198,7 @@ Rectangle { TableView { id: tableView - model: root.model + model: root.sortedModel clip: true readonly property real maxAvailableHeight: gridLayout.maxAvailableHeight From 37216207996d44f54c86c02454163ca68a37bdd8 Mon Sep 17 00:00:00 2001 From: Knud Dollereder Date: Fri, 22 Mar 2024 11:38:14 +0100 Subject: [PATCH 087/202] QmlProjectManager: New project structure support for CMakeGenerator - Add writer interface in order to support the current and the new project structure in parallel. Using the new one if qdsVersion is >= 4.5 - Separated templates for the new generator from the old one - Add file name validity check - Generate files in the folder src and cmake if they do not exist yet. Only re-generate files in src/autogen. - Add action to enable or disable the cmake-generator - Add function that checks if a resource file is within the project folder but not part of the project Change-Id: I3d75dbee1043ed28e6126cf0b2c83994cb70ed45 Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- src/plugins/qmlprojectmanager/CMakeLists.txt | 3 + .../buildsystem/qmlbuildsystem.cpp | 12 + .../buildsystem/qmlbuildsystem.h | 3 + .../cmakegen/boilerplate.qrc | 15 +- .../cmakegen/cmakegenerator.cpp | 555 ++++++++---------- .../cmakegen/cmakegenerator.h | 71 +-- .../cmakegen/cmakewriter.cpp | 259 ++++++++ .../qmlprojectmanager/cmakegen/cmakewriter.h | 98 ++++ .../cmakegen/cmakewriterv0.cpp | 178 ++++++ .../cmakegen/cmakewriterv0.h | 25 + .../cmakegen/cmakewriterv1.cpp | 179 ++++++ .../cmakegen/cmakewriterv1.h | 25 + .../cmakemodule_v1.tpl} | 3 +- .../cmakeroot_v0.tpl} | 0 .../cmakegen/templates/cmakeroot_v1.tpl | 46 ++ .../cmakegen/templates/environment_h.tpl | 24 + .../import_qml_components_h.tpl} | 1 + .../cmakegen/templates/insight.tpl | 19 + .../cmakegen/templates/main_cpp_v0.tpl | 35 ++ .../cmakegen/templates/main_cpp_v1.tpl | 31 + .../cmakegen/templates/qmlcomponents.tpl | 34 ++ .../qmlprojectmanager/qmlprojectmanager.qbs | 4 + .../qmlprojectmanager/qmlprojectplugin.cpp | 2 + 23 files changed, 1255 insertions(+), 367 deletions(-) create mode 100644 src/plugins/qmlprojectmanager/cmakegen/cmakewriter.cpp create mode 100644 src/plugins/qmlprojectmanager/cmakegen/cmakewriter.h create mode 100644 src/plugins/qmlprojectmanager/cmakegen/cmakewriterv0.cpp create mode 100644 src/plugins/qmlprojectmanager/cmakegen/cmakewriterv0.h create mode 100644 src/plugins/qmlprojectmanager/cmakegen/cmakewriterv1.cpp create mode 100644 src/plugins/qmlprojectmanager/cmakegen/cmakewriterv1.h rename src/plugins/qmlprojectmanager/cmakegen/{gencmakemodule.tpl => templates/cmakemodule_v1.tpl} (97%) rename src/plugins/qmlprojectmanager/cmakegen/{gencmakeroot.tpl => templates/cmakeroot_v0.tpl} (100%) create mode 100644 src/plugins/qmlprojectmanager/cmakegen/templates/cmakeroot_v1.tpl create mode 100644 src/plugins/qmlprojectmanager/cmakegen/templates/environment_h.tpl rename src/plugins/qmlprojectmanager/cmakegen/{gencmakeheadercomponents.tpl => templates/import_qml_components_h.tpl} (99%) create mode 100644 src/plugins/qmlprojectmanager/cmakegen/templates/insight.tpl create mode 100644 src/plugins/qmlprojectmanager/cmakegen/templates/main_cpp_v0.tpl create mode 100644 src/plugins/qmlprojectmanager/cmakegen/templates/main_cpp_v1.tpl create mode 100644 src/plugins/qmlprojectmanager/cmakegen/templates/qmlcomponents.tpl diff --git a/src/plugins/qmlprojectmanager/CMakeLists.txt b/src/plugins/qmlprojectmanager/CMakeLists.txt index 3067411a244..b3eb298f3bc 100644 --- a/src/plugins/qmlprojectmanager/CMakeLists.txt +++ b/src/plugins/qmlprojectmanager/CMakeLists.txt @@ -50,6 +50,9 @@ extend_qtc_plugin(QmlProjectManager generatecmakelists.cpp generatecmakelists.h generatecmakelistsconstants.h cmakegenerator.cpp cmakegenerator.h + cmakewriter.cpp cmakewriter.h + cmakewriterv0.cpp cmakewriterv0.h + cmakewriterv1.cpp cmakewriterv1.h boilerplate.qrc ) diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp index a0b8263900c..00e501d6f88 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp @@ -93,6 +93,7 @@ QmlBuildSystem::QmlBuildSystem(Target *target) connect(target->project(), &Project::projectFileIsDirty, this, [this] { refresh(RefreshOptions::Project); m_cmakeGen->initialize(qmlProject()); + m_cmakeGen->updateMenuAction(); updateMcuBuildStep(project()->activeTarget(), qtForMCUs()); }); @@ -501,6 +502,17 @@ void QmlBuildSystem::setPrimaryLanguage(QString language) m_projectItem->setPrimaryLanguage(language); } +bool QmlBuildSystem::enableCMakeGeneration() const +{ + return m_projectItem->enableCMakeGeneration(); +} + +void QmlBuildSystem::setEnableCMakeGeneration(bool enable) +{ + if (enable != enableCMakeGeneration()) + m_projectItem->setEnableCMakeGeneration(enable); +} + void QmlBuildSystem::refreshFiles(const QSet & /*added*/, const QSet &removed) { if (m_blockFilesUpdate) { diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h index 524af5cd1bb..e3f9b99adb0 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h +++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h @@ -83,6 +83,9 @@ public: QString primaryLanguage() const; void setPrimaryLanguage(QString language); + bool enableCMakeGeneration() const; + void setEnableCMakeGeneration(bool enable); + bool forceFreeType() const; bool widgetApp() const; diff --git a/src/plugins/qmlprojectmanager/cmakegen/boilerplate.qrc b/src/plugins/qmlprojectmanager/cmakegen/boilerplate.qrc index 10fab598382..b997114b2f0 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/boilerplate.qrc +++ b/src/plugins/qmlprojectmanager/cmakegen/boilerplate.qrc @@ -1,8 +1,17 @@ + + templates/cmakeroot_v0.tpl + templates/cmakeroot_v1.tpl + templates/main_cpp_v0.tpl + templates/main_cpp_v1.tpl + templates/cmakemodule_v1.tpl + templates/insight.tpl + templates/qmlcomponents.tpl + templates/environment_h.tpl + templates/import_qml_components_h.tpl + + - gencmakeroot.tpl - gencmakemodule.tpl - gencmakeheadercomponents.tpl qmlprojectmaincpp.tpl qmlprojectmaincppheader.tpl qmlprojectenvheader.tpl diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp index 736ac04ec6f..abd812832c7 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp +++ b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp @@ -2,13 +2,22 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "cmakegenerator.h" -#include "generatecmakelistsconstants.h" + +#include "qmlprojectmanager/qmlproject.h" +#include "qmlprojectmanager/qmlprojectconstants.h" +#include "qmlprojectmanager/qmlprojectmanagertr.h" #include "projectexplorer/projectmanager.h" #include "projectexplorer/projectnodes.h" -#include "qmlprojectmanager/qmlproject.h" +#include "utils/filenamevalidatinglineedit.h" + +#include "coreplugin/actionmanager/actionmanager.h" +#include "coreplugin/actionmanager/actioncontainer.h" + +#include #include +#include #include @@ -16,30 +25,63 @@ namespace QmlProjectManager { namespace GenerateCmake { -const char TEMPLATE_CMAKELISTS_ROOT[] = ":/boilerplatetemplates/gencmakeroot.tpl"; -const char TEMPLATE_CMAKELISTS_MODULE[] = ":/boilerplatetemplates/gencmakemodule.tpl"; +void CMakeGenerator::createMenuAction(QObject *parent) +{ + Core::ActionContainer *fileMenu = Core::ActionManager::actionContainer( + Core::Constants::M_FILE); + Core::ActionContainer *exportMenu = Core::ActionManager::createMenu( + QmlProjectManager::Constants::EXPORT_MENU); -const char TEMPLATE_SOURCE_MAIN[] = ":/boilerplatetemplates/qmlprojectmaincpp.tpl"; -const char TEMPLATE_HEADER_IMPORT_COMPS[] = ":/boilerplatetemplates/gencmakeheadercomponents.tpl"; -const char TEMPLATE_HEADER_IMPORT_PLUGINS[] = ":/boilerplatetemplates/qmlprojectmaincppheader.tpl"; -const char TEMPLATE_HEADER_ENVIRONMENT[] = ":/boilerplatetemplates/qmlprojectenvheader.tpl"; + exportMenu->menu()->setTitle(Tr::tr("Export Project")); + exportMenu->appendGroup(QmlProjectManager::Constants::G_EXPORT_GENERATE); + fileMenu->addMenu(exportMenu, Core::Constants::G_FILE_EXPORT); -const char DO_NOT_EDIT_FILE_COMMENT[] - = "### This file is automatically generated by Qt Design Studio.\n" - "### Do not change\n\n"; + auto action = new QAction(Tr::tr("Enable Automatic CMake Generation"), parent); + action->setEnabled(false); + action->setCheckable(true); -const char TEMPLATE_BIG_RESOURCES[] = R"( -qt6_add_resources(%1 %2 - BIG_RESOURCES - PREFIX "%3" - VERSION 1.0 - FILES %4 -))"; + Core::Command *cmd = Core::ActionManager::registerAction(action, "QmlProject.EnableCMakeGeneration"); + exportMenu->addAction(cmd, QmlProjectManager::Constants::G_EXPORT_GENERATE); -const char TEMPLATE_LINK_LIBRARIES[] = R"( -target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE -%3 -))"; + QObject::connect( + ProjectExplorer::ProjectManager::instance(), + &ProjectExplorer::ProjectManager::startupProjectChanged, + [action]() { + if (auto buildSystem = QmlBuildSystem::getStartupBuildSystem()) { + action->setEnabled(!buildSystem->qtForMCUs()); + action->setChecked(buildSystem->enableCMakeGeneration()); + } + } + ); + + QObject::connect(action, &QAction::toggled, [](bool checked) { + if (auto buildSystem = QmlBuildSystem::getStartupBuildSystem()) + buildSystem->setEnableCMakeGeneration(checked); + }); +} + +void CMakeGenerator::logIssue(const QString &text) +{ + // TODO: Use Issues panel as soon as it is usable in DS. + qDebug() << text; +} + +void CMakeGenerator::updateMenuAction() +{ + QTC_ASSERT(buildSystem(), return); + + Core::Command *cmd = Core::ActionManager::command("QmlProject.EnableCMakeGeneration"); + if (!cmd) + return; + + QAction *action = cmd->action(); + if (!action) + return; + + bool enabled = buildSystem()->enableCMakeGeneration(); + if (enabled != action->isChecked()) + action->setChecked(enabled); +} CMakeGenerator::CMakeGenerator(QmlBuildSystem *bs, QObject *parent) : QObject(parent) @@ -47,6 +89,44 @@ CMakeGenerator::CMakeGenerator(QmlBuildSystem *bs, QObject *parent) , m_buildSystem(bs) {} +const QmlProject *CMakeGenerator::qmlProject() const +{ + if (m_buildSystem) + return m_buildSystem->qmlProject(); + return nullptr; +} + +const QmlBuildSystem *CMakeGenerator::buildSystem() const +{ + return m_buildSystem; +} + +bool CMakeGenerator::findFile(const Utils::FilePath& file) const +{ + return findFile(m_root, file); +} + +bool CMakeGenerator::isRootNode(const NodePtr &node) const +{ + return node->name == "Main"; +} + +bool CMakeGenerator::hasChildModule(const NodePtr &node) const +{ + for (const NodePtr &child : node->subdirs) { + if (child->type == Node::Type::Module) + return true; + if (hasChildModule(child)) + return true; + } + return false; +} + +QString CMakeGenerator::projectName() const +{ + return m_projectName; +} + void CMakeGenerator::setEnabled(bool enabled) { m_enabled = enabled; @@ -57,11 +137,11 @@ void CMakeGenerator::initialize(QmlProject *project) if (!m_enabled) return; - m_srcs.clear(); m_moduleNames.clear(); + m_writer = CMakeWriter::create(this); m_root = std::make_shared(); - m_root->module = true; + m_root->type = Node::Type::App; m_root->uri = QString("Main"); m_root->name = QString("Main"); m_root->dir = project->rootProjectDirectory(); @@ -72,8 +152,10 @@ void CMakeGenerator::initialize(QmlProject *project) parseNodeTree(m_root, rootProjectNode); parseSourceTree(); + compareWithFileSystem(m_root); + createCMakeFiles(m_root); - createEntryPoints(m_root); + createSourceFiles(); } void CMakeGenerator::update(const QSet &added, const QSet &removed) @@ -81,6 +163,8 @@ void CMakeGenerator::update(const QSet &added, const QSet &rem if (!m_enabled) return; + QTC_ASSERT(m_writer, return); + std::set dirtyModules; for (const QString &add : added) { const Utils::FilePath path = Utils::FilePath::fromString(add); @@ -89,7 +173,8 @@ void CMakeGenerator::update(const QSet &added, const QSet &rem if (auto module = findModuleFor(node)) dirtyModules.insert(module); } else { - qDebug() << "CmakeGen: Failed to find Folder node " << path; + QString text("Failed to find Folder for file: %1"); + logIssue(text.arg(add)); } } @@ -103,282 +188,65 @@ void CMakeGenerator::update(const QSet &added, const QSet &rem } for (auto module : dirtyModules) - createModuleCMakeFile(module); + m_writer->writeModuleCMakeFile(module, m_root); } -std::vector CMakeGenerator::files(const NodePtr &node, - const FileGetter &getter) const +bool CMakeGenerator::isQml(const Utils::FilePath &path) const { - std::vector out = getter(node); - for (const CMakeGenerator::NodePtr &child : node->subdirs) { - if (child->module) - continue; - - auto childFiles = files(child, getter); - out.insert(out.end(), childFiles.begin(), childFiles.end()); - } - return out; + const QString suffix = path.suffix(); + return suffix == "qml" || suffix == "ui.qml"; } -std::vector CMakeGenerator::qmlFiles(const NodePtr &node) const +bool CMakeGenerator::isResource(const Utils::FilePath &path) const { - return files(node, [](const NodePtr &n) { return n->files; }); -} - -std::vector CMakeGenerator::singletons(const NodePtr &node) const -{ - return files(node, [](const NodePtr &n) { return n->singletons; }); -} - -std::vector CMakeGenerator::resources(const NodePtr &node) const -{ - return files(node, [](const NodePtr &n) { return n->resources; }); -} - -std::vector CMakeGenerator::sources(const NodePtr &node) const -{ - return files(node, [](const NodePtr &n) { return n->sources; }); + static const QStringList suffixes = { + "json", "mesh", "dae", "qad", "hints", "png", "hdr", "ttf", "jpg", + "jpeg", "js", "qsb", "frag", "frag.qsb", "vert", "vert.qsb", "svg", + "ktx", "bmp", "gif", "webp", "tiff"}; + return suffixes.contains(path.suffix(), Qt::CaseInsensitive); } void CMakeGenerator::createCMakeFiles(const NodePtr &node) const { - if (isRootNode(node)) - createMainCMakeFile(node); + QTC_ASSERT(m_writer, return); - if (node->module || hasChildModule(node)) - createModuleCMakeFile(node); + if (isRootNode(node)) + m_writer->writeRootCMakeFile(node); + + if (node->type == Node::Type::Module || (hasChildModule(node))) + m_writer->writeModuleCMakeFile(node, m_root); for (const NodePtr &n : node->subdirs) createCMakeFiles(n); } -void CMakeGenerator::createMainCMakeFile(const NodePtr &node) const +void CMakeGenerator::createSourceFiles() const { - const QString appName = m_projectName + "App"; + QTC_ASSERT(m_writer, return); - const QString qtcontrolsConfFile = makeEnvironmentVariable(Constants::ENV_VARIABLE_CONTROLCONF); - - QString fileSection = ""; - if (!qtcontrolsConfFile.isEmpty()) - fileSection = QString(" FILES\n %1").arg(qtcontrolsConfFile); - - const QString fileTemplate = readTemplate(TEMPLATE_CMAKELISTS_ROOT); - const QString fileContent = fileTemplate.arg(appName, m_srcs.join(" "), fileSection); - - const Utils::FilePath file = node->dir.pathAppended("CMakeLists.txt"); - writeFile(file, fileContent); -} - -void CMakeGenerator::createModuleCMakeFile(const NodePtr &node) const -{ - Utils::FilePath writeToFile = node->dir.pathAppended("CMakeLists.txt"); - - if (!node->module && hasChildModule(node)) { - QString content(DO_NOT_EDIT_FILE_COMMENT); - content.append(makeSubdirectoriesBlock(node)); - writeFile(writeToFile, content); - return; + NodePtr sourceNode = {}; + for (const NodePtr &child : m_root->subdirs) { + if (child->name == m_writer->sourceDirName()) + sourceNode = child; } - QString templatePrefix; - templatePrefix.append(makeSubdirectoriesBlock(node)); - templatePrefix.append(makeSingletonBlock(node)); - - auto [resources, bigResources] = makeResourcesBlocks(node); - QString moduleContent; - moduleContent.append(makeQmlFilesBlock(node)); - moduleContent.append(resources); - - QString templatePostfix; - templatePostfix.append(bigResources); - - if (isRootNode(node)) { - writeToFile = node->dir.pathAppended("qmlModules"); - QString pluginNames; - for (const QString &moduleName : m_moduleNames) - pluginNames.append(" " + moduleName + "plugin\n"); - - templatePostfix += QString::fromUtf8(TEMPLATE_LINK_LIBRARIES, -1).arg(pluginNames); - } - - const QString fileTemplate = readTemplate(TEMPLATE_CMAKELISTS_MODULE); - const QString fileContent - = fileTemplate.arg(node->name, node->uri, templatePrefix, moduleContent, templatePostfix); - - writeFile(writeToFile, fileContent); -} - -void CMakeGenerator::createEntryPoints(const NodePtr &node) const -{ - createMainCppFile(node); -} - -void CMakeGenerator::createMainCppFile(const NodePtr &node) const -{ - const Utils::FilePath srcDir = node->dir.pathAppended(Constants::DIRNAME_CPP); - if (!srcDir.exists()) { - srcDir.createDir(); - - const Utils::FilePath componentsHeaderPath = srcDir.pathAppended( - "import_qml_components_plugins.h"); - - const QString componentsHeaderContent = readTemplate(TEMPLATE_HEADER_IMPORT_COMPS); - writeFile(componentsHeaderPath, componentsHeaderContent); - - const Utils::FilePath cppFilePath = srcDir.pathAppended("main.cpp"); - const QString cppContent = readTemplate(TEMPLATE_SOURCE_MAIN); - writeFile(cppFilePath, cppContent); - - const Utils::FilePath envHeaderPath = srcDir.pathAppended("app_environment.h"); - if (m_buildSystem) { - QString environment; - const QString qtcontrolsConfFile = makeEnvironmentVariable( - Constants::ENV_VARIABLE_CONTROLCONF); - for (Utils::EnvironmentItem &envItem : m_buildSystem->environment()) { - QString key = envItem.name; - QString value = envItem.value; - if (value == qtcontrolsConfFile) - value.prepend(":/"); - environment.append(QString(" qputenv(\"%1\", \"%2\");\n").arg(key).arg(value)); - } - const QString envHeaderContent - = readTemplate(TEMPLATE_HEADER_ENVIRONMENT).arg(environment); - writeFile(envHeaderPath, envHeaderContent); - } - } - - QString moduleContent; - for (const QString &module : m_moduleNames) - moduleContent.append(QString("Q_IMPORT_QML_PLUGIN(%1)\n").arg(module + "Plugin")); - - const QString headerContent = readTemplate(TEMPLATE_HEADER_IMPORT_PLUGINS).arg(moduleContent); - const Utils::FilePath headerFilePath = srcDir.pathAppended("import_qml_plugins.h"); - writeFile(headerFilePath, headerContent); -} - -void CMakeGenerator::writeFile(const Utils::FilePath &path, const QString &content) const -{ - QFile fileHandle(path.toString()); - fileHandle.open(QIODevice::WriteOnly); - QTextStream stream(&fileHandle); - stream << content; - fileHandle.close(); -} - -QString CMakeGenerator::makeRelative(const NodePtr &node, const Utils::FilePath &path) const -{ - const QString dir = node->dir.toString(); - return "\"" + Utils::FilePath::calcRelativePath(path.toString(), dir) + "\""; -} - -QString CMakeGenerator::makeEnvironmentVariable(const QString &key) const -{ - QString value; - if (m_buildSystem) { - auto envItems = m_buildSystem->environment(); - auto confEnv = std::find_if(envItems.begin(), - envItems.end(), - [key](Utils::EnvironmentItem &item) { return item.name == key; }); - if (confEnv != envItems.end()) - value = confEnv->value; - } - return value; -} - -QString CMakeGenerator::makeSingletonBlock(const NodePtr &node) const -{ - const QString setProperties( - "set_source_files_properties(%1\n PROPERTIES\n %2 %3\n)\n\n"); - - QString str; - for (const Utils::FilePath &path : node->singletons) - str.append(setProperties.arg(path.fileName()).arg("QT_QML_SINGLETON_TYPE").arg("true")); - return str; -} - -QString CMakeGenerator::makeSubdirectoriesBlock(const NodePtr &node) const -{ - QString str; - for (const NodePtr &n : node->subdirs) { - if (n->module || hasChildModule(n)) - str.append(QString("add_subdirectory(%1)\n").arg(n->dir.fileName())); - } - return str; -} - -QString CMakeGenerator::makeQmlFilesBlock(const NodePtr &node) const -{ - QString qmlFileContent; - for (const Utils::FilePath &path : qmlFiles(node)) - qmlFileContent.append(QString(" %1\n").arg(makeRelative(node, path))); - - if (isRootNode(node) && qmlFileContent.isEmpty()) - qmlFileContent.append(QString(" %1\n").arg("\"main.qml\"")); - - QString str; - if (!qmlFileContent.isEmpty()) - str.append(QString(" QML_FILES\n%1").arg(qmlFileContent)); - - return str; -} - -std::tuple CMakeGenerator::makeResourcesBlocks(const NodePtr &node) const -{ - QString resourcesOut; - QString bigResourcesOut; - - QString resourceFiles; - std::vector bigResources; - for (const Utils::FilePath &path : resources(node)) { - if (path.fileSize() > 5000000) { - bigResources.push_back(makeRelative(node, path)); - continue; - } - resourceFiles.append(QString(" %1\n").arg(makeRelative(node, path))); - } - - if (!resourceFiles.isEmpty()) - resourcesOut.append(QString(" RESOURCES\n%1").arg(resourceFiles)); - - QString templatePostfix; - if (!bigResources.empty()) { - QString resourceContent; - for (const QString &res : bigResources) - resourceContent.append(QString("\n %1").arg(res)); - - const QString prefixPath = QString(node->uri).replace('.', '/'); - const QString prefix = "/qt/qml/" + prefixPath; - const QString resourceName = node->name + "BigResource"; - - bigResourcesOut = QString::fromUtf8(TEMPLATE_BIG_RESOURCES, -1) - .arg(node->name, resourceName, prefix, resourceContent); - } - - return {resourcesOut, bigResourcesOut}; -} - -QString CMakeGenerator::readTemplate(const QString &templatePath) const -{ - QFile templatefile(templatePath); - templatefile.open(QIODevice::ReadOnly); - QTextStream stream(&templatefile); - QString content = stream.readAll(); - templatefile.close(); - return content; + if (sourceNode) + m_writer->writeSourceFiles(sourceNode, m_root); } void CMakeGenerator::readQmlDir(const Utils::FilePath &filePath, NodePtr &node) const { - node->module = true; + node->type = Node::Type::Module; QFile f(filePath.toString()); f.open(QIODevice::ReadOnly); QTextStream stream(&f); Utils::FilePath dir = filePath.parentDir(); + static const QRegularExpression whitespaceRegex("\\s+"); while (!stream.atEnd()) { const QString line = stream.readLine(); - const QStringList tokenizedLine = line.split(QRegularExpression("\\s+")); + const QStringList tokenizedLine = line.split(whitespaceRegex); const QString maybeFileName = tokenizedLine.last(); if (tokenizedLine.first().compare("module", Qt::CaseInsensitive) == 0) { node->uri = tokenizedLine.last(); @@ -392,11 +260,11 @@ void CMakeGenerator::readQmlDir(const Utils::FilePath &filePath, NodePtr &node) f.close(); } -CMakeGenerator::NodePtr CMakeGenerator::findModuleFor(const NodePtr &node) const +NodePtr CMakeGenerator::findModuleFor(const NodePtr &node) const { NodePtr current = node; while (current->parent) { - if (current->module) + if (current->type == Node::Type::Module) return current; current = current->parent; @@ -404,7 +272,7 @@ CMakeGenerator::NodePtr CMakeGenerator::findModuleFor(const NodePtr &node) const return nullptr; } -CMakeGenerator::NodePtr CMakeGenerator::findNode(NodePtr &node, const Utils::FilePath &path) const +NodePtr CMakeGenerator::findNode(NodePtr &node, const Utils::FilePath &path) const { const Utils::FilePath parentDir = path.parentDir(); for (NodePtr &child : node->subdirs) { @@ -416,8 +284,7 @@ CMakeGenerator::NodePtr CMakeGenerator::findNode(NodePtr &node, const Utils::Fil return nullptr; } -CMakeGenerator::NodePtr CMakeGenerator::findOrCreateNode(NodePtr &node, - const Utils::FilePath &path) const +NodePtr CMakeGenerator::findOrCreateNode(NodePtr &node, const Utils::FilePath &path) const { if (auto found = findNode(node, path)) return found; @@ -442,14 +309,46 @@ CMakeGenerator::NodePtr CMakeGenerator::findOrCreateNode(NodePtr &node, return last; } +bool findFileWithGetter(const Utils::FilePath &file, const NodePtr &node, const FileGetter &getter) +{ + for (const auto &f : getter(node)) { + if (f == file) + return true; + } + for (const auto &subdir : node->subdirs) { + if (findFileWithGetter(file, subdir, getter)) + return true; + } + return false; +} + +bool CMakeGenerator::findFile(const NodePtr &node, const Utils::FilePath &file) const +{ + if (isResource(file)) { + return findFileWithGetter(file, node, [](const NodePtr &n) { return n->resources; }); + } else if (isQml(file)) { + if (findFileWithGetter(file, node, [](const NodePtr &n) { return n->files; })) + return true; + else if (findFileWithGetter(file, node, [](const NodePtr &n) { return n->singletons; })) + return true; + } + return false; +} + void CMakeGenerator::insertFile(NodePtr &node, const Utils::FilePath &path) const { + QString error; + if (!Utils::FileNameValidatingLineEdit::validateFileName(path.fileName(), false, &error)) { + QString text(path.path() + error); + logIssue(error); + } + if (path.fileName() == "qmldir") { readQmlDir(path, node); - } else if (path.suffix() == "qml" || path.suffix() == "ui.qml") { - node->files.push_back(path); } else if (path.suffix() == "cpp") { node->sources.push_back(path); + } else if (isQml(path)) { + node->files.push_back(path); } else if (isResource(path)) { node->resources.push_back(path); } @@ -458,12 +357,12 @@ void CMakeGenerator::insertFile(NodePtr &node, const Utils::FilePath &path) cons void CMakeGenerator::removeFile(NodePtr &node, const Utils::FilePath &path) const { if (path.fileName() == "qmldir") { - node->module = false; + node->type = Node::Type::Folder; node->singletons.clear(); node->uri = ""; node->name = path.parentDir().fileName(); - } else if (path.suffix() == "qml") { + } else if (isQml(path)) { auto iter = std::find(node->files.begin(), node->files.end(), path); if (iter != node->files.end()) node->files.erase(iter); @@ -474,33 +373,9 @@ void CMakeGenerator::removeFile(NodePtr &node, const Utils::FilePath &path) cons } } -bool CMakeGenerator::isRootNode(const NodePtr &node) const -{ - return node->name == "Main"; -} - -bool CMakeGenerator::hasChildModule(const NodePtr &node) const -{ - for (const NodePtr &child : node->subdirs) { - if (child->module) - return true; - if (hasChildModule(child)) - return true; - } - return false; -} - -bool CMakeGenerator::isResource(const Utils::FilePath &path) const -{ - static const QStringList suffixes = { - "json", "mesh", "dae", "qad", "hints", "png", "hdr", "ttf", "jpg", "JPG", - "js", "qsb", "frag", "frag.qsb", "vert", "vert.qsb", "svg", "ktx"}; - return suffixes.contains(path.suffix()); -} - void CMakeGenerator::printModules(const NodePtr &node) const { - if (node->module) + if (node->type == Node::Type::Module) qDebug() << "Module: " << node->name; for (const auto &child : node->subdirs) @@ -516,7 +391,27 @@ void CMakeGenerator::printNodeTree(const NodePtr &generatorNode, size_t indent) return str; }; + QString typeString; + switch (generatorNode->type) + { + case Node::Type::App: + typeString = "Node::Type::App"; + break; + case Node::Type::Folder: + typeString = "Node::Type::Folder"; + break; + case Node::Type::Module: + typeString = "Node::Type::Module"; + break; + case Node::Type::Library: + typeString = "Node::Type::Library"; + break; + default: + typeString = "Node::Type::Undefined"; + } + qDebug() << addIndent(indent) << "GeneratorNode: " << generatorNode->name; + qDebug() << addIndent(indent) << "type: " << typeString; qDebug() << addIndent(indent) << "directory: " << generatorNode->dir; qDebug() << addIndent(indent) << "files: " << generatorNode->files; qDebug() << addIndent(indent) << "singletons: " << generatorNode->singletons; @@ -532,7 +427,7 @@ void CMakeGenerator::parseNodeTree(NodePtr &generatorNode, { for (const auto *childNode : folderNode->nodes()) { if (const auto *subFolderNode = childNode->asFolderNode()) { - CMakeGenerator::NodePtr childGeneratorNode = std::make_shared(); + NodePtr childGeneratorNode = std::make_shared(); childGeneratorNode->parent = generatorNode; childGeneratorNode->dir = subFolderNode->filePath(); childGeneratorNode->name = subFolderNode->displayName(); @@ -544,25 +439,55 @@ void CMakeGenerator::parseNodeTree(NodePtr &generatorNode, } } - if (generatorNode->name == "content") - generatorNode->module = true; + if (m_writer) + m_writer->transformNode(generatorNode); - if (generatorNode->module) + if (generatorNode->type == Node::Type::Module) m_moduleNames.push_back(generatorNode->name); } void CMakeGenerator::parseSourceTree() { - m_srcs.clear(); - const QString srcDir = m_root->dir.pathAppended(Constants::DIRNAME_CPP).path(); - QDirIterator it(srcDir, QStringList({"*.cpp"}), QDir::Files, QDirIterator::Subdirectories); + QTC_ASSERT(m_writer, return); + + const Utils::FilePath srcDir = m_root->dir.pathAppended(m_writer->sourceDirName()); + QDirIterator it(srcDir.path(), {"*.cpp"}, QDir::Files, QDirIterator::Subdirectories); + + NodePtr srcNode = std::make_shared(); + srcNode->parent = m_root; + srcNode->type = Node::Type::App; + srcNode->dir = srcDir; + srcNode->uri = srcDir.baseName(); + srcNode->name = srcNode->uri; + while (it.hasNext()) { - QString relative = Utils::FilePath::calcRelativePath(it.next(), m_root->dir.path()); - m_srcs.push_back(relative); + auto next = it.next(); + srcNode->sources.push_back(Utils::FilePath::fromString(next)); } - if (m_srcs.empty()) - m_srcs.push_back("src/main.cpp"); + if (srcNode->sources.empty()) + srcNode->sources.push_back(srcDir.pathAppended("main.cpp")); + + if (m_writer) + m_writer->transformNode(srcNode); + + m_root->subdirs.push_back(srcNode); +} + +void CMakeGenerator::compareWithFileSystem(const NodePtr &node) const +{ + std::vector files; + QDirIterator iter(node->dir.path(), QDir::Files, QDirIterator::Subdirectories); + + while (iter.hasNext()) { + auto next = Utils::FilePath::fromString(iter.next()); + if (isResource(next) && !findFile(next)) + files.push_back(next); + } + + const QString text("File %1 is not part of the project"); + for (const auto& file : files) + logIssue(text.arg(file.path())); } } // namespace GenerateCmake diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h index f89257ac5dc..e9e87da1412 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h +++ b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h @@ -2,6 +2,8 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #pragma once +#include "cmakewriter.h" + #include "utils/filepath.h" #include @@ -22,80 +24,55 @@ class CMakeGenerator : public QObject Q_OBJECT public: + static void createMenuAction(QObject *parent); + static void logIssue(const QString &text); + CMakeGenerator(QmlBuildSystem *bs, QObject *parent = nullptr); + QString projectName() const; + + const QmlProject *qmlProject() const; + const QmlBuildSystem *buildSystem() const; + + bool findFile(const Utils::FilePath &file) const; + bool isRootNode(const NodePtr &node) const; + bool hasChildModule(const NodePtr &node) const; + void setEnabled(bool enabled); - void initialize(QmlProject *project); - void update(const QSet &added, const QSet &removed); + void updateMenuAction(); private: - struct Node - { - std::shared_ptr parent = nullptr; - bool module = false; - - QString uri; - QString name; - Utils::FilePath dir; - - std::vector> subdirs; - std::vector files; - std::vector singletons; - std::vector resources; - std::vector sources; - }; - - using NodePtr = std::shared_ptr; - using FileGetter = std::function(const NodePtr &)>; - - std::vector files(const NodePtr &node, const FileGetter &getter) const; - std::vector qmlFiles(const NodePtr &node) const; - std::vector singletons(const NodePtr &node) const; - std::vector resources(const NodePtr &node) const; - std::vector sources(const NodePtr &node) const; + bool isQml(const Utils::FilePath &path) const; + bool isResource(const Utils::FilePath &path) const; void createCMakeFiles(const NodePtr &node) const; - void createMainCMakeFile(const NodePtr &node) const; - void createModuleCMakeFile(const NodePtr &node) const; + void createSourceFiles() const; - void createEntryPoints(const NodePtr &node) const; - void createMainCppFile(const NodePtr &node) const; - void writeFile(const Utils::FilePath &path, const QString &content) const; - - QString makeRelative(const NodePtr &node, const Utils::FilePath &path) const; - QString makeEnvironmentVariable(const QString &key) const; - QString makeSingletonBlock(const NodePtr &node) const; - QString makeSubdirectoriesBlock(const NodePtr &node) const; - QString makeQmlFilesBlock(const NodePtr &node) const; - std::tuple makeResourcesBlocks(const NodePtr &node) const; - - QString readTemplate(const QString &templatePath) const; void readQmlDir(const Utils::FilePath &filePath, NodePtr &node) const; - NodePtr findModuleFor(const NodePtr &node) const; NodePtr findNode(NodePtr &node, const Utils::FilePath &path) const; NodePtr findOrCreateNode(NodePtr &node, const Utils::FilePath &path) const; + bool findFile(const NodePtr &node, const Utils::FilePath &file) const; void insertFile(NodePtr &node, const Utils::FilePath &path) const; void removeFile(NodePtr &node, const Utils::FilePath &path) const; - bool isRootNode(const NodePtr &node) const; - bool hasChildModule(const NodePtr &node) const; - bool isResource(const Utils::FilePath &path) const; - void printModules(const NodePtr &generatorNode) const; void printNodeTree(const NodePtr &generatorNode, size_t indent = 0) const; void parseNodeTree(NodePtr &generatorNode, const ProjectExplorer::FolderNode *folderNode); void parseSourceTree(); + void compareWithFileSystem(const NodePtr &node) const; + bool m_enabled = false; + CMakeWriter::Ptr m_writer = {}; + QString m_projectName = {}; NodePtr m_root = {}; - QStringList m_srcs = {}; - std::vector m_moduleNames = {}; + QStringList m_moduleNames = {}; QmlBuildSystem *m_buildSystem = nullptr; }; diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakewriter.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakewriter.cpp new file mode 100644 index 00000000000..65d4a8c6924 --- /dev/null +++ b/src/plugins/qmlprojectmanager/cmakegen/cmakewriter.cpp @@ -0,0 +1,259 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#include "cmakewriter.h" +#include "cmakegenerator.h" +#include "cmakewriterv0.h" +#include "cmakewriterv1.h" +#include "generatecmakelistsconstants.h" + +#include "qmlprojectmanager/qmlproject.h" +#include "qmlprojectmanager/buildsystem/qmlbuildsystem.h" + +#include "utils/namevalueitem.h" + +#include +#include + +namespace QmlProjectManager { + +namespace GenerateCmake { + +const char TEMPLATE_BIG_RESOURCES[] = R"( +qt6_add_resources(%1 %2 + BIG_RESOURCES + PREFIX "%3" + VERSION 1.0 + FILES %4 +))"; + +CMakeWriter::Ptr CMakeWriter::create(CMakeGenerator *parent) +{ + const QmlProject *project = parent->qmlProject(); + QTC_ASSERT(project, return {}); + + const QmlBuildSystem *buildSystem = parent->buildSystem(); + QTC_ASSERT(buildSystem, return {}); + + const QString versionString = buildSystem->versionDesignStudio(); + bool ok = false; + if (float version = versionString.toFloat(&ok); ok && version > 4.4) + return std::make_unique(parent); + + return std::make_unique(parent); +} + +CMakeWriter::CMakeWriter(CMakeGenerator *parent) + : m_parent(parent) +{} + +const CMakeGenerator *CMakeWriter::parent() const +{ + return m_parent; +} + +bool CMakeWriter::isPlugin(const NodePtr &node) const +{ + if (node->type == Node::Type::Module) + return true; + return false; +} + +QString CMakeWriter::sourceDirName() const +{ + return Constants::DIRNAME_CPP; +} + +void CMakeWriter::transformNode(NodePtr &) const +{} + +std::vector CMakeWriter::files(const NodePtr &node, const FileGetter &getter) const +{ + std::vector out = getter(node); + for (const NodePtr &child : node->subdirs) { + if (child->type == Node::Type::Module) + continue; + + auto childFiles = files(child, getter); + out.insert(out.end(), childFiles.begin(), childFiles.end()); + } + return out; +} + +std::vector CMakeWriter::qmlFiles(const NodePtr &node) const +{ + return files(node, [](const NodePtr &n) { return n->files; }); +} + +std::vector CMakeWriter::singletons(const NodePtr &node) const +{ + return files(node, [](const NodePtr &n) { return n->singletons; }); +} + +std::vector CMakeWriter::resources(const NodePtr &node) const +{ + return files(node, [](const NodePtr &n) { return n->resources; }); +} + +std::vector CMakeWriter::sources(const NodePtr &node) const +{ + return files(node, [](const NodePtr &n) { return n->sources; }); +} + +std::vector CMakeWriter::plugins(const NodePtr &node) const +{ + QTC_ASSERT(parent(), return {}); + std::vector out; + collectPlugins(node, out); + return out; +} + +QString CMakeWriter::getEnvironmentVariable(const QString &key) const +{ + QTC_ASSERT(parent(), return {}); + + QString value; + if (m_parent->buildSystem()) { + auto envItems = m_parent->buildSystem()->environment(); + auto confEnv = std::find_if( + envItems.begin(), envItems.end(), [key](const Utils::EnvironmentItem &item) { + return item.name == key; + }); + if (confEnv != envItems.end()) + value = confEnv->value; + } + return value; +} + +QString CMakeWriter::makeRelative(const NodePtr &node, const Utils::FilePath &path) const +{ + const QString dir = node->dir.toString(); + return "\"" + Utils::FilePath::calcRelativePath(path.toString(), dir) + "\""; +} + +QString CMakeWriter::makeQmlFilesBlock(const NodePtr &node) const +{ + QTC_ASSERT(parent(), return {}); + + QString qmlFileContent; + for (const Utils::FilePath &path : qmlFiles(node)) + qmlFileContent.append(QString("\t\t%1\n").arg(makeRelative(node, path))); + + QString str; + if (!qmlFileContent.isEmpty()) + str.append(QString("\tQML_FILES\n%1").arg(qmlFileContent)); + + return str; +} + +QString CMakeWriter::makeSingletonBlock(const NodePtr &node) const +{ + QString str; + const QString setProperties("set_source_files_properties(%1\n\tPROPERTIES\n\t\t%2 %3\n)\n\n"); + for (const Utils::FilePath &path : node->singletons) + str.append(setProperties.arg(path.fileName()).arg("QT_QML_SINGLETON_TYPE").arg("true")); + return str; +} + +QString CMakeWriter::makeSubdirectoriesBlock(const NodePtr &node) const +{ + QTC_ASSERT(parent(), return {}); + + QString str; + for (const NodePtr &n : node->subdirs) { + if (n->type == Node::Type::Module || n->type == Node::Type::Library + || n->type == Node::Type::App || parent()->hasChildModule(n)) + str.append(QString("add_subdirectory(%1)\n").arg(n->dir.fileName())); + } + return str; +} + +QString CMakeWriter::makeSetEnvironmentFn() const +{ + QTC_ASSERT(parent(), return {}); + QTC_ASSERT(parent()->buildSystem(), return {}); + + const QmlBuildSystem *buildSystem = parent()->buildSystem(); + const QString configFile = getEnvironmentVariable(Constants::ENV_VARIABLE_CONTROLCONF); + + QString out("inline void set_qt_environment() {\n"); + for (Utils::EnvironmentItem &envItem : buildSystem->environment()) { + QString key = envItem.name; + QString value = envItem.value; + if (value == configFile) + value.prepend(":/"); + out.append(QString("\tqputenv(\"%1\", \"%2\");\n").arg(key).arg(value)); + } + out.append("}"); + + return out; +} + +std::tuple CMakeWriter::makeResourcesBlocks(const NodePtr &node) const +{ + QString resourcesOut; + QString bigResourcesOut; + + QString resourceFiles; + std::vector bigResources; + for (const Utils::FilePath &path : resources(node)) { + if (path.fileSize() > 5000000) { + bigResources.push_back(makeRelative(node, path)); + continue; + } + resourceFiles.append(QString("\t\t%1\n").arg(makeRelative(node, path))); + } + + if (!resourceFiles.isEmpty()) + resourcesOut.append(QString("\tRESOURCES\n%1").arg(resourceFiles)); + + QString templatePostfix; + if (!bigResources.empty()) { + QString resourceContent; + for (const QString &res : bigResources) + resourceContent.append(QString("\n %1").arg(res)); + + const QString prefixPath = QString(node->uri).replace('.', '/'); + const QString prefix = "/qt/qml/" + prefixPath; + const QString resourceName = node->name + "BigResource"; + + bigResourcesOut = QString::fromUtf8(TEMPLATE_BIG_RESOURCES, -1) + .arg(node->name, resourceName, prefix, resourceContent); + } + + return {resourcesOut, bigResourcesOut}; +} + +QString CMakeWriter::readTemplate(const QString &templatePath) const +{ + QFile templatefile(templatePath); + templatefile.open(QIODevice::ReadOnly); + QTextStream stream(&templatefile); + QString content = stream.readAll(); + templatefile.close(); + return content; +} + +void CMakeWriter::writeFile(const Utils::FilePath &path, const QString &content) const +{ + QFile fileHandle(path.toString()); + if (fileHandle.open(QIODevice::WriteOnly)) { + QTextStream stream(&fileHandle); + stream << content; + } else { + QString text("Failed to write file: %1"); + CMakeGenerator::logIssue(text.arg(path.path())); + } + fileHandle.close(); +} + +void CMakeWriter::collectPlugins(const NodePtr &node, std::vector &out) const +{ + if (isPlugin(node)) + out.push_back(node->name); + for (const auto &child : node->subdirs) + collectPlugins(child, out); +} + +} // End namespace GenerateCmake. + +} // End namespace QmlProjectManager. diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakewriter.h b/src/plugins/qmlprojectmanager/cmakegen/cmakewriter.h new file mode 100644 index 00000000000..86f6403d130 --- /dev/null +++ b/src/plugins/qmlprojectmanager/cmakegen/cmakewriter.h @@ -0,0 +1,98 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#pragma once + +#include "utils/filepath.h" + +#include + +namespace QmlProjectManager { + +class QmlProject; +class QmlBuildSystem; + +namespace GenerateCmake { + +struct Node +{ + enum class Type { + App, + Module, + Library, + Folder, + }; + + std::shared_ptr parent = nullptr; + Type type = Type::Folder; + + QString uri; + QString name; + Utils::FilePath dir; + + std::vector> subdirs; + std::vector files; + std::vector singletons; + std::vector resources; + std::vector sources; +}; + +using NodePtr = std::shared_ptr; +using FileGetter = std::function(const NodePtr &)>; + +class CMakeGenerator; + +const char DO_NOT_EDIT_FILE[] = + "### This file is automatically generated by Qt Design Studio.\n" + "### Do not change\n\n"; + +const char TEMPLATE_LINK_LIBRARIES[] = + "target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE\n" + "%3" + ")"; + +class CMakeWriter +{ +public: + using Ptr = std::shared_ptr; + + static Ptr create(CMakeGenerator *parent); + + CMakeWriter(CMakeGenerator *parent); + const CMakeGenerator *parent() const; + + virtual bool isPlugin(const NodePtr &node) const; + virtual QString sourceDirName() const; + virtual void transformNode(NodePtr &) const; + + virtual void writeRootCMakeFile(const NodePtr &node) const = 0; + virtual void writeModuleCMakeFile(const NodePtr &node, const NodePtr &root) const = 0; + virtual void writeSourceFiles(const NodePtr &node, const NodePtr &root) const = 0; + +protected: + std::vector files(const NodePtr &node, const FileGetter &getter) const; + std::vector qmlFiles(const NodePtr &node) const; + std::vector singletons(const NodePtr &node) const; + std::vector resources(const NodePtr &node) const; + std::vector sources(const NodePtr &node) const; + std::vector plugins(const NodePtr &node) const; + + QString getEnvironmentVariable(const QString &key) const; + + QString makeRelative(const NodePtr &node, const Utils::FilePath &path) const; + QString makeQmlFilesBlock(const NodePtr &node) const; + QString makeSingletonBlock(const NodePtr &node) const; + QString makeSubdirectoriesBlock(const NodePtr &node) const; + QString makeSetEnvironmentFn() const; + std::tuple makeResourcesBlocks(const NodePtr &node) const; + + QString readTemplate(const QString &templatePath) const; + void writeFile(const Utils::FilePath &path, const QString &content) const; + +private: + void collectPlugins(const NodePtr &node, std::vector &out) const; + const CMakeGenerator *m_parent = nullptr; +}; + +} // End namespace GenerateCmake. + +} // End namespace QmlProjectManager. diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv0.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv0.cpp new file mode 100644 index 00000000000..71b6dbc4b0c --- /dev/null +++ b/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv0.cpp @@ -0,0 +1,178 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#include "cmakewriterv0.h" +#include "cmakegenerator.h" +#include "generatecmakelistsconstants.h" + +namespace QmlProjectManager { + +namespace GenerateCmake { + +const char TEMPLATE_ADD_QML_MODULE[] = R"( +qt6_add_qml_module(%1 + URI "%2" + VERSION 1.0 + RESOURCE_PREFIX "/qt/qml" +%3))"; + +CMakeWriterV0::CMakeWriterV0(CMakeGenerator *parent) + : CMakeWriter(parent) +{} + +bool CMakeWriterV0::isPlugin(const NodePtr &node) const +{ + if (CMakeWriter::isPlugin(node)) + return true; + + if (node->type == Node::Type::App) + return !node->files.empty() || !node->singletons.empty() || !node->resources.empty(); + + return false; +} + +void CMakeWriterV0::transformNode(NodePtr &node) const +{ + QTC_ASSERT(parent(), return); + + if (node->name == "src") { + node->type = Node::Type::Folder; + } else if (node->name == "content") { + node->type = Node::Type::Module; + } else if (node->type == Node::Type::App) { + Utils::FilePath path = node->dir.pathAppended("main.qml"); + if (!path.exists()) { + QString text("Expected File %1 not found."); + CMakeGenerator::logIssue(text.arg(path.path())); + return; + } + if (!parent()->findFile(path)) + node->files.push_back(path); + } +} + +void CMakeWriterV0::writeRootCMakeFile(const NodePtr &node) const +{ + QTC_ASSERT(parent(), return); + + const Utils::FilePath insightPath = node->dir.pathAppended("insight"); + if (!insightPath.exists()) { + const QString insightTemplate = readTemplate(":/templates/insight"); + writeFile(insightPath, insightTemplate); + } + + const Utils::FilePath componentPath = node->dir.pathAppended("qmlcomponents"); + if (!componentPath.exists()) { + const QString compTemplate = readTemplate(":/templates/qmlcomponents"); + writeFile(componentPath, compTemplate); + } + + const QString appName = parent()->projectName() + "App"; + const QString qtcontrolsConfFile = getEnvironmentVariable(Constants::ENV_VARIABLE_CONTROLCONF); + + QString fileSection = ""; + if (!qtcontrolsConfFile.isEmpty()) + fileSection = QString("\tFILES\n\t\t%1").arg(qtcontrolsConfFile); + + QStringList srcs; + for (const Utils::FilePath &path : sources(node)) + srcs.push_back(makeRelative(node, path)); + + const QString fileTemplate = readTemplate(":/templates/cmakeroot_v0"); + const QString fileContent = fileTemplate.arg(appName, srcs.join(" "), fileSection); + + const Utils::FilePath cmakeFile = node->dir.pathAppended("CMakeLists.txt"); + writeFile(cmakeFile, fileContent); +} + +void CMakeWriterV0::writeModuleCMakeFile(const NodePtr &node, const NodePtr &root) const +{ + QTC_ASSERT(parent(), return); + + Utils::FilePath writeToFile = node->dir.pathAppended("CMakeLists.txt"); + + QString content(DO_NOT_EDIT_FILE); + if (node->type == Node::Type::Folder && parent()->hasChildModule(node)) { + content.append(makeSubdirectoriesBlock(node)); + writeFile(writeToFile, content); + return; + } + + content.append(makeSubdirectoriesBlock(node)); + content.append("\n"); + content.append(makeSingletonBlock(node)); + + QString qmlModulesContent; + qmlModulesContent.append(makeQmlFilesBlock(node)); + + auto [resources, bigResources] = makeResourcesBlocks(node); + qmlModulesContent.append(resources); + + if (!qmlModulesContent.isEmpty()) { + const QString addLibraryTemplate("qt_add_library(%1 STATIC)"); + const QString addModuleTemplate(TEMPLATE_ADD_QML_MODULE); + + content.append(addLibraryTemplate.arg(node->name)); + content.append(addModuleTemplate.arg(node->name, node->uri, qmlModulesContent)); + content.append("\n\n"); + } + + content.append(bigResources); + + if (node->type == Node::Type::App) { + writeToFile = node->dir.pathAppended("qmlModules"); + QString pluginNames; + for (const QString &moduleName : plugins(root)) + pluginNames.append("\t" + moduleName + "plugin\n"); + + if (!pluginNames.isEmpty()) + content += QString::fromUtf8(TEMPLATE_LINK_LIBRARIES, -1).arg(pluginNames); + } + + writeFile(writeToFile, content); +} + +void CMakeWriterV0::writeSourceFiles(const NodePtr &node, const NodePtr &root) const +{ + QTC_ASSERT(parent(), return); + + const Utils::FilePath srcDir = node->dir; + if (!srcDir.exists()) { + srcDir.createDir(); + + const Utils::FilePath componentsHeaderPath = srcDir.pathAppended( + "import_qml_components_plugins.h"); + const QString componentsHeaderContent = readTemplate( + ":/templates/import_qml_components_h"); + writeFile(componentsHeaderPath, componentsHeaderContent); + + const Utils::FilePath cppFilePath = srcDir.pathAppended("main.cpp"); + const QString cppContent = readTemplate(":/templates/main_cpp_v0"); + writeFile(cppFilePath, cppContent); + } + + QString fileHeader( + "/*\n" + " * This file is automatically generated by Qt Design Studio.\n" + " * Do not change\n" + "*/\n\n"); + + const Utils::FilePath envHeaderPath = srcDir.pathAppended("app_environment.h"); + QString envHeaderContent(fileHeader); + envHeaderContent.append("#include \n\n"); + envHeaderContent.append(makeSetEnvironmentFn()); + writeFile(envHeaderPath, envHeaderContent); + + QString importPluginsContent; + for (const QString &module : plugins(root)) + importPluginsContent.append(QString("Q_IMPORT_QML_PLUGIN(%1)\n").arg(module + "Plugin")); + + QString importPluginsHeader(fileHeader); + importPluginsHeader.append("#include \n\n"); + importPluginsHeader.append(importPluginsContent); + + const Utils::FilePath headerFilePath = srcDir.pathAppended("import_qml_plugins.h"); + writeFile(headerFilePath, importPluginsHeader); +} + +} // namespace GenerateCmake +} // namespace QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv0.h b/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv0.h new file mode 100644 index 00000000000..a1cd16d0fce --- /dev/null +++ b/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv0.h @@ -0,0 +1,25 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#pragma once + +#include "cmakewriter.h" + +namespace QmlProjectManager { + +namespace GenerateCmake { + +class CMakeWriterV0 final : public CMakeWriter +{ +public: + CMakeWriterV0(CMakeGenerator *parent); + + bool isPlugin(const NodePtr &node) const override; + void transformNode(NodePtr &node) const override; + + void writeRootCMakeFile(const NodePtr &node) const override; + void writeModuleCMakeFile(const NodePtr &node, const NodePtr &root) const override; + void writeSourceFiles(const NodePtr &node, const NodePtr &root) const override; +}; + +} // namespace GenerateCmake +} // namespace QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv1.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv1.cpp new file mode 100644 index 00000000000..d78abd227d7 --- /dev/null +++ b/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv1.cpp @@ -0,0 +1,179 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#include "cmakewriterv1.h" +#include "cmakegenerator.h" +#include "generatecmakelistsconstants.h" + +#include "qmlprojectmanager/buildsystem/qmlbuildsystem.h" + +namespace QmlProjectManager { + +namespace GenerateCmake { + +const char TEMPLATE_SRC_CMAKELISTS[] = R"( +target_sources(${CMAKE_PROJECT_NAME} PUBLIC +%2) + +target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Gui + Qt${QT_VERSION_MAJOR}::Quick + Qt${QT_VERSION_MAJOR}::Qml))"; + +CMakeWriterV1::CMakeWriterV1(CMakeGenerator *parent) + : CMakeWriter(parent) +{} + +QString CMakeWriterV1::sourceDirName() const +{ + return "App"; +} + +void CMakeWriterV1::transformNode(NodePtr &node) const +{ + QTC_ASSERT(parent(), return); + + QString contentDir = parent()->projectName() + "Content"; + if (node->name == contentDir) + node->type = Node::Type::Module; +} + +void CMakeWriterV1::writeRootCMakeFile(const NodePtr &node) const +{ + QTC_ASSERT(parent(), return); + + const Utils::FilePath cmakeFolderPath = node->dir.pathAppended("cmake"); + if (!cmakeFolderPath.exists()) + cmakeFolderPath.createDir(); + + const Utils::FilePath insightPath = cmakeFolderPath.pathAppended("insight.cmake"); + if (!insightPath.exists()) { + const QString insightTemplate = readTemplate(":/templates/insight"); + writeFile(insightPath, insightTemplate); + } + + const Utils::FilePath componentPath = cmakeFolderPath.pathAppended("qmlcomponents.cmake"); + if (!componentPath.exists()) { + const QString compTemplate = readTemplate(":/templates/qmlcomponents"); + writeFile(componentPath, compTemplate); + } + + const Utils::FilePath file = node->dir.pathAppended("CMakeLists.txt"); + const QString appName = parent()->projectName() + "App"; + + QString fileSection = ""; + const QString configFile = getEnvironmentVariable(Constants::ENV_VARIABLE_CONTROLCONF); + if (!configFile.isEmpty()) + fileSection = QString("\t\t%1").arg(configFile); + + const QString fileTemplate = readTemplate(":/templates/cmakeroot_v1"); + const QString fileContent = fileTemplate.arg(appName, fileSection); + writeFile(file, fileContent); + + const Utils::FilePath userFile = node->dir.pathAppended("qds.cmake"); + QString userFileContent(DO_NOT_EDIT_FILE); + userFileContent.append(makeSubdirectoriesBlock(node)); + userFileContent.append("\n"); + + QString pluginNames; + std::vector plugs = plugins(node); + for (size_t i = 0; i < plugs.size(); ++i) { + pluginNames.append("\t" + plugs[i] + "plugin"); + if (i != plugs.size() - 1) + pluginNames.append("\n"); + } + + QString linkLibrariesTemplate( + "target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE\n" + "%1)"); + + userFileContent.append(linkLibrariesTemplate.arg(pluginNames)); + + writeFile(userFile, userFileContent); +} + +void CMakeWriterV1::writeModuleCMakeFile(const NodePtr &node, const NodePtr &) const +{ + QTC_ASSERT(parent(), return); + + if (node->type == Node::Type::App) + return; + + Utils::FilePath writeToFile = node->dir.pathAppended("CMakeLists.txt"); + if (node->type == Node::Type::Folder && parent()->hasChildModule(node)) { + QString content(DO_NOT_EDIT_FILE); + content.append(makeSubdirectoriesBlock(node)); + writeFile(writeToFile, content); + return; + } + + QString prefix; + prefix.append(makeSubdirectoriesBlock(node)); + prefix.append(makeSingletonBlock(node)); + + auto [resources, bigResources] = makeResourcesBlocks(node); + QString moduleContent; + moduleContent.append(makeQmlFilesBlock(node)); + moduleContent.append(resources); + + QString postfix; + postfix.append(bigResources); + + const QString fileTemplate = readTemplate(":/templates/cmakemodule_v1"); + const QString fileContent = fileTemplate + .arg(node->name, node->uri, prefix, moduleContent, postfix); + writeFile(writeToFile, fileContent); +} + +void CMakeWriterV1::writeSourceFiles(const NodePtr &node, const NodePtr &root) const +{ + QTC_ASSERT(parent(), return); + QTC_ASSERT(parent()->buildSystem(), return); + + const QmlBuildSystem *buildSystem = parent()->buildSystem(); + + const Utils::FilePath srcDir = node->dir; + if (!srcDir.exists()) + srcDir.createDir(); + + const Utils::FilePath autogenDir = srcDir.pathAppended("autogen"); + if (!autogenDir.exists()) + autogenDir.createDir(); + + const Utils::FilePath mainCppPath = srcDir.pathAppended("main.cpp"); + if (!mainCppPath.exists()) { + const QString cppContent = readTemplate(":/templates/main_cpp_v1"); + writeFile(mainCppPath, cppContent); + } + + const Utils::FilePath cmakePath = srcDir.pathAppended("CMakeLists.txt"); + if (!cmakePath.exists()) { + std::vector sourcePaths = sources(node); + if (sourcePaths.empty()) + sourcePaths.push_back(mainCppPath); + + QString srcs = {}; + for (const Utils::FilePath &src : sourcePaths) + srcs.append("\t" + makeRelative(node, src) + "\n"); + + QString fileTemplate = QString::fromUtf8(TEMPLATE_SRC_CMAKELISTS, -1).arg(srcs); + writeFile(cmakePath, fileTemplate); + } + + const Utils::FilePath headerPath = autogenDir.pathAppended("environment.h"); + + QString environmentPrefix; + for (const QString &module : plugins(root)) + environmentPrefix.append(QString("Q_IMPORT_QML_PLUGIN(%1)\n").arg(module + "Plugin")); + + const QString mainFile("const char mainQmlFile[] = \"qrc:/qt/qml/%1\";"); + environmentPrefix.append("\n"); + environmentPrefix.append(mainFile.arg(buildSystem->mainFile())); + + const QString environmentPostfix = makeSetEnvironmentFn(); + const QString headerTemplate = readTemplate(":/templates/environment_h"); + writeFile(headerPath, headerTemplate.arg(environmentPrefix, environmentPostfix)); +} + +} // namespace GenerateCmake +} // namespace QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv1.h b/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv1.h new file mode 100644 index 00000000000..2a6a05b07be --- /dev/null +++ b/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv1.h @@ -0,0 +1,25 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#pragma once + +#include "cmakewriter.h" + +namespace QmlProjectManager { + +namespace GenerateCmake { + +class CMakeWriterV1 final : public CMakeWriter +{ +public: + CMakeWriterV1(CMakeGenerator *parent); + + QString sourceDirName() const override; + void transformNode(NodePtr &node) const override; + + void writeRootCMakeFile(const NodePtr &node) const override; + void writeModuleCMakeFile(const NodePtr &node, const NodePtr &root) const override; + void writeSourceFiles(const NodePtr &node, const NodePtr &root) const override; +}; + +} // namespace GenerateCmake +} // namespace QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/cmakegen/gencmakemodule.tpl b/src/plugins/qmlprojectmanager/cmakegen/templates/cmakemodule_v1.tpl similarity index 97% rename from src/plugins/qmlprojectmanager/cmakegen/gencmakemodule.tpl rename to src/plugins/qmlprojectmanager/cmakegen/templates/cmakemodule_v1.tpl index b81d253c907..25d894b347f 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/gencmakemodule.tpl +++ b/src/plugins/qmlprojectmanager/cmakegen/templates/cmakemodule_v1.tpl @@ -8,7 +8,6 @@ qt6_add_qml_module(%1 URI "%2" VERSION 1.0 RESOURCE_PREFIX "/qt/qml" -%4 -) +%4) %5 diff --git a/src/plugins/qmlprojectmanager/cmakegen/gencmakeroot.tpl b/src/plugins/qmlprojectmanager/cmakegen/templates/cmakeroot_v0.tpl similarity index 100% rename from src/plugins/qmlprojectmanager/cmakegen/gencmakeroot.tpl rename to src/plugins/qmlprojectmanager/cmakegen/templates/cmakeroot_v0.tpl diff --git a/src/plugins/qmlprojectmanager/cmakegen/templates/cmakeroot_v1.tpl b/src/plugins/qmlprojectmanager/cmakegen/templates/cmakeroot_v1.tpl new file mode 100644 index 00000000000..a5d5e2bd023 --- /dev/null +++ b/src/plugins/qmlprojectmanager/cmakegen/templates/cmakeroot_v1.tpl @@ -0,0 +1,46 @@ + +cmake_minimum_required(VERSION 3.21.1) + +option(LINK_INSIGHT "Link Qt Insight Tracker library" ON) +option(BUILD_QDS_COMPONENTS "Build design studio components" ON) + +project(%1 LANGUAGES CXX) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + +set(CMAKE_AUTOMOC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(QT_QML_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/qml) +set(QML_IMPORT_PATH ${QT_QML_OUTPUT_DIRECTORY} + CACHE STRING "Import paths for Qt Creator's code model" + FORCE +) + +find_package(Qt6 6.2 REQUIRED COMPONENTS Core Gui Qml Quick) + +if (Qt6_VERSION VERSION_GREATER_EQUAL 6.3) + qt_standard_project_setup() +endif() + +qt_add_executable(${CMAKE_PROJECT_NAME}) +qt_add_resources(${CMAKE_PROJECT_NAME} "configuration" + PREFIX "/" + FILES +%2) + +include(qds) + +if (BUILD_QDS_COMPONENTS) + include(qmlcomponents OPTIONAL) +endif() + +if (LINK_INSIGHT) + include(insight OPTIONAL) +endif () + +include(GNUInstallDirs) +install(TARGETS ${CMAKE_PROJECT_NAME} + BUNDLE DESTINATION . + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} +) diff --git a/src/plugins/qmlprojectmanager/cmakegen/templates/environment_h.tpl b/src/plugins/qmlprojectmanager/cmakegen/templates/environment_h.tpl new file mode 100644 index 00000000000..d835c942f65 --- /dev/null +++ b/src/plugins/qmlprojectmanager/cmakegen/templates/environment_h.tpl @@ -0,0 +1,24 @@ +/* + * This file is automatically generated by Qt Design Studio. + * Do not change. +*/ + +#include +#include "qqmlextensionplugin.h" + +%1 + +#ifdef BUILD_QDS_COMPONENTS + +Q_IMPORT_QML_PLUGIN(QtQuick_Studio_ComponentsPlugin) +Q_IMPORT_QML_PLUGIN(QtQuick_Studio_EffectsPlugin) +Q_IMPORT_QML_PLUGIN(QtQuick_Studio_ApplicationPlugin) +Q_IMPORT_QML_PLUGIN(FlowViewPlugin) +Q_IMPORT_QML_PLUGIN(QtQuick_Studio_LogicHelperPlugin) +Q_IMPORT_QML_PLUGIN(QtQuick_Studio_MultiTextPlugin) +Q_IMPORT_QML_PLUGIN(QtQuick_Studio_EventSimulatorPlugin) +Q_IMPORT_QML_PLUGIN(QtQuick_Studio_EventSystemPlugin) + +#endif + +%2 diff --git a/src/plugins/qmlprojectmanager/cmakegen/gencmakeheadercomponents.tpl b/src/plugins/qmlprojectmanager/cmakegen/templates/import_qml_components_h.tpl similarity index 99% rename from src/plugins/qmlprojectmanager/cmakegen/gencmakeheadercomponents.tpl rename to src/plugins/qmlprojectmanager/cmakegen/templates/import_qml_components_h.tpl index 167481d7c74..84d3c1c777c 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/gencmakeheadercomponents.tpl +++ b/src/plugins/qmlprojectmanager/cmakegen/templates/import_qml_components_h.tpl @@ -17,3 +17,4 @@ Q_IMPORT_QML_PLUGIN(QtQuick_Studio_EventSimulatorPlugin) Q_IMPORT_QML_PLUGIN(QtQuick_Studio_EventSystemPlugin) #endif + diff --git a/src/plugins/qmlprojectmanager/cmakegen/templates/insight.tpl b/src/plugins/qmlprojectmanager/cmakegen/templates/insight.tpl new file mode 100644 index 00000000000..8245e31f0d9 --- /dev/null +++ b/src/plugins/qmlprojectmanager/cmakegen/templates/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/src/plugins/qmlprojectmanager/cmakegen/templates/main_cpp_v0.tpl b/src/plugins/qmlprojectmanager/cmakegen/templates/main_cpp_v0.tpl new file mode 100644 index 00000000000..0ff9201d916 --- /dev/null +++ b/src/plugins/qmlprojectmanager/cmakegen/templates/main_cpp_v0.tpl @@ -0,0 +1,35 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include +#include + +#include "app_environment.h" +#include "import_qml_plugins.h" + +int main(int argc, char *argv[]) +{ + set_qt_environment(); + QGuiApplication app(argc, argv); + + QQmlApplicationEngine engine; + const QUrl url(u"qrc:/qt/qml/Main/main.qml"_qs); + QObject::connect( + &engine, &QQmlApplicationEngine::objectCreated, &app, + [url](QObject *obj, const QUrl &objUrl) { + if (!obj && url == objUrl) + QCoreApplication::exit(-1); + }, + Qt::QueuedConnection); + + engine.addImportPath(QCoreApplication::applicationDirPath() + "/qml"); + engine.addImportPath(":/"); + + engine.load(url); + + if (engine.rootObjects().isEmpty()) { + return -1; + } + + return app.exec(); +} diff --git a/src/plugins/qmlprojectmanager/cmakegen/templates/main_cpp_v1.tpl b/src/plugins/qmlprojectmanager/cmakegen/templates/main_cpp_v1.tpl new file mode 100644 index 00000000000..4d2a526d763 --- /dev/null +++ b/src/plugins/qmlprojectmanager/cmakegen/templates/main_cpp_v1.tpl @@ -0,0 +1,31 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include +#include + +#include "autogen/environment.h" + +int main(int argc, char *argv[]) +{ + set_qt_environment(); + QGuiApplication app(argc, argv); + + QQmlApplicationEngine engine; + const QUrl url(mainQmlFile); + QObject::connect( + &engine, &QQmlApplicationEngine::objectCreated, &app, + [url](QObject *obj, const QUrl &objUrl) { + if (!obj && url == objUrl) + QCoreApplication::exit(-1); + }, Qt::QueuedConnection); + + engine.addImportPath(QCoreApplication::applicationDirPath() + "/qml"); + engine.addImportPath(":/"); + engine.load(url); + + if (engine.rootObjects().isEmpty()) + return -1; + + return app.exec(); +} diff --git a/src/plugins/qmlprojectmanager/cmakegen/templates/qmlcomponents.tpl b/src/plugins/qmlprojectmanager/cmakegen/templates/qmlcomponents.tpl new file mode 100644 index 00000000000..a9f20243a69 --- /dev/null +++ b/src/plugins/qmlprojectmanager/cmakegen/templates/qmlcomponents.tpl @@ -0,0 +1,34 @@ +### This file is automatically generated by Qt Design Studio. +### Do not change + +message("Building designer components.") + +set(QT_QML_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/qml") + +include(FetchContent) +FetchContent_Declare( + ds + GIT_TAG qds-4.5 + GIT_REPOSITORY https://code.qt.io/qt-labs/qtquickdesigner-components.git +) + +FetchContent_GetProperties(ds) +FetchContent_Populate(ds) + +target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE + QuickStudioComponentsplugin + QuickStudioEffectsplugin + QuickStudioApplicationplugin + FlowViewplugin + QuickStudioLogicHelperplugin + QuickStudioMultiTextplugin + QuickStudioEventSimulatorplugin + QuickStudioEventSystemplugin + QuickStudioUtilsplugin +) + +add_subdirectory(${ds_SOURCE_DIR} ${ds_BINARY_DIR}) + +target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE + BULD_QDS_COMPONENTS=true +) diff --git a/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs b/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs index da6bebd6135..271b8828ab6 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs +++ b/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs @@ -54,6 +54,10 @@ QtcPlugin { "cmakegeneratordialog.cpp", "cmakegeneratordialog.h", "cmakeprojectconverter.cpp", "cmakeprojectconverter.h", "cmakeprojectconverterdialog.cpp", "cmakeprojectconverterdialog.h", + "cmakegenerator.cpp", "cmakegenerator.h", + "cmakewriter.cpp", "cmakewriter.h", + "cmakewriterv0.cpp", "cmakewriterv0.h", + "cmakewriterv1.cpp", "cmakewriterv1.h" ] } diff --git a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp index 62abce5fe96..14ac6060b38 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp @@ -386,6 +386,8 @@ void QmlProjectPlugin::initialize() mainUifileAction->setEnabled(buildSystem->mainUiFilePath() != fileNode->filePath()); }); + + GenerateCmake::CMakeGenerator::createMenuAction(this); } GenerateCmake::generateMenuEntry(this); From 35524ec21bf529f6cf080061970ffbd49ff878f0 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 28 Mar 2024 14:49:57 +0100 Subject: [PATCH 088/202] QmlDesigner: Do not allow import for QtQuick3D.MaterialEditor Change-Id: I81ad86ff0584e05bd5132077ac3416ab2eae7d62 Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index 3820322997a..d09143c2814 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -824,6 +824,7 @@ constexpr auto skipModules = std::make_tuple(EndsWith(u".impl"), Equals(u"QtQuick.Controls.NativeStyle"), Equals(u"QtQuick.Controls.Universal"), Equals(u"QtQuick.Controls.Windows"), + Equals(u"QtQuick3D.MaterialEditor"), StartsWith(u"QtQuick.LocalStorage"), StartsWith(u"QtQuick.NativeStyle"), StartsWith(u"QtQuick.Pdf"), From a60ae6a32c07dba52bd0ffb406d8a258ccc8f6ea Mon Sep 17 00:00:00 2001 From: Shrief Gabr Date: Fri, 29 Mar 2024 06:08:20 +0200 Subject: [PATCH 089/202] QmlDesigner: Add "Unsaved Changes" indicator to Save button Task-number: QDS-12237 Change-Id: Ib210ca06e061e82824ff4398604a569af44c2f6d Reviewed-by: Ali Kianian Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../CollectionDetailsToolbar.qml | 23 +++++++++++++++++-- .../imports/StudioTheme/Values.qml | 1 + 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml index 876e8456371..852341b9d5d 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml @@ -17,6 +17,7 @@ Rectangle { required property var model required property var backend property int selectedRow: -1 + property bool hasUnsavedChanges: false implicitHeight: StudioTheme.Values.toolbarHeight color: StudioTheme.Values.themeToolbarBackground @@ -34,6 +35,14 @@ Rectangle { fileDialog.reject() } + Connections { + target: root.model + + function onDataChanged() { + hasUnsavedChanges = true + } + } + RowLayout { id: container @@ -122,8 +131,18 @@ Rectangle { buttonIcon: StudioTheme.Constants.save_medium tooltip: qsTr("Save changes") - enabled: root.model.collectionName !== "" - onClicked: root.model.saveDataStoreCollections() + enabled: root.model.collectionName !== "" && root.hasUnsavedChanges + onClicked: hasUnsavedChanges = !root.model.saveDataStoreCollections() + + Rectangle { + width: StudioTheme.Values.smallStatusIndicatorDiameter + height: StudioTheme.Values.smallStatusIndicatorDiameter + radius: StudioTheme.Values.smallStatusIndicatorDiameter / 2 + anchors.right: parent.right + anchors.top: parent.top + visible: hasUnsavedChanges + color: StudioTheme.Values.themeIconColorSelected + } } IconButton { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml index 59030cd1264..1a04c8ebc30 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml @@ -251,6 +251,7 @@ QtObject { property real collectionTableVerticalMargin: 10 property real collectionCellMinimumWidth: 60 property real collectionCellMinimumHeight: 20 + property real smallStatusIndicatorDiameter: 6 // NEW NEW NEW readonly property int flowMargin: 7 From 1376139ec61fd86ba41f4056bf409350b95bea3d Mon Sep 17 00:00:00 2001 From: Shrief Gabr Date: Tue, 2 Apr 2024 13:13:53 +0300 Subject: [PATCH 090/202] EffectComposer: Check if effect name is duplicate before saving Task-number: QDS-12280 Change-Id: I42532ef8330ae518073a559edf7a41de834d98c9 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../qmldesigner/effectComposerQmlSources/SaveAsDialog.qml | 8 +++++--- src/plugins/effectcomposer/effectcomposermodel.cpp | 8 ++++++++ src/plugins/effectcomposer/effectcomposermodel.h | 1 + 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/SaveAsDialog.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/SaveAsDialog.qml index d139ba92055..65b01be4573 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/SaveAsDialog.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/SaveAsDialog.qml @@ -50,11 +50,13 @@ StudioControls.Dialog { if (/[^A-Za-z0-9_]+/.test(text)) errMsg = qsTr("Name contains invalid characters.") else if (!/^[A-Z]/.test(text)) - errMsg = qsTr("Name must start with a capital letter") + errMsg = qsTr("Name must start with a capital letter.") else if (text.length < 3) - errMsg = qsTr("Name must have at least 3 characters") + errMsg = qsTr("Name must have at least 3 characters.") else if (/\s/.test(text)) - errMsg = qsTr("Name cannot contain white space") + errMsg = qsTr("Name cannot contain white space.") + else if (EffectComposerBackend.effectComposerModel.nameExists(text)) + errMsg = qsTr("Name is already taken.") emptyText.text = errMsg btnSave.enabled = errMsg.length === 0 diff --git a/src/plugins/effectcomposer/effectcomposermodel.cpp b/src/plugins/effectcomposer/effectcomposermodel.cpp index 394b85aa552..606ca8e93e2 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.cpp +++ b/src/plugins/effectcomposer/effectcomposermodel.cpp @@ -224,6 +224,14 @@ QString EffectComposerModel::getUniqueEffectName() const return QString("Effect%1").arg(num, 2, 10, QChar('0')); } +bool EffectComposerModel::nameExists(const QString &name) const +{ + const QString effectsDir = QmlDesigner::ModelNodeOperations::getEffectsDefaultDirectory(); + const QString path = effectsDir + QDir::separator() + "%1" + ".qep"; + + return QFile::exists(path.arg(name)); +} + QString EffectComposerModel::fragmentShader() const { return m_fragmentShader; diff --git a/src/plugins/effectcomposer/effectcomposermodel.h b/src/plugins/effectcomposer/effectcomposermodel.h index e92213d12a5..0c1b6355f9e 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.h +++ b/src/plugins/effectcomposer/effectcomposermodel.h @@ -71,6 +71,7 @@ public: Q_INVOKABLE void clear(bool clearName = false); Q_INVOKABLE void assignToSelected(); Q_INVOKABLE QString getUniqueEffectName() const; + Q_INVOKABLE bool nameExists(const QString &name) const; bool shadersUpToDate() const; void setShadersUpToDate(bool newShadersUpToDate); From 22a5e4948ba9468fd0ff2ded5017634a271ec650 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Thu, 4 Apr 2024 10:12:10 +0300 Subject: [PATCH 091/202] QmlDesigner: Deselect the model by editing a cell or clicking out Fixes: QDS-11760 Change-Id: I6a63b2f9589c69859beff79ad04502974c5d18af Reviewed-by: Shrief Gabr Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../CollectionDetailsView.qml | 12 ++++++++++-- .../collectiondetailssortfiltermodel.cpp | 6 ++++++ .../collectiondetailssortfiltermodel.h | 1 + 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml index 3bb48ef3a6a..e3f1a589595 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml @@ -26,6 +26,11 @@ Rectangle { toolbar.closeDialogs() } + MouseArea { + anchors.fill: parent + onClicked: tableView.model.deselectAll() + } + Column { id: topRow readonly property real maxAvailableHeight: root.height @@ -100,7 +105,8 @@ Rectangle { id: topHeaderMouseArea anchors.fill: parent - anchors.margins: 5 + anchors.leftMargin: StudioTheme.Values.borderHover + anchors.rightMargin: StudioTheme.Values.borderHover acceptedButtons: Qt.LeftButton | Qt.RightButton hoverEnabled: true onClicked: (mouse) => { @@ -188,7 +194,8 @@ Rectangle { MouseArea { anchors.fill: parent - anchors.margins: 5 + anchors.topMargin: StudioTheme.Values.borderHover + anchors.bottomMargin: StudioTheme.Values.borderHover acceptedButtons: Qt.LeftButton onClicked: tableView.model.selectRow(index) } @@ -366,6 +373,7 @@ Rectangle { top: itemCell.top left: itemCell.left } + Component.onCompleted: tableView.model.deselectAll() } } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp index f56bb36e886..2cc6ac05a6b 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp @@ -62,6 +62,12 @@ bool CollectionDetailsSortFilterModel::selectColumn(int column) return m_source->selectColumn(mapToSource(index(0, column)).column()); } +void CollectionDetailsSortFilterModel::deselectAll() +{ + QTC_ASSERT(m_source, return); + m_source->deselectAll(); +} + CollectionDetailsSortFilterModel::~CollectionDetailsSortFilterModel() = default; bool CollectionDetailsSortFilterModel::filterAcceptsRow(int sourceRow, diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h index 93305f3ca20..10f6e09b057 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h @@ -31,6 +31,7 @@ public: Q_INVOKABLE bool selectRow(int row); Q_INVOKABLE bool selectColumn(int column); + Q_INVOKABLE void deselectAll(); signals: void selectedColumnChanged(int); From e6b542079f4ff4193684ba741e84a2b467c79f99 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Thu, 4 Apr 2024 11:09:45 +0300 Subject: [PATCH 092/202] QmlDesigner: Set the default column type of the model to string Change-Id: Id4daa8e802bcc7b40e212e7fc0e30b3c4f63ba4e Reviewed-by: Mahmoud Badri --- .../components/collectioneditor/collectioneditorutils.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp index 4725987f12e..f1e4e2c207d 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp @@ -3,6 +3,7 @@ #include "collectioneditorutils.h" +#include "collectiondatatypemodel.h" #include "model.h" #include "nodemetainfo.h" #include "propertymetainfo.h" @@ -288,7 +289,7 @@ QJsonObject defaultCollection() QJsonArray columns; QJsonObject defaultColumn; defaultColumn.insert("name", "Column 1"); - defaultColumn.insert("type", "string"); + defaultColumn.insert("type", CollectionDataTypeModel::dataTypeToString(DataType::String)); columns.append(defaultColumn); QJsonArray collectionData; From 212551567bad79c30db38db03199d0ca79fd865c Mon Sep 17 00:00:00 2001 From: Mats Honkamaa Date: Thu, 4 Apr 2024 13:08:31 +0300 Subject: [PATCH 093/202] Doc: Fix missing file extensions for images MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I485424b9dc071da2d1afb42b26e6599e3cc78fbc Reviewed-by: Teea Põldsam Reviewed-by: Johanna Vanhatapio --- doc/qtdesignstudio/examples/doc/rainSnowParticles.qdoc | 2 +- .../src/qtquick3d-editor/qtdesignstudio-3d-camera.qdoc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/qtdesignstudio/examples/doc/rainSnowParticles.qdoc b/doc/qtdesignstudio/examples/doc/rainSnowParticles.qdoc index 88dbd5fbdbb..5cc040d4276 100644 --- a/doc/qtdesignstudio/examples/doc/rainSnowParticles.qdoc +++ b/doc/qtdesignstudio/examples/doc/rainSnowParticles.qdoc @@ -41,7 +41,7 @@ Now you have added a particle system to your scene. - \image rain-snow-tutorial-particle-system + \image rain-snow-tutorial-particle-system.png \section2 Adjusting the Behavior and Apperance of the Particle System Next, you adjust the position, behavior, and apperance of the particle diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-camera.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-camera.qdoc index 35fc2338c2e..c245c490e67 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-camera.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-camera.qdoc @@ -65,7 +65,7 @@ You can edit the camera properties in the \uicontrol Properties view. - \image studio-qtquick-camera-properties "Properties view for Perspective Camera" + \image studio-qtquick-camera-properties.png "Properties view for Perspective Camera" \section1 Setting Camera Field of View From 18b7027cd432a2abfaa45175f4223dd3f0c56451 Mon Sep 17 00:00:00 2001 From: Mats Honkamaa Date: Fri, 15 Mar 2024 09:53:12 +0200 Subject: [PATCH 094/202] Doc: Add docs for Model Editor - Add docs for Model Editor view - Update Repeater3D docs - Update Loading Placeholder Data docs - Update Lists and Other Data Models docs Task-number: QDS-11240 Change-Id: I98ad17ee54b94ebe4ac78db9be5349f88664ce76 Reviewed-by: Ali Kianian Reviewed-by: Mats Honkamaa --- .../images/edit-list-model-model-editor.webp | Bin 0 -> 16430 bytes doc/qtdesignstudio/images/icons/export.png | Bin 0 -> 1617 bytes doc/qtdesignstudio/images/icons/import.png | Bin 0 -> 1834 bytes .../images/model-editor-new-model.webp | Bin 0 -> 5832 bytes .../images/repeater3d-listmodel-navigator.png | Bin 5996 -> 4705 bytes .../images/repeater3d-model-editor.webp | Bin 0 -> 10202 bytes .../src/components/qtquick-data-models.qdoc | 14 +-- .../src/qtdesignstudio-toc.qdoc | 1 + .../qtdesignstudio-3d-repeater-3d.qdoc | 106 ++++++------------ .../src/views/qtquick-effect-maker-view.qdoc | 2 +- .../src/views/studio-model-editor.qdoc | 58 ++++++++++ .../src/views/studio-workspaces.qdoc | 2 +- 12 files changed, 103 insertions(+), 80 deletions(-) create mode 100644 doc/qtdesignstudio/images/edit-list-model-model-editor.webp create mode 100644 doc/qtdesignstudio/images/icons/export.png create mode 100644 doc/qtdesignstudio/images/icons/import.png create mode 100644 doc/qtdesignstudio/images/model-editor-new-model.webp create mode 100644 doc/qtdesignstudio/images/repeater3d-model-editor.webp create mode 100644 doc/qtdesignstudio/src/views/studio-model-editor.qdoc diff --git a/doc/qtdesignstudio/images/edit-list-model-model-editor.webp b/doc/qtdesignstudio/images/edit-list-model-model-editor.webp new file mode 100644 index 0000000000000000000000000000000000000000..e7f47c6402cf513408e3abf1fcdd02849b60ef31 GIT binary patch literal 16430 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 KzO-M;!vFx?47j-f literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/icons/export.png b/doc/qtdesignstudio/images/icons/export.png new file mode 100644 index 0000000000000000000000000000000000000000..6d6f15bd4a8d1cba8ea822d419901a4bc6779a41 GIT binary patch literal 1617 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7I14-?iy0WWg+Z8+Vb&Z8 z1_oA@%#etZ2wxwokg&dz0$52&wylyQ$U=n(-v9;Y{GwC^OFcu~WCH^u1#?TiG-Fef)U;$B1tS9^ zV|_zIeFIBfLnA8_11lqQ1t?ImQ?MyYNwW%aaf4b`l#*tvlu=SrV5P5LUS6(OZmgGI zl&)`RX=$l%V5Dzkq+67drdwQ@SCUwvn^&w1Gr=XbIJqdZpd>RtPXT0NVp4u-iLH_n z)YyvL0=Thx#n5m{&d=4aNG#Ad)H4A23GCUFWVpJ5(xM!&kGF7t6Oq&;Z_uvxR#aRS6v)ZS&*t9lv*lEl2^R8JRMrHb4Fz0AxMt3=CWlayqOR9yq(G(+7KOOrHR z3u99gUBg5}a|2`ZGy^kJQ<#32{Nz$lq{H;5kf`6kC^J1XFENK~b3l0!lv=DjGK)*{ ziz=a+B{;PZ%F4-904ETu#AHyyw^d38#dvCp5>z;o>=1w$Tb_}chc~(*%8K*TO3D+9 zQXSJ%^GYa5Y5H~wHu|943-O{pIIkf29xR8H(_zYkT--oRaH#|;q3je8MPF)Oimg(S zlD*x&5IS!ulY;-7#n>=Rups_sT0=FR|CKcjwlyc`A1( zO*$5oQzJI}3FjJ~6x}p&owWK*%Oz(NzuRg2ecmD+Z`m7*4wTMTC=s#0-WpzEdG&#) zdBytp!p`l%Ja&f1&C4u~yZcE*^UQno*=g3n^ZR!@vhmhcS1@M#{BDu-*~oVz!SGN* zqwj>a5{blu19}Uj0`Dw%#dS&HOYXvQv%p`6Hn#qsR5anRVCWH+-(nYI-9Ou^1$Mo$ ul`~qT=8-P>PV94ssN6=A8fDhBh`)@t)RkS$>*ibm75bj8elF{r5}E+k4hC@m literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/icons/import.png b/doc/qtdesignstudio/images/icons/import.png new file mode 100644 index 0000000000000000000000000000000000000000..b4ca149761be585a810c85210bf9ed0d4cee994a GIT binary patch literal 1834 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7I14-?iy0WWg+Z8+Vb&Z8 z1_m~s%#etZ2wxwokg&dz0$52&wylyQ$U=n(-v9;Y{GwC^OFcu~WCH^u1#?Til%z!Cltg141tS9^ zV|_y-eM4hi15+ylGbRtPXT0NVp4u-iLH_n z)YyvL0=Thx#n5m{&d=4aNG#Ad)H4A23GCUFWVpJ5(xM!&kGF7t6Oq&;Z_uvxR#aRS6v)ZS&*t9lv=^@L5P8+m8q$ffeDJz)RM%M#F9jUic^xU((;RP6H9EBGIJBtQ}qk-(v=k8 zA#CNGpOatYo1c+q`%`FWR4N`PdQ_W3u z4UJ7wbuG+N4Ry^;4UG&elM{_n49sBq{fjcwGxHL2$TkO*7eT4T$|JM5B)_NOa*WPu}Vw^C45_@WKfK!rYJ#$GqHpKaXx?;Tb_}chc~(*%8K*TO3D+9QXSJ% z^GYa5Y5KVQRh(Fc-&e%NE5x~|zQU3+A<78*${d=1j1A4f`Nz~i!N}0mEX~3gHUB`& zOU_8lOHWO)Rnk`i`BNWMN^1X-lPY9{rjo++t-OC;j7CLvXOe zMU{%42F-czKa?5jZaDU!uYvIzBm18e7k^X6{T3oCgr3Maze(mb^nS7Gj_SUI_P{l< zZ|*95pLnnE$n~o(r(+yii<_bs2!Gf7EE(!xJ#X5f^kw>0w-{?{lBWv%jrIK{FaLtw kXT#BmU#>wJ3)@e!mfTiY@1Hfl1yq}Py85}Sb4q9e0HJ+J-~a#s literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/model-editor-new-model.webp b/doc/qtdesignstudio/images/model-editor-new-model.webp new file mode 100644 index 0000000000000000000000000000000000000000..6ea705a047b9b83cf86a385aeb2dc40decdf11bf GIT binary patch literal 5832 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/repeater3d-listmodel-navigator.png b/doc/qtdesignstudio/images/repeater3d-listmodel-navigator.png index f4b3093bbbbf7cbb392edfad85746c6ee85e919b..302d7d397a1fa57f2579339eab9e3f14c9a644b2 100644 GIT binary patch literal 4705 zcmeAS@N?(olHy`uVBq!ia0y~yVDw{PVASGZW?*3Oec>t2z`(R7z$e62OH0ex*f@OW zpA{=sNJvQBzkk1`x_ZHa1zNt{xmsGeEADIMu2_+)rKy$ctEF{+MQ*pRmT&iwBi+7Q zTD~jpuQ+m~d+)ye-A9hhoH?`m$o+{EC*Hrm;{K8D%F0SlPtT5yjwMT$+`oS$GBPqW zH1x>*`^Lt#;XAkg|Nq}<=N~~q!4>!KFI%=OC@9E#=l<{olfuKp_4M@e^733=T`epu z?2V1JwY8^ComN^}>g434Etsuq?qFzWC>Xfi*f{E7ow4_fGet#3;qBSh*4Fy^`g`~8 ztvmSNxbEPt-Fr4|+T`o&>$CIEiu*?nA3j`HR~MfU9}^Sf@9(eO{^!h@Ge_>PFfE!L z8yg$E;eGhdTXH!!Gcz-jl9J3!O}WJt%ohAPa{tJhwQFb3p552i=M*UDHUGx7Yu7v{ z9FPm#K5yQjT zL2)xbm@|3|9<0?{r?{>R4|?tKA|=>H8rp*%FWHK&e&L0P%wDyE4QWh z+@_q$?LIPV)~tom#)7Iw2mk*MTJs`$;lmp@Zg|eQ>d?7MP%v9i(9vz_pI5J5J%0T7 z$dMz)(F-*rDvqqkmGjS15p?7dmHYqyVfTvrxhszR|9`Oi$cpf#^Bqd2-(S(K6W_EV zH&;$DTfsH%;lgNH`%p7uM-c-DUTHNJ4(=a6ejNP&(0k{-6)TQB{D1J^|AXN>_uHrJ zcdXg=@!-ZIM^@Zlv7)~^P}0m(-_FB( z!k-mK?mO+g+1-8Q(4j*|R;+NYS#0bx->iL-pz)*?-Q5T4>Q=1CJ<^@q-JP48o2%uU zn+r-~xlZlbE4qDG@;i7mP4d&U0J=PH|0mwvhDg|c6N#=W>$xtag`Z#mml%w9)Jyd&Rc zUp4hT;yLHmTD3XvWqeM2?6YX)xz1FPZFg{q<^FfcT(34-viL^x7e2Gd6?U8$Y+HYMlizNZb^5tpI%sU4%Qg+>)Rx6m!QzH2tL(W@Mp>9Lt;k`Gd!Ntmi;H>$a(w6Ke{GpD&(;7SbjET^?#%j zAKx(bjWfUStDg>YQjL9BUK;QAnzLhzZ`>{xr|>CVA=Bra6)s$LhI2}lpn74J`6Cs- zw{CMxpOi$GhNMQ`PPJTn&2NF$>dR}HH`e%-TL(;iUe+8Tr+)TX)2l5_5pwE(XXPcn z?3ip}DqSeFye#-o=11M=XPX(8a;2|cAG%{<$b*u%FIm@=Gygf8Y+@(T^yuxoCbe}o z%TxPZi;hdznz1alUl?&D(sKE8lM}9Urs{udlkPOy+!z>`egnr@t%EdevA_>yo;mWiDgvwS>W|j^abC7zU*W;eyQf_I5j4ZkZ2G52B(;8m&+R2fn zpYEq@yZrslRQ3P1)-P^l+G(%7oXcwXt2gLsr0QnR{R@H>ABiaY<+{%~vhr@nWQ(of zP*V1LbS3YI=N!`)HT82uweQu-iMY>Mv~2##r{V8B=j{A7v*zytlSgMn(?8v1St^*W z7p4^;Rd|j6`LnA5yMpg7m>Mxr$koz(9ZTHtwOa!Md@ZBD1Q`9DtiIeqdE+5Ixv%1n zW47C`mXwp!{PJi1oL{@u!+&mhq?!0d-DqZuylKC&rQa2s4du_*F`fT^;^waG?tMqU zW-4bW1?m+UNY0%*b7rsF^UFT-KEE=%vAgoLr}&d_)92Gzi<5qdUGRETW@$cGAne!P znLAr_)Z69+1Qecom%Lz$>MyrPKKD;=nDeY~n&qsp*3Gw{2ieUn|H^yh=ReWMPrscF ze}DDC8#&88oww_S@b++sC zlB4Igy_kM~(-raM8!jme-qiMDKNS95pTAtkN}cCe(!=O!zoOnqUeOWqux)SuxIe1K z?~Bf2&giYjBzK-XRrshTs$u�}+?${jZ;YDztT-Q-5RUY_a8y7CaB?IX7lbnzd}* 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 E0I^{~EaktaqI2e>YSL+ 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 G2~7ZL`NISN diff --git a/doc/qtdesignstudio/images/repeater3d-model-editor.webp b/doc/qtdesignstudio/images/repeater3d-model-editor.webp new file mode 100644 index 0000000000000000000000000000000000000000..e0e608736688e0f263656bedefee6fe3ad3dd505 GIT binary patch literal 10202 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 \uicontrol {Default Components} > \uicontrol Views to the \uicontrol Navigator or \uicontrol {2D} view. \li Right-click the view in \uicontrol Navigator, and select - \uicontrol {Edit List Model} in the context-menu to open - the list model editor: - \image qtquick-designer-edit-list-model.png "List view in model editor" - \li Double-click the column headings and cells to change their values. - \li Use the toolbar buttons to add, remove, or move rows and columns. - In a list, each column represents a property and each row adds a + \uicontrol {Edit Model} in the context-menu to open the + \uicontrol {Model Editor} view. + \image edit-list-model-model-editor.webp "List view in Model Editor" + \li Double-click a cell to edit its value. + \li Use the toolbar buttons to add or remove rows and columns. + In a list, each column represents a property, and each row adds a list item. \endlist diff --git a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc index cf00e324194..76779962795 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc @@ -39,6 +39,7 @@ \li \l{Texture Editor} \li \l{Qt Insight} \li \l{Effect Composer} + \li \l{Model Editor} \endlist \li \l{Managing Workspaces} \li \l{Manage sessions} diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-repeater-3d.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-repeater-3d.qdoc index 9e181a8aae5..0f0d0634cab 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-repeater-3d.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-repeater-3d.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -8,10 +8,6 @@ \title Repeater3D - \note The \uicontrol Repeater3D component is released as a tech preview - feature in \QDS 2.2, and its functionality will be improved in future - releases. - The \uicontrol Repeater3D component is used to create multiple similar items. Like other view types, \uicontrol Repeater3D needs a model and a delegate. The delegate sets the item to use and the model sets the @@ -100,90 +96,58 @@ \endlist \image repeater3d-numeric-model.webp - \section1 Adding a Repeater3D Component with a List Model + \section1 Adding a Repeater3D Component with a Model This section explains how to add a \uicontrol Repeater3D component with - a ListModel to your \QDS project: + a model to your \QDS project: To add a \uicontrol Repeater3D component: \list 1 \li Drag a \uicontrol Repeater3D component from \uicontrol Components to \e scene in \uicontrol Navigator. - \li You need to enter the QML code for the \uicontrol ListModel manually. - Go to the \uicontrol {Code} view and enter the following code somewhere - inside the root object: - \code qml - ListModel { - id: planetModel - ListElement { - name: "Mars" - radius: 3.39 - } - ListElement { - name: "Earth" - radius: 6.37 - } - ListElement { - name: "Venus" - radius: 6.05 - } - } - \endcode - The default root object for a \QDS project is \uicontrol Rectangle, so - you can paste the \uicontrol ListModel code, for example, like this: - \code qml - Rectangle { - width: Constants.width - height: Constants.height - color: Constants.backgroundColor - - ListModel { - id: planetModel - ListElement { - name: "Mars" - radius: 3.39 - } - ListElement { - name: "Earth" - radius: 6.37 - } - ListElement { - name: "Venus" - radius: 6.05 - } - } - - View3D { - id: view3D - anchors.fill: parent - ... - \endcode - \li In the \uicontrol {Code} view, add \c {model: planetModel} to the - \uicontrol Repeater3D object to tell that you want to use your - \uicontrol ListModel as the model for the \uicontrol Repeater3D object. - \code qml - Repeater3D { - id: repeater3D - model: planetModel - } - \endcode + \li Go to \uicontrol {Model Editor} and create a new model with the name + \e planetModel. + \li Add the following columns and data to the model. + \raw HTML + + + + + + + + + + + + + + + + + +
name (String)radius (Real)
Mars3.39
Earth6.37
Venus6.05
+ \endraw + \note You can also import a model in JSON or CSV format. See \l {Importing a Data Model}. + \image repeater3d-model-editor.webp + \li In \uicontrol Navigator, select \e{_3DRepeater}. + \li In \uicontrol Properties, set \uicontrol Model to \e {DataStore.planetModel}. \endlist - Now, you have set up the \uicontrol Repeater3D component to use a \uicontrol ListModel to draw the items. Next, you need to add the - item to draw. In this example we are using a \uicontrol Sphere. + item to draw. In this example, you are using a \uicontrol Sphere. \list 1 - \li From \uicontrol Components, drag a \uicontrol Sphere to \e repeater3D + \li From \uicontrol Components, drag a \uicontrol Sphere to \e _3DRepeater in \uicontrol Navigator. \image repeater3d-listmodel-navigator.png - \li Select \e sphere in \uicontrol Navigator and select + \li Select \e sphere in \uicontrol Navigator and in the \Properties view, select \inlineimage icons/action-icon.png next to \uicontrol Scale > \uicontrol X. \li Select \uicontrol {Set binding} to open \uicontrol {Binding Editor}. \li In the binding editor, enter \c{radius}. This sets the X - scale to the radius value defined in the ListModel for each of the sphere + scale to the radius value defined in the model for each of the sphere instances. \image repeater3d-radius-binding.png \li Select \uicontrol OK. @@ -195,7 +159,7 @@ position so you need to change the position to see all spheres. \list 1 - \li Select \e sphere in \uicontrol Navigator and select + \li Select \e sphere in \uicontrol Navigator and in the \uicontrol Properties view, select \inlineimage icons/action-icon.png next to \uicontrol Translation > \uicontrol X. \li Select \uicontrol {Set binding} to open \uicontrol {Binding Editor}. diff --git a/doc/qtdesignstudio/src/views/qtquick-effect-maker-view.qdoc b/doc/qtdesignstudio/src/views/qtquick-effect-maker-view.qdoc index 842e5033ed7..734602cf1e5 100644 --- a/doc/qtdesignstudio/src/views/qtquick-effect-maker-view.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-effect-maker-view.qdoc @@ -4,7 +4,7 @@ /*! \page qtquick-effect-composer-view.html \previouspage studio-qt-insight.html - \nextpage creator-project-managing-workspaces.html + \nextpage studio-model-editor.html \title Effect Composer diff --git a/doc/qtdesignstudio/src/views/studio-model-editor.qdoc b/doc/qtdesignstudio/src/views/studio-model-editor.qdoc new file mode 100644 index 00000000000..e56055c7bd6 --- /dev/null +++ b/doc/qtdesignstudio/src/views/studio-model-editor.qdoc @@ -0,0 +1,58 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page studio-model-editor.html + \previouspage qtquick-effect-composer-view.html + \nextpage creator-project-managing-workspaces.html + + \title Model Editor + + In the \uicontrol {Model Editor} view, you can create, manage, import, and export + data models. With data models, you can, for example, populate views with data. + + \image edit-list-model-model-editor.webp + + For examples of how to use data models, see + \l {Adding a Repeater3D Component with a List Model}. + + \section1 Creating a Data Model + + To create a data model: + \list 1 + \li In \uicontrol {Model Editor}, select \inlineimage {icons/zoomIn.png}. + \li Enter a name and select \uicontrol {Create}. + \endlist + + This creates a single-cell table. + + \image model-editor-new-model.webp + + Next, add columns, rows, and data to the model. + + \note You must manually save the table after you have made changes. To do this, + select \inlineimage {icons/save-effect-composer.png}. + + \section1 Editing a Data Model + + Edit a data model in one of the following ways: + \list + \li Right-click a column name to edit its name and type, delete, or sort it. + \li Double-click a cell to edit its content. + \li Use the toolbar to add and remove columns and rows. + \endlist + + \note You must manually save the table after you have made changes. To do this, + select \inlineimage {icons/save-effect-composer.png}. + + \section1 Importing a Data Model + + Import data models from JSON or CSV files. To do this, select \inlineimage {icons/import.png} + in \uicontrol {Model Editor}. + + \section1 Exporting a Data Model + + Export data models to JSON or CSV files. To do this, select \inlineimage {icons/export.png} + in \uicontrol {Model Editor}. + +*/ diff --git a/doc/qtdesignstudio/src/views/studio-workspaces.qdoc b/doc/qtdesignstudio/src/views/studio-workspaces.qdoc index 407513c40f6..ed79be99d1f 100644 --- a/doc/qtdesignstudio/src/views/studio-workspaces.qdoc +++ b/doc/qtdesignstudio/src/views/studio-workspaces.qdoc @@ -3,7 +3,7 @@ /*! \page creator-project-managing-workspaces.html - \previouspage qtquick-effect-composer-view.html + \previouspage studio-model-editor.html \nextpage creator-project-managing-sessions.html \title Managing Workspaces From da21fa4c3396e867537909b68f4a053114d195c0 Mon Sep 17 00:00:00 2001 From: Teea Poldsam Date: Fri, 5 Apr 2024 09:06:31 +0300 Subject: [PATCH 095/202] Doc: Fix a broken link in Optimizing Designs Fixes: QDS-12415 Change-Id: Ie1e8e3548a0f793adf0a8c82fdcf68018bf3f880 Reviewed-by: Mats Honkamaa --- .../src/overviews/qtquick-optimizing-designs.qdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/qtdesignstudio/src/overviews/qtquick-optimizing-designs.qdoc b/doc/qtdesignstudio/src/overviews/qtquick-optimizing-designs.qdoc index 3cf0c1336b3..937362450b4 100644 --- a/doc/qtdesignstudio/src/overviews/qtquick-optimizing-designs.qdoc +++ b/doc/qtdesignstudio/src/overviews/qtquick-optimizing-designs.qdoc @@ -21,7 +21,7 @@ \endlist For more useful information for application developers, see - \l {Performance Considerations And Suggestions}. + \l {QML Performance Considerations And Suggestions}. For more information about optimizing 3D scenes, see \l{Creating Optimized 3D Scenes}. From bc5628afca0c0716642cd69679d6b51acfa60316 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Thu, 21 Mar 2024 12:47:09 +0200 Subject: [PATCH 096/202] QmlDesigner: Add content library user materials bundle Fixes: QDS-12389 Change-Id: Icec1b06c57e0eaa4ff444e3143d3cba0803c8dd1 Reviewed-by: Miikka Heikkinen --- .../ContentLibrary.qml | 46 ++- .../ContentLibraryMaterial.qml | 13 +- .../ContentLibraryMaterialContextMenu.qml | 13 +- .../ContentLibraryMaterialsView.qml | 25 +- .../ContentLibraryTexturesView.qml | 3 - .../ContentLibraryUserView.qml | 151 +++++++++ .../UnimportBundleMaterialDialog.qml | 23 +- src/plugins/qmldesigner/CMakeLists.txt | 1 + .../contentlibrary/contentlibrarymaterial.h | 3 +- .../contentlibraryusermodel.cpp | 308 ++++++++++++++++++ .../contentlibrary/contentlibraryusermodel.h | 118 +++++++ .../contentlibrary/contentlibraryview.cpp | 81 +++++ .../contentlibrary/contentlibraryview.h | 2 + .../contentlibrary/contentlibrarywidget.cpp | 17 +- .../contentlibrary/contentlibrarywidget.h | 4 + 15 files changed, 761 insertions(+), 47 deletions(-) create mode 100644 share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml create mode 100644 src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp create mode 100644 src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibrary.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibrary.qml index c6db8425ff2..2c98b58adc7 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibrary.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibrary.qml @@ -23,6 +23,7 @@ Item { texturesView.closeContextMenu() environmentsView.closeContextMenu() effectsView.closeContextMenu() + userView.closeContextMenu() HelperWidgets.Controller.closeContextMenu() } @@ -113,10 +114,18 @@ Item { id: tabBar width: parent.width height: StudioTheme.Values.toolbarHeight - tabsModel: [{name: qsTr("Materials"), icon: StudioTheme.Constants.material_medium}, - {name: qsTr("Textures"), icon: StudioTheme.Constants.textures_medium}, - {name: qsTr("Environments"), icon: StudioTheme.Constants.languageList_medium}, - {name: qsTr("Effects"), icon: StudioTheme.Constants.effects}] + + Component.onCompleted: { + var tabs = [ + { name: qsTr("Materials"), icon: StudioTheme.Constants.material_medium }, + { name: qsTr("Textures"), icon: StudioTheme.Constants.textures_medium }, + { name: qsTr("Environments"), icon: StudioTheme.Constants.languageList_medium }, + { name: qsTr("Effects"), icon: StudioTheme.Constants.effects } + ]; + if (ContentLibraryBackend.rootView.userBundleEnabled()) + tabs.push({ name: qsTr("User Assets"), icon: StudioTheme.Constants.effects }); + tabBar.tabsModel = tabs; + } } } } @@ -148,7 +157,8 @@ Item { onUnimport: (bundleMat) => { confirmUnimportDialog.targetBundleItem = bundleMat - confirmUnimportDialog.targetBundleType = "material" + confirmUnimportDialog.targetBundleLabel = "material" + confirmUnimportDialog.targetBundleModel = ContentLibraryBackend.materialsModel confirmUnimportDialog.open() } @@ -208,7 +218,31 @@ Item { onUnimport: (bundleItem) => { confirmUnimportDialog.targetBundleItem = bundleItem - confirmUnimportDialog.targetBundleType = "effect" + confirmUnimportDialog.targetBundleLabel = "effect" + confirmUnimportDialog.targetBundleModel = ContentLibraryBackend.effectsModel + confirmUnimportDialog.open() + } + + onCountChanged: root.responsiveResize(stackLayout.width, stackLayout.height) + } + + ContentLibraryUserView { + id: userView + + adsFocus: root.adsFocus + width: root.width + + cellWidth: root.thumbnailSize + cellHeight: root.thumbnailSize + 20 + numColumns: root.numColumns + hideHorizontalScrollBar: true + + searchBox: searchBox + + onUnimport: (bundleItem) => { + confirmUnimportDialog.targetBundleItem = bundleItem + confirmUnimportDialog.targetBundleLabel = "material" + confirmUnimportDialog.targetBundleModel = ContentLibraryBackend.userModel confirmUnimportDialog.open() } diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterial.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterial.qml index 93b226d6caf..0e9fc4903eb 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterial.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterial.qml @@ -12,12 +12,15 @@ import WebFetcher Item { id: root - signal showContextMenu() - // Download states: "" (ie default, not downloaded), "unavailable", "downloading", "downloaded", // "failed" property string downloadState: modelData.isDownloaded() ? "downloaded" : "" + property bool importerRunning: false + + signal showContextMenu() + signal addToProject() + visible: modelData.bundleMaterialVisible MouseArea { @@ -29,7 +32,7 @@ Item { acceptedButtons: Qt.LeftButton | Qt.RightButton onPressed: (mouse) => { - if (mouse.button === Qt.LeftButton && !materialsModel.importerRunning) { + if (mouse.button === Qt.LeftButton && !root.importerRunning) { if (root.downloadState === "downloaded") ContentLibraryBackend.rootView.startDragMaterial(modelData, mapToGlobal(mouse.x, mouse.y)) } else if (mouse.button === Qt.RightButton && root.downloadState === "downloaded") { @@ -96,12 +99,12 @@ Item { pressColor: Qt.hsla(c.hslHue, c.hslSaturation, c.hslLightness, .4) anchors.right: img.right anchors.bottom: img.bottom - enabled: !ContentLibraryBackend.materialsModel.importerRunning + enabled: !root.importerRunning visible: root.downloadState === "downloaded" && (containsMouse || mouseArea.containsMouse) onClicked: { - ContentLibraryBackend.materialsModel.addToProject(modelData) + root.addToProject() } } diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialContextMenu.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialContextMenu.qml index ca3a05bdd12..b67ec311ef0 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialContextMenu.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialContextMenu.qml @@ -15,8 +15,9 @@ StudioControls.Menu { readonly property bool targetAvailable: targetMaterial && !importerRunning - signal unimport(var bundleMat); - signal addToProject(var bundleMat) + signal unimport(); + signal addToProject() + signal applyToSelected(bool add) function popupMenu(targetMaterial = null) { @@ -29,13 +30,13 @@ StudioControls.Menu { StudioControls.MenuItem { text: qsTr("Apply to selected (replace)") enabled: root.targetAvailable && root.hasModelSelection - onTriggered: materialsModel.applyToSelected(root.targetMaterial, false) + onTriggered: root.applyToSelected(false) } StudioControls.MenuItem { text: qsTr("Apply to selected (add)") enabled: root.targetAvailable && root.hasModelSelection - onTriggered: materialsModel.applyToSelected(root.targetMaterial, true) + onTriggered: root.applyToSelected(true) } StudioControls.MenuSeparator {} @@ -45,7 +46,7 @@ StudioControls.Menu { text: qsTr("Add an instance to project") onTriggered: { - root.addToProject(root.targetMaterial) + root.addToProject() } } @@ -53,6 +54,6 @@ StudioControls.Menu { enabled: root.targetAvailable && root.targetMaterial.bundleMaterialImported text: qsTr("Remove from project") - onTriggered: root.unimport(root.targetMaterial) + onTriggered: root.unimport() } } diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml index c21baf4c580..9a0e33b8e5e 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml @@ -27,8 +27,6 @@ HelperWidgets.ScrollView { root.count = c } - property var currMaterialItem: null - property var rootItem: null property var materialsModel: ContentLibraryBackend.materialsModel required property var searchBox @@ -51,17 +49,19 @@ HelperWidgets.ScrollView { ContentLibraryMaterialContextMenu { id: ctxMenu - hasModelSelection: materialsModel.hasModelSelection - importerRunning: materialsModel.importerRunning + hasModelSelection: root.materialsModel.hasModelSelection + importerRunning: root.materialsModel.importerRunning - onUnimport: (bundleMat) => root.unimport(bundleMat) - onAddToProject: (bundleMat) => materialsModel.addToProject(bundleMat) + onApplyToSelected: (add) => root.materialsModel.applyToSelected(ctxMenu.targetMaterial, add) + + onUnimport: root.unimport(ctxMenu.targetMaterial) + onAddToProject: root.materialsModel.addToProject(ctxMenu.targetMaterial) } Repeater { id: categoryRepeater - model: materialsModel + model: root.materialsModel delegate: HelperWidgets.Section { id: section @@ -73,7 +73,7 @@ HelperWidgets.ScrollView { bottomPadding: StudioTheme.Values.sectionPadding caption: bundleCategoryName - visible: bundleCategoryVisible && !materialsModel.isEmpty + visible: bundleCategoryVisible && !root.materialsModel.isEmpty expanded: bundleCategoryExpanded expandOnClick: false category: "ContentLib_Mat" @@ -103,7 +103,10 @@ HelperWidgets.ScrollView { width: root.cellWidth height: root.cellHeight + importerRunning: root.materialsModel.importerRunning + onShowContextMenu: ctxMenu.popupMenu(modelData) + onAddToProject: root.materialsModel.addToProject(modelData) } onCountChanged: root.assignMaxCount() @@ -115,13 +118,13 @@ HelperWidgets.ScrollView { Text { id: infoText text: { - if (!materialsModel.matBundleExists) + if (!root.materialsModel.matBundleExists) qsTr("No materials available. Make sure you have internet connection.") else if (!ContentLibraryBackend.rootView.isQt6Project) qsTr("Content Library materials are not supported in Qt5 projects.") else if (!ContentLibraryBackend.rootView.hasQuick3DImport) qsTr("To use Content Library, first add the QtQuick3D module in the Components view.") - else if (!materialsModel.hasRequiredQuick3DImport) + else if (!root.materialsModel.hasRequiredQuick3DImport) qsTr("To use Content Library, version 6.3 or later of the QtQuick3D module is required.") else if (!ContentLibraryBackend.rootView.hasMaterialLibrary) qsTr("Content Library is disabled inside a non-visual component.") @@ -134,7 +137,7 @@ HelperWidgets.ScrollView { font.pixelSize: StudioTheme.Values.baseFontSize topPadding: 10 leftPadding: 10 - visible: materialsModel.isEmpty + visible: root.materialsModel.isEmpty } } } diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexturesView.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexturesView.qml index 1fac9f2234e..617b724e664 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexturesView.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexturesView.qml @@ -27,9 +27,6 @@ HelperWidgets.ScrollView { root.count = c } - property var currMaterialItem: null - property var rootItem: null - required property var searchBox required property var model required property string sectionCategory diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml new file mode 100644 index 00000000000..d3d1dbad92d --- /dev/null +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml @@ -0,0 +1,151 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import HelperWidgets as HelperWidgets +import StudioControls as StudioControls +import StudioTheme as StudioTheme +import ContentLibraryBackend + +HelperWidgets.ScrollView { + id: root + + clip: true + interactive: !ctxMenu.opened && !ContentLibraryBackend.rootView.isDragging + && !HelperWidgets.Controller.contextMenuOpened + + property real cellWidth: 100 + property real cellHeight: 120 + property int numColumns: 4 + + property int count: 0 + function assignMaxCount() { + let c = 0 + for (let i = 0; i < categoryRepeater.count; ++i) + c = Math.max(c, categoryRepeater.itemAt(i)?.count ?? 0) + + root.count = c + } + + required property var searchBox + + signal unimport(var bundleItem); + + function closeContextMenu() { + ctxMenu.close() + } + + function expandVisibleSections() { + for (let i = 0; i < categoryRepeater.count; ++i) { + let cat = categoryRepeater.itemAt(i) + if (cat.visible && !cat.expanded) + cat.expandSection() + } + } + + Column { + ContentLibraryMaterialContextMenu { + id: ctxMenu + + hasModelSelection: ContentLibraryBackend.userModel.hasModelSelection + importerRunning: ContentLibraryBackend.userModel.importerRunning + + onApplyToSelected: (add) => ContentLibraryBackend.userModel.applyToSelected(ctxMenu.targetMaterial, add) + + onUnimport: root.unimport(ctxMenu.targetMaterial) + onAddToProject: ContentLibraryBackend.userModel.addToProject(ctxMenu.targetMaterial) + } + + Repeater { + id: categoryRepeater + + model: ContentLibraryBackend.userModel + + delegate: HelperWidgets.Section { + id: section + + width: root.width + leftPadding: StudioTheme.Values.sectionPadding + rightPadding: StudioTheme.Values.sectionPadding + topPadding: StudioTheme.Values.sectionPadding + bottomPadding: StudioTheme.Values.sectionPadding + + caption: categoryName + visible: categoryVisible + expanded: categoryExpanded + expandOnClick: false + category: "ContentLib_User" + + onToggleExpand: categoryExpanded = !categoryExpanded + onExpand: categoryExpanded = true + onCollapse: categoryExpanded = false + + function expandSection() { + categoryExpanded = true + } + + property alias count: repeater.count + + onCountChanged: root.assignMaxCount() + + property int numVisibleItem: 1 // initially, the tab is invisible so this will be 0 + + Grid { + width: section.width - section.leftPadding - section.rightPadding + spacing: StudioTheme.Values.sectionGridSpacing + columns: root.numColumns + + Repeater { + id: repeater + model: categoryItems + + delegate: ContentLibraryMaterial { + width: root.cellWidth + height: root.cellHeight + + importerRunning: ContentLibraryBackend.userModel.importerRunning + + onShowContextMenu: ctxMenu.popupMenu(modelData) + onAddToProject: ContentLibraryBackend.userModel.addToProject(modelData) + + onVisibleChanged: { + section.numVisibleItem += visible ? 1 : -1 + } + } + + onCountChanged: root.assignMaxCount() + } + } + + Text { + text: qsTr("No match found."); + color: StudioTheme.Values.themeTextColor + font.pixelSize: StudioTheme.Values.baseFontSize + leftPadding: 10 + visible: !searchBox.isEmpty() && section.numVisibleItem === 0 + } + } + } + + Text { + id: infoText + text: { + if (!ContentLibraryBackend.effectsModel.bundleExists) + qsTr("User bundle couldn't be found.") + else if (!ContentLibraryBackend.rootView.isQt6Project) + qsTr("Content Library is not supported in Qt5 projects.") + else if (!ContentLibraryBackend.rootView.hasQuick3DImport) + qsTr("To use Content Library, first add the QtQuick3D module in the Components view.") + else if (!ContentLibraryBackend.rootView.hasMaterialLibrary) + qsTr("Content Library is disabled inside a non-visual component.") + else + "" + } + color: StudioTheme.Values.themeTextColor + font.pixelSize: StudioTheme.Values.baseFontSize + topPadding: 10 + leftPadding: 10 + visible: ContentLibraryBackend.effectsModel.isEmpty + } + } +} diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/UnimportBundleMaterialDialog.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/UnimportBundleMaterialDialog.qml index 48be045d8bd..4385e3bf82e 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/UnimportBundleMaterialDialog.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/UnimportBundleMaterialDialog.qml @@ -12,24 +12,27 @@ import ContentLibraryBackend StudioControls.Dialog { id: root - title: qsTr("Bundle material might be in use") + property var targetBundleItem + property var targetBundleLabel // "effect" or "material" + property var targetBundleModel + + title: qsTr("Bundle %1 might be in use").arg(root.targetBundleLabel) anchors.centerIn: parent closePolicy: Popup.CloseOnEscape implicitWidth: 300 modal: true - property var targetBundleType // "effect" or "material" - property var targetBundleItem + onOpened: warningText.forceActiveFocus() contentItem: Column { spacing: 20 width: parent.width Text { - id: folderNotEmpty + id: warningText - text: qsTr("If the %1 you are removing is in use, it might cause the project to malfunction.\n\nAre you sure you want to remove the %1?") - .arg(root.targetBundleType) + text: qsTr("If the %1 you are removing is in use, it might cause the project to malfunction.\n\nAre you sure you want to remove it?") + .arg(root.targetBundleLabel) color: StudioTheme.Values.themeTextColor wrapMode: Text.WordWrap anchors.right: parent.right @@ -49,11 +52,7 @@ StudioControls.Dialog { text: qsTr("Remove") onClicked: { - if (root.targetBundleType === "material") - ContentLibraryBackend.materialsModel.removeFromProject(root.targetBundleItem) - else if (root.targetBundleType === "effect") - ContentLibraryBackend.effectsModel.removeFromProject(root.targetBundleItem) - + root.targetBundleModel.removeFromProject(root.targetBundleItem) root.accept() } } @@ -64,6 +63,4 @@ StudioControls.Dialog { } } } - - onOpened: folderNotEmpty.forceActiveFocus() } diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 01f03cc2712..1c7bebffa32 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -826,6 +826,7 @@ extend_qtc_plugin(QmlDesigner contentlibraryeffect.cpp contentlibraryeffect.h contentlibraryeffectscategory.cpp contentlibraryeffectscategory.h contentlibraryeffectsmodel.cpp contentlibraryeffectsmodel.h + contentlibraryusermodel.cpp contentlibraryusermodel.h ) extend_qtc_plugin(QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h index f546ea98cd3..90d8468fa16 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h @@ -3,9 +3,8 @@ #pragma once -#include "qmldesignercorelib_global.h" +#include "nodeinstanceglobal.h" -#include #include #include diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp new file mode 100644 index 00000000000..56ba8cc6c73 --- /dev/null +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp @@ -0,0 +1,308 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "contentlibraryusermodel.h" + +#include "contentlibrarybundleimporter.h" +#include "contentlibrarymaterial.h" +#include "contentlibrarymaterialscategory.h" +#include "contentlibrarywidget.h" + +#include +#include "qmldesignerconstants.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace QmlDesigner { + +ContentLibraryUserModel::ContentLibraryUserModel(ContentLibraryWidget *parent) + : QAbstractListModel(parent) + , m_widget(parent) +{ + m_userCategories = {tr("Materials")/*, tr("Textures"), tr("3D"), tr("Effects"), tr("2D components")*/}; // TODO + + loadUserBundle(); +} + +int ContentLibraryUserModel::rowCount(const QModelIndex &) const +{ + return m_userCategories.size(); +} + +QVariant ContentLibraryUserModel::data(const QModelIndex &index, int role) const +{ + QTC_ASSERT(index.isValid() && index.row() < m_userCategories.size(), return {}); + QTC_ASSERT(roleNames().contains(role), return {}); + + if (role == NameRole) + return m_userCategories.at(index.row()); + + if (role == ItemsRole) { + if (index.row() == 0) + return QVariant::fromValue(m_userMaterials); + if (index.row() == 1) + return QVariant::fromValue(m_userTextures); + if (index.row() == 2) + return QVariant::fromValue(m_user3DItems); + if (index.row() == 3) + return QVariant::fromValue(m_userEffects); + } + + if (role == VisibleRole) + return true; // TODO + + if (role == ExpandedRole) + return true; // TODO + + return {}; +} + +bool ContentLibraryUserModel::isValidIndex(int idx) const +{ + return idx > -1 && idx < rowCount(); +} + +void ContentLibraryUserModel::updateIsEmpty() +{ + bool anyMatVisible = Utils::anyOf(m_userMaterials, [&](ContentLibraryMaterial *mat) { + return mat->visible(); + }); + + bool newEmpty = !anyMatVisible || !m_widget->hasMaterialLibrary() || !hasRequiredQuick3DImport(); + + if (newEmpty != m_isEmpty) { + m_isEmpty = newEmpty; + emit isEmptyChanged(); + } +} + +QHash ContentLibraryUserModel::roleNames() const +{ + static const QHash roles { + {NameRole, "categoryName"}, + {VisibleRole, "categoryVisible"}, + {ExpandedRole, "categoryExpanded"}, + {ItemsRole, "categoryItems"} + }; + return roles; +} + +void ContentLibraryUserModel::createImporter(const QString &bundlePath, const QString &bundleId, + const QStringList &sharedFiles) +{ + m_importer = new Internal::ContentLibraryBundleImporter(bundlePath, bundleId, sharedFiles); +#ifdef QDS_USE_PROJECTSTORAGE + connect(m_importer, + &Internal::ContentLibraryBundleImporter::importFinished, + this, + [&](const QmlDesigner::TypeName &typeName) { + m_importerRunning = false; + emit importerRunningChanged(); + if (typeName.size()) + emit bundleMaterialImported(typeName); + }); +#else + connect(m_importer, + &Internal::ContentLibraryBundleImporter::importFinished, + this, + [&](const QmlDesigner::NodeMetaInfo &metaInfo) { + m_importerRunning = false; + emit importerRunningChanged(); + if (metaInfo.isValid()) + emit bundleMaterialImported(metaInfo); + }); +#endif + + connect(m_importer, &Internal::ContentLibraryBundleImporter::unimportFinished, this, + [&](const QmlDesigner::NodeMetaInfo &metaInfo) { + Q_UNUSED(metaInfo) + m_importerRunning = false; + emit importerRunningChanged(); + emit bundleMaterialUnimported(metaInfo); + }); + + resetModel(); + updateIsEmpty(); +} + +void ContentLibraryUserModel::loadUserBundle() +{ + if (m_matBundleExists) + return; + + QDir bundleDir{Paths::bundlesPathSetting() + "/User/materials"}; + + if (m_bundleObj.isEmpty()) { + QFile matsJsonFile(bundleDir.filePath("user_materials_bundle.json")); + + if (!matsJsonFile.open(QIODevice::ReadOnly)) { + qWarning("Couldn't open user_materials_bundle.json"); + return; + } + + QJsonDocument matBundleJsonDoc = QJsonDocument::fromJson(matsJsonFile.readAll()); + if (matBundleJsonDoc.isNull()) { + qWarning("Invalid user_materials_bundle.json file"); + return; + } else { + m_bundleObj = matBundleJsonDoc.object(); + } + } + + QString bundleId = m_bundleObj.value("id").toString(); + + // parse materials + const QJsonObject matsObj = m_bundleObj.value("materials").toObject(); + const QStringList materialNames = matsObj.keys(); + for (const QString &matName : materialNames) { + const QJsonObject matObj = matsObj.value(matName).toObject(); + + QStringList files; + const QJsonArray assetsArr = matObj.value("files").toArray(); + for (const auto /*QJson{Const,}ValueRef*/ &asset : assetsArr) + files.append(asset.toString()); + + QUrl icon = QUrl::fromLocalFile(bundleDir.filePath(matObj.value("icon").toString())); + QString qml = matObj.value("qml").toString(); + + TypeName type = QLatin1String("%1.%2.%3").arg( + QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1), + bundleId, + qml.chopped(4)).toLatin1(); // chopped(4): remove .qml + + auto userMat = new ContentLibraryMaterial(this, matName, qml, type, icon, files, + bundleDir.path(), ""); + + m_userMaterials.append(userMat); + } + + QStringList sharedFiles; + const QJsonArray sharedFilesArr = m_bundleObj.value("sharedFiles").toArray(); + for (const auto /*QJson{Const,}ValueRef*/ &file : sharedFilesArr) + sharedFiles.append(file.toString()); + + createImporter(bundleDir.path(), bundleId, sharedFiles); + + m_matBundleExists = true; + emit matBundleExistsChanged(); +} + +bool ContentLibraryUserModel::hasRequiredQuick3DImport() const +{ + return m_widget->hasQuick3DImport() && m_quick3dMajorVersion == 6 && m_quick3dMinorVersion >= 3; +} + +bool ContentLibraryUserModel::matBundleExists() const +{ + return m_matBundleExists; +} + +Internal::ContentLibraryBundleImporter *ContentLibraryUserModel::bundleImporter() const +{ + return m_importer; +} + +void ContentLibraryUserModel::setSearchText(const QString &searchText) +{ + QString lowerSearchText = searchText.toLower(); + + if (m_searchText == lowerSearchText) + return; + + m_searchText = lowerSearchText; + + for (ContentLibraryMaterial *mat : std::as_const(m_userMaterials)) + mat->filter(m_searchText); + + updateIsEmpty(); +} + +void ContentLibraryUserModel::updateImportedState(const QStringList &importedMats) +{ + bool changed = false; + + for (ContentLibraryMaterial *mat : std::as_const(m_userMaterials)) + changed |= mat->setImported(importedMats.contains(mat->qml().chopped(4))); + + if (changed) + resetModel(); +} + +void ContentLibraryUserModel::setQuick3DImportVersion(int major, int minor) +{ + bool oldRequiredImport = hasRequiredQuick3DImport(); + + m_quick3dMajorVersion = major; + m_quick3dMinorVersion = minor; + + bool newRequiredImport = hasRequiredQuick3DImport(); + + if (oldRequiredImport == newRequiredImport) + return; + + emit hasRequiredQuick3DImportChanged(); + + updateIsEmpty(); +} + +void ContentLibraryUserModel::resetModel() +{ + beginResetModel(); + endResetModel(); +} + +void ContentLibraryUserModel::applyToSelected(ContentLibraryMaterial *mat, bool add) +{ + emit applyToSelectedTriggered(mat, add); +} + +void ContentLibraryUserModel::addToProject(ContentLibraryMaterial *mat) +{ + QString err = m_importer->importComponent(mat->qml(), mat->files()); + + if (err.isEmpty()) { + m_importerRunning = true; + emit importerRunningChanged(); + } else { + qWarning() << __FUNCTION__ << err; + } +} + +void ContentLibraryUserModel::removeFromProject(ContentLibraryMaterial *mat) +{ + emit bundleMaterialAboutToUnimport(mat->type()); + + QString err = m_importer->unimportComponent(mat->qml()); + + if (err.isEmpty()) { + m_importerRunning = true; + emit importerRunningChanged(); + } else { + qWarning() << __FUNCTION__ << err; + } +} + +bool ContentLibraryUserModel::hasModelSelection() const +{ + return m_hasModelSelection; +} + +void ContentLibraryUserModel::setHasModelSelection(bool b) +{ + if (b == m_hasModelSelection) + return; + + m_hasModelSelection = b; + emit hasModelSelectionChanged(); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h new file mode 100644 index 00000000000..72c535e4014 --- /dev/null +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h @@ -0,0 +1,118 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "nodemetainfo.h" + +#include +#include + +namespace QmlDesigner { + +class ContentLibraryEffect; +class ContentLibraryMaterial; +class ContentLibraryTexture; +class ContentLibraryWidget; + +namespace Internal { +class ContentLibraryBundleImporter; +} + +class ContentLibraryUserModel : public QAbstractListModel +{ + Q_OBJECT + + Q_PROPERTY(bool matBundleExists READ matBundleExists NOTIFY matBundleExistsChanged) + Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) + Q_PROPERTY(bool hasRequiredQuick3DImport READ hasRequiredQuick3DImport NOTIFY hasRequiredQuick3DImportChanged) + Q_PROPERTY(bool hasModelSelection READ hasModelSelection NOTIFY hasModelSelectionChanged) + Q_PROPERTY(bool importerRunning MEMBER m_importerRunning NOTIFY importerRunningChanged) + Q_PROPERTY(QList userMaterials MEMBER m_userMaterials NOTIFY userMaterialsChanged) + Q_PROPERTY(QList userTextures MEMBER m_userTextures NOTIFY userTexturesChanged) + Q_PROPERTY(QList user3DItems MEMBER m_user3DItems NOTIFY user3DItemsChanged) + Q_PROPERTY(QList userEffects MEMBER m_userEffects NOTIFY userEffectsChanged) + +public: + ContentLibraryUserModel(ContentLibraryWidget *parent = nullptr); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + QHash roleNames() const override; + + void setSearchText(const QString &searchText); + void updateImportedState(const QStringList &importedMats); + + void setQuick3DImportVersion(int major, int minor); + + bool hasRequiredQuick3DImport() const; + + bool matBundleExists() const; + + bool hasModelSelection() const; + void setHasModelSelection(bool b); + + void resetModel(); + void updateIsEmpty(); + + Internal::ContentLibraryBundleImporter *bundleImporter() const; + + Q_INVOKABLE void applyToSelected(QmlDesigner::ContentLibraryMaterial *mat, bool add = false); + Q_INVOKABLE void addToProject(QmlDesigner::ContentLibraryMaterial *mat); + Q_INVOKABLE void removeFromProject(QmlDesigner::ContentLibraryMaterial *mat); + +signals: + void isEmptyChanged(); + void hasRequiredQuick3DImportChanged(); + void hasModelSelectionChanged(); + void userMaterialsChanged(); + void userTexturesChanged(); + void user3DItemsChanged(); + void userEffectsChanged(); + + void applyToSelectedTriggered(QmlDesigner::ContentLibraryMaterial *mat, bool add = false); + +#ifdef QDS_USE_PROJECTSTORAGE + void bundleMaterialImported(const QmlDesigner::TypeName &typeName); +#else + void bundleMaterialImported(const QmlDesigner::NodeMetaInfo &metaInfo); +#endif + void bundleMaterialAboutToUnimport(const QmlDesigner::TypeName &type); + void bundleMaterialUnimported(const QmlDesigner::NodeMetaInfo &metaInfo); + void importerRunningChanged(); + void matBundleExistsChanged(); + +private: + void loadUserBundle(); + bool isValidIndex(int idx) const; + void createImporter(const QString &bundlePath, const QString &bundleId, + const QStringList &sharedFiles); + + ContentLibraryWidget *m_widget = nullptr; + QString m_searchText; + + QList m_userMaterials; + QList m_userTextures; + QList m_userEffects; + QList m_user3DItems; + QStringList m_userCategories; + + QJsonObject m_bundleObj; + Internal::ContentLibraryBundleImporter *m_importer = nullptr; + + bool m_isEmpty = true; + bool m_matBundleExists = false; + bool m_hasModelSelection = false; + bool m_importerRunning = false; + + int m_quick3dMajorVersion = -1; + int m_quick3dMinorVersion = -1; + + QString m_importerBundlePath; + QString m_importerBundleId; + QStringList m_importerSharedFiles; + + enum Roles { NameRole = Qt::UserRole + 1, VisibleRole, ExpandedRole, ItemsRole }; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp index 61ae078ea8d..37ad648c3a4 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp @@ -10,6 +10,7 @@ #include "contentlibrarymaterialsmodel.h" #include "contentlibrarytexture.h" #include "contentlibrarytexturesmodel.h" +#include "contentlibraryusermodel.h" #include "contentlibrarywidget.h" #include "externaldependenciesinterface.h" #include "nodelistproperty.h" @@ -204,6 +205,8 @@ WidgetInfo ContentLibraryView::widgetInfo() connect(effectsModel, &ContentLibraryEffectsModel::bundleItemUnimported, this, &ContentLibraryView::updateBundleEffectsImportedState); + + connectUserBundle(); } return createWidgetInfo(m_widget.data(), @@ -213,6 +216,64 @@ WidgetInfo ContentLibraryView::widgetInfo() tr("Content Library")); } +void ContentLibraryView::connectUserBundle() +{ + ContentLibraryUserModel *userModel = m_widget->userModel().data(); + + connect(userModel, + &ContentLibraryUserModel::applyToSelectedTriggered, + this, + [&](ContentLibraryMaterial *bundleMat, bool add) { + if (m_selectedModels.isEmpty()) + return; + + m_bundleMaterialTargets = m_selectedModels; + m_bundleMaterialAddToSelected = add; + + ModelNode defaultMat = getBundleMaterialDefaultInstance(bundleMat->type()); + if (defaultMat.isValid()) + applyBundleMaterialToDropTarget(defaultMat); + else + m_widget->userModel()->addToProject(bundleMat); + }); + +#ifdef QDS_USE_PROJECTSTORAGE + connect(userModel, + &ContentLibraryUserModel::bundleMaterialImported, + this, + [&](const QmlDesigner::TypeName &typeName) { + applyBundleMaterialToDropTarget({}, typeName); + updateBundleUserMaterialsImportedState(); + }); +#else + connect(userModel, + &ContentLibraryUserModel::bundleMaterialImported, + this, + [&](const QmlDesigner::NodeMetaInfo &metaInfo) { + applyBundleMaterialToDropTarget({}, metaInfo); + updateBundleUserMaterialsImportedState(); + }); +#endif + + connect(userModel, &ContentLibraryUserModel::bundleMaterialAboutToUnimport, this, + [&] (const QmlDesigner::TypeName &type) { + // delete instances of the bundle material that is about to be unimported + executeInTransaction("ContentLibraryView::connectUserModel", [&] { + ModelNode matLib = Utils3D::materialLibraryNode(this); + if (!matLib.isValid()) + return; + + Utils::reverseForeach(matLib.directSubModelNodes(), [&](const ModelNode &mat) { + if (mat.isValid() && mat.type() == type) + QmlObjectNode(mat).destroy(); + }); + }); + }); + + connect(userModel, &ContentLibraryUserModel::bundleMaterialUnimported, this, + &ContentLibraryView::updateBundleUserMaterialsImportedState); +} + void ContentLibraryView::modelAttached(Model *model) { AbstractView::modelAttached(model); @@ -276,6 +337,7 @@ void ContentLibraryView::selectedNodesChanged(const QList &selectedNo }); m_widget->materialsModel()->setHasModelSelection(!m_selectedModels.isEmpty()); + m_widget->userModel()->setHasModelSelection(!m_selectedModels.isEmpty()); } void ContentLibraryView::customNotification(const AbstractView *view, @@ -548,6 +610,25 @@ void ContentLibraryView::updateBundleMaterialsImportedState() m_widget->materialsModel()->updateImportedState(importedBundleMats); } +void ContentLibraryView::updateBundleUserMaterialsImportedState() +{ + using namespace Utils; + + if (!m_widget->userModel()->bundleImporter()) + return; + + QStringList importedBundleMats; + + FilePath bundlePath = m_widget->userModel()->bundleImporter()->resolveBundleImportPath(); + + if (bundlePath.exists()) { + importedBundleMats = transform(bundlePath.dirEntries({{"*.qml"}, QDir::Files}), + [](const FilePath &f) { return f.fileName().chopped(4); }); + } + + m_widget->userModel()->updateImportedState(importedBundleMats); +} + void ContentLibraryView::updateBundleEffectsImportedState() { using namespace Utils; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h index 3b57b7a4abb..48a8fd98f49 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h @@ -46,8 +46,10 @@ public: const QVariant &data) override; private: + void connectUserBundle(); void active3DSceneChanged(qint32 sceneId); void updateBundleMaterialsImportedState(); + void updateBundleUserMaterialsImportedState(); void updateBundleEffectsImportedState(); void updateBundlesQuick3DVersion(); #ifdef QDS_USE_PROJECTSTORAGE diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp index c885a76ba71..8a2e81cfb21 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp @@ -10,6 +10,7 @@ #include "contentlibrarytexture.h" #include "contentlibrarytexturesmodel.h" #include "contentlibraryiconprovider.h" +#include "contentlibraryusermodel.h" #include "utils/filedownloader.h" #include "utils/fileextractor.h" @@ -126,6 +127,7 @@ ContentLibraryWidget::ContentLibraryWidget() , m_texturesModel(new ContentLibraryTexturesModel("Textures", this)) , m_environmentsModel(new ContentLibraryTexturesModel("Environments", this)) , m_effectsModel(new ContentLibraryEffectsModel(this)) + , m_userModel(new ContentLibraryUserModel(this)) { qmlRegisterType("WebFetcher", 1, 0, "FileDownloader"); qmlRegisterType("WebFetcher", 1, 0, "FileExtractor"); @@ -177,7 +179,8 @@ ContentLibraryWidget::ContentLibraryWidget() {"materialsModel", QVariant::fromValue(m_materialsModel.data())}, {"texturesModel", QVariant::fromValue(m_texturesModel.data())}, {"environmentsModel", QVariant::fromValue(m_environmentsModel.data())}, - {"effectsModel", QVariant::fromValue(m_effectsModel.data())}}); + {"effectsModel", QVariant::fromValue(m_effectsModel.data())}, + {"userModel", QVariant::fromValue(m_userModel.data())}}); reloadQmlSource(); } @@ -601,6 +604,12 @@ void ContentLibraryWidget::markTextureUpdated(const QString &textureKey) m_environmentsModel->markTextureHasNoUpdates(subcategory, textureKey); } +bool ContentLibraryWidget::userBundleEnabled() const +{ + // TODO: this method is to be removed after user bundle implementation is complete + return Core::ICore::settings()->value("QML/Designer/UseExperimentalFeatures45", false).toBool(); +} + QSize ContentLibraryWidget::sizeHint() const { return {420, 420}; @@ -715,6 +724,7 @@ void ContentLibraryWidget::updateSearch() m_effectsModel->setSearchText(m_filterText); m_texturesModel->setSearchText(m_filterText); m_environmentsModel->setSearchText(m_filterText); + m_userModel->setSearchText(m_filterText); m_quickWidget->update(); } @@ -821,4 +831,9 @@ QPointer ContentLibraryWidget::effectsModel() const return m_effectsModel; } +QPointer ContentLibraryWidget::userModel() const +{ + return m_userModel; +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h index ab71a3dc799..729443817ea 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h @@ -24,6 +24,7 @@ class ContentLibraryMaterial; class ContentLibraryMaterialsModel; class ContentLibraryTexture; class ContentLibraryTexturesModel; +class ContentLibraryUserModel; class ContentLibraryWidget : public QFrame { @@ -65,6 +66,7 @@ public: QPointer texturesModel() const; QPointer environmentsModel() const; QPointer effectsModel() const; + QPointer userModel() const; Q_INVOKABLE void startDragEffect(QmlDesigner::ContentLibraryEffect *eff, const QPointF &mousePos); Q_INVOKABLE void startDragMaterial(QmlDesigner::ContentLibraryMaterial *mat, const QPointF &mousePos); @@ -74,6 +76,7 @@ public: Q_INVOKABLE void addLightProbe(QmlDesigner::ContentLibraryTexture *tex); Q_INVOKABLE void updateSceneEnvState(); Q_INVOKABLE void markTextureUpdated(const QString &textureKey); + Q_INVOKABLE bool userBundleEnabled() const; QSize sizeHint() const override; @@ -112,6 +115,7 @@ private: QPointer m_texturesModel; QPointer m_environmentsModel; QPointer m_effectsModel; + QPointer m_userModel; QShortcut *m_qmlSourceUpdateShortcut = nullptr; From 3494f37b18be3ccb04db38c2c9c2ead6ec3da17e Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Mon, 8 Apr 2024 10:48:18 +0300 Subject: [PATCH 097/202] QmlDesigner: Set the color data by the first commit in Model Editor The color didn't use to be saved by the first commit. The problem was that the ColorEditorPopup uses a backendValue, which usually is provided by the property editor items. Here this backendValue is faked, to be used by the internal modules. Fixes: QDS-12018 Change-Id: I77bd0e2992f31f2fa4da7b1d7a5f0667f6923085 Reviewed-by: Mahmoud Badri Reviewed-by: Shrief Gabr Reviewed-by: Qt CI Patch Build Bot --- .../ColorViewDelegate.qml | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ColorViewDelegate.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ColorViewDelegate.qml index 6bb1b60159a..16e55acfb43 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ColorViewDelegate.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ColorViewDelegate.qml @@ -16,19 +16,33 @@ Row { property color color property bool supportGradient: false - readonly property color __editColor: edit + + property QtObject backendValue: QtObject { + property color value: edit + readonly property color editColor: edit + + function resetValue() { + if (value) + value = "" + } + + onValueChanged: { + if (editColor !== value) + edit = value + } + } property variant value: { - if (!edit) + if (!colorEditor.backendValue || !colorEditor.backendValue.value) return "white" // default color for Rectangle if (colorEditor.isVector3D) { - return Qt.rgba(__editColor.x, - __editColor.y, - __editColor.z, 1) + return Qt.rgba(colorEditor.backendValue.value.x, + colorEditor.backendValue.value.y, + colorEditor.backendValue.value.z, 1) } - return __editColor + return colorEditor.backendValue.value } property alias gradientPropertyName: popupDialog.gradientPropertyName @@ -42,31 +56,17 @@ Row { property bool __block: false - function getColorFromEditValue() { - if (!edit) - return "white" // default color for Rectangle - - if (colorEditor.isVector3D) { - return Qt.rgba(__editColor.x, - __editColor.y, - __editColor.z, 1) - } - - return __editColor - } - function resetShapeColor() { - if (edit) - edit = "" + colorEditor.backendValue.resetValue() } function writeColor() { if (colorEditor.isVector3D) { - edit = Qt.vector3d(colorEditor.color.r, + colorEditor.backendValue.value = Qt.vector3d(colorEditor.color.r, colorEditor.color.g, colorEditor.color.b) } else { - edit = colorEditor.color + colorEditor.backendValue.value = colorEditor.color } } @@ -77,7 +77,7 @@ Row { // Syncing color from backend to frontend and block reflection function syncColor() { colorEditor.__block = true - colorEditor.color = colorEditor.getColorFromEditValue() + colorEditor.color = colorEditor.value hexTextField.syncColor() colorEditor.__block = false } @@ -92,7 +92,7 @@ Row { colorEditor.syncColor() } - function on__EditColorChanged() { + function onBackendValueChanged() { if (popupDialog.isSolid()) colorEditor.syncColor() } @@ -208,7 +208,7 @@ Row { if (colorEditor.supportGradient && popupDialog.loaderItem.gradientModel.hasGradient) { var hexColor = convertColorToString(colorEditor.color) hexTextField.text = hexColor - edit = hexColor + colorEditor.backendValue.value = hexColor popupDialog.loaderItem.commitGradientColor() } } @@ -292,5 +292,5 @@ Row { Component.onCompleted: popupDialog.determineActiveColorMode() - on__EditColorChanged: popupDialog.determineActiveColorMode() + onBackendValueChanged: popupDialog.determineActiveColorMode() } From 932bdb339bee5778d6f5ab9c83fd995b372a42b0 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 28 Mar 2024 14:50:34 +0100 Subject: [PATCH 098/202] QmlDesigner: Add support for Key enums Change-Id: I602c44283a4f8978d76117fd5b885ef34d377857 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann Reviewed-by: --- .../qmldesigner/designercore/model/texttomodelmerger.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index d09143c2814..5951413086b 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -84,6 +84,9 @@ bool isGlobalQtEnums(QStringView value) u"TopToBottom", u"UpArrowCursor", u"Vertical", u"WaitCursor", u"WhatsThisCursor", u"WheelFocus"}); + if (value.toString().startsWith("Key_")) + return true; + return std::binary_search(std::begin(list), std::end(list), QmlDesigner::ModelUtils::toStdStringView(value)); From db04da3ba7ec8ae0549acf77f256aa272de4d1ce Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 28 Mar 2024 10:58:29 +0100 Subject: [PATCH 099/202] QmlDesigner: Remove Qt 6.2/6.3/6.4 from extended 3d wizard Change-Id: Ic79d7ccf4183aafd30953dc65ad1bae4661c8f11 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Miikka Heikkinen Reviewed-by: --- .../application-extended-3d/wizard.json | 26 +------------------ 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/wizard.json index 7a0333e1f1e..e32ecb8a0bd 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/wizard.json @@ -236,33 +236,9 @@ "type": "ComboBox", "data": { - "index": 5, + "index": 2, "items": [ - { - "trKey": "Qt 6.2", - "value": - "({ - 'TargetQuickVersion': '6.2', - 'TargetQuick3DVersion': '6.2' - })" - }, - { - "trKey": "Qt 6.3", - "value": - "({ - 'TargetQuickVersion': '6.3', - 'TargetQuick3DVersion': '6.3' - })" - }, - { - "trKey": "Qt 6.4", - "value": - "({ - 'TargetQuickVersion': '6.4', - 'TargetQuick3DVersion': '6.4' - })" - }, { "trKey": "Qt 6.5", "value": From 5a13304d728d48aa601f379b6048cfbfc8f6d565 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Thu, 21 Mar 2024 12:47:09 +0200 Subject: [PATCH 100/202] QmlDesigner: Save a project material to content library Fixes: QDS-12392 Change-Id: Ic84197bb1bcede6d3b06d1cff09f00617cc2a958 Reviewed-by: Miikka Heikkinen --- .../MaterialBrowserContextMenu.qml | 9 ++ .../contentlibrary/contentlibrarymaterial.h | 2 +- .../contentlibraryusermodel.cpp | 69 ++++++++- .../contentlibrary/contentlibraryusermodel.h | 11 ++ .../contentlibrary/contentlibraryview.cpp | 144 +++++++++++++++++- .../contentlibrary/contentlibraryview.h | 7 + .../materialbrowser/materialbrowserwidget.cpp | 20 +++ .../materialbrowser/materialbrowserwidget.h | 2 + src/plugins/qmldesigner/utils/asset.cpp | 10 +- src/plugins/qmldesigner/utils/asset.h | 2 + 10 files changed, 263 insertions(+), 13 deletions(-) diff --git a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowserContextMenu.qml b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowserContextMenu.qml index f72d21b35b6..b01aa7d2a3c 100644 --- a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowserContextMenu.qml +++ b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowserContextMenu.qml @@ -127,4 +127,13 @@ StudioControls.Menu { onTriggered: materialBrowserModel.addNewMaterial() } + + Component.onCompleted: { + if (MaterialBrowserBackend.rootView.userBundleEnabled()) { + var menuItem = Qt.createQmlObject("import StudioControls as StudioControls; StudioControls.MenuItem {}", root) + menuItem.text = qsTr("Add to Content Library") + menuItem.onTriggered.connect(MaterialBrowserBackend.rootView.addMaterialToContentLibrary) + root.addItem(menuItem) + } + } } diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h index 90d8468fa16..26b5adc468b 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h @@ -30,7 +30,7 @@ public: const QUrl &icon, const QStringList &files, const QString &downloadPath, - const QString &baseWebUrl); + const QString &baseWebUrl = {}); bool filter(const QString &searchText); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp index 56ba8cc6c73..6616b3f2528 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp @@ -7,9 +7,9 @@ #include "contentlibrarymaterial.h" #include "contentlibrarymaterialscategory.h" #include "contentlibrarywidget.h" +#include "qmldesignerconstants.h" #include -#include "qmldesignerconstants.h" #include #include @@ -85,6 +85,59 @@ void ContentLibraryUserModel::updateIsEmpty() } } +void ContentLibraryUserModel::addMaterial(const QString &name, const QString &qml, + const QUrl &icon, const QStringList &files) +{ + auto libMat = new ContentLibraryMaterial(this, name, qml, qmlToModule(qml), icon, files, + Paths::bundlesPathSetting().append("/User/materials")); + + m_userMaterials.append(libMat); + int matSectionIdx = 0; + emit dataChanged(index(matSectionIdx, 0), index(matSectionIdx, 0)); +} + +// returns unique library material's name and qml component +QPair ContentLibraryUserModel::getUniqueLibMaterialNameAndQml(const QString &matName) const +{ + QTC_ASSERT(!m_bundleObj.isEmpty(), return {}); + + const QJsonObject matsObj = m_bundleObj.value("materials").toObject(); + const QStringList matNames = matsObj.keys(); + + QStringList matQmls; + for (const QString &matName : matNames) + matQmls.append(matsObj.value(matName).toObject().value("qml").toString().chopped(4)); // remove .qml + + QString retName = matName.isEmpty() ? "Material" : matName; + retName = retName.trimmed(); + + QString retQml = retName; + retQml.remove(' '); + if (retQml.at(0).isLower()) + retQml[0] = retQml.at(0).toUpper(); + retQml.prepend("My"); + + int num = 1; + if (matNames.contains(retName) || matQmls.contains(retQml)) { + while (matNames.contains(retName + QString::number(num)) + || matQmls.contains(retQml + QString::number(num))) { + ++num; + } + + retName += QString::number(num); + retQml += QString::number(num); + } + + return {retName, retQml + ".qml"}; +} + +TypeName ContentLibraryUserModel::qmlToModule(const QString &qmlName) const +{ + return QLatin1String("%1.%2.%3").arg(QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1), + m_bundleId, + qmlName.chopped(4)).toLatin1(); // chopped(4): remove .qml +} + QHash ContentLibraryUserModel::roleNames() const { static const QHash roles { @@ -134,6 +187,11 @@ void ContentLibraryUserModel::createImporter(const QString &bundlePath, const QS updateIsEmpty(); } +QJsonObject &ContentLibraryUserModel::bundleJsonObjectRef() +{ + return m_bundleObj; +} + void ContentLibraryUserModel::loadUserBundle() { if (m_matBundleExists) @@ -158,7 +216,7 @@ void ContentLibraryUserModel::loadUserBundle() } } - QString bundleId = m_bundleObj.value("id").toString(); + m_bundleId = m_bundleObj.value("id").toString(); // parse materials const QJsonObject matsObj = m_bundleObj.value("materials").toObject(); @@ -174,10 +232,7 @@ void ContentLibraryUserModel::loadUserBundle() QUrl icon = QUrl::fromLocalFile(bundleDir.filePath(matObj.value("icon").toString())); QString qml = matObj.value("qml").toString(); - TypeName type = QLatin1String("%1.%2.%3").arg( - QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1), - bundleId, - qml.chopped(4)).toLatin1(); // chopped(4): remove .qml + TypeName type = qmlToModule(qml); auto userMat = new ContentLibraryMaterial(this, matName, qml, type, icon, files, bundleDir.path(), ""); @@ -190,7 +245,7 @@ void ContentLibraryUserModel::loadUserBundle() for (const auto /*QJson{Const,}ValueRef*/ &file : sharedFilesArr) sharedFiles.append(file.toString()); - createImporter(bundleDir.path(), bundleId, sharedFiles); + createImporter(bundleDir.path(), m_bundleId, sharedFiles); m_matBundleExists = true; emit matBundleExistsChanged(); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h index 72c535e4014..1bf939d3b82 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h @@ -8,6 +8,8 @@ #include #include +QT_FORWARD_DECLARE_CLASS(QUrl) + namespace QmlDesigner { class ContentLibraryEffect; @@ -43,6 +45,9 @@ public: void setSearchText(const QString &searchText); void updateImportedState(const QStringList &importedMats); + QPair getUniqueLibMaterialNameAndQml(const QString &matName) const; + TypeName qmlToModule(const QString &qmlName) const; + void setQuick3DImportVersion(int major, int minor); bool hasRequiredQuick3DImport() const; @@ -55,6 +60,11 @@ public: void resetModel(); void updateIsEmpty(); + void addMaterial(const QString &name, const QString &qml, const QUrl &icon, const QStringList &files); + + void setBundleObj(const QJsonObject &newBundleObj); + QJsonObject &bundleJsonObjectRef(); + Internal::ContentLibraryBundleImporter *bundleImporter() const; Q_INVOKABLE void applyToSelected(QmlDesigner::ContentLibraryMaterial *mat, bool add = false); @@ -90,6 +100,7 @@ private: ContentLibraryWidget *m_widget = nullptr; QString m_searchText; + QString m_bundleId; QList m_userMaterials; QList m_userTextures; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp index 37ad648c3a4..632997b7c8f 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp @@ -3,6 +3,8 @@ #include "contentlibraryview.h" +#include "asset.h" +#include "bindingproperty.h" #include "contentlibrarybundleimporter.h" #include "contentlibraryeffect.h" #include "contentlibraryeffectsmodel.h" @@ -12,12 +14,15 @@ #include "contentlibrarytexturesmodel.h" #include "contentlibraryusermodel.h" #include "contentlibrarywidget.h" +#include "documentmanager.h" #include "externaldependenciesinterface.h" #include "nodelistproperty.h" #include "qmldesignerconstants.h" #include "qmlobjectnode.h" #include "variantproperty.h" -#include +#include "utils3d.h" + +#include #include #include @@ -31,6 +36,10 @@ #include #endif +#include +#include +#include +#include #include namespace QmlDesigner { @@ -345,8 +354,6 @@ void ContentLibraryView::customNotification(const AbstractView *view, const QList &nodeList, const QList &data) { - Q_UNUSED(data) - if (view == this) return; @@ -386,6 +393,10 @@ void ContentLibraryView::customNotification(const AbstractView *view, m_bundleEffectPos = data.size() == 1 ? data.first() : QVariant(); m_widget->effectsModel()->addInstance(m_draggedBundleEffect); m_bundleEffectTarget = nodeList.first() ? nodeList.first() : Utils3D::active3DSceneNode(this); + } else if (identifier == "add_material_to_content_lib") { + QTC_ASSERT(nodeList.size() == 1 && data.size() == 1, return); + + addLibMaterial(nodeList.first(), data.first().value()); } } @@ -514,6 +525,132 @@ void ContentLibraryView::applyBundleMaterialToDropTarget(const ModelNode &bundle } #endif +// Add a project material to Content Library's user tab +void ContentLibraryView::addLibMaterial(const ModelNode &mat, const QPixmap &icon) +{ + auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/materials/"); + + auto [name, qml] = m_widget->userModel()->getUniqueLibMaterialNameAndQml( + mat.variantProperty("objectName").value().toString()); + + QString iconPath = QLatin1String("icons/%1.png").arg(mat.id()); + QString fullIconPath = bundlePath.pathAppended(iconPath).toString(); + + // save icon + bool iconSaved = icon.save(fullIconPath); + if (!iconSaved) + qWarning() << __FUNCTION__ << "icon save failed"; + + // generate and save material Qml file + const QStringList depAssets = writeLibMaterialQml(mat, qml); + + // add the material to the bundle json + QJsonObject &jsonRef = m_widget->userModel()->bundleJsonObjectRef(); + QJsonObject matsObj = jsonRef.value("materials").toObject(); + QJsonObject matObj; + matObj.insert("qml", qml); + matObj.insert("icon", iconPath); + QJsonArray filesArr; + for (const QString &assetPath : depAssets) + filesArr.append(assetPath); + matObj.insert("files", filesArr); + + matsObj.insert(name, matObj); + jsonRef.insert("materials", matsObj); + auto result = bundlePath.pathAppended("user_materials_bundle.json") + .writeFileContents(QJsonDocument(jsonRef).toJson()); + if (!result) + qWarning() << __FUNCTION__ << result.error(); + + // copy material assets to bundle folder + for (const QString &assetPath : depAssets) { + Asset asset(assetPath); + QString subDir; + if (asset.isImage()) + subDir = "images"; + else if (asset.isShader()) + subDir = "shaders"; + + Utils::FilePath assetPathSource = DocumentManager::currentResourcePath().pathAppended(assetPath); + Utils::FilePath assetPathTarget = bundlePath.pathAppended(QString("%1/%2") + .arg(subDir, "/" + asset.fileName())); + + auto result = assetPathSource.copyFile(assetPathTarget); + if (!result) + qWarning() << __FUNCTION__ << result.error(); + } + + m_widget->userModel()->addMaterial(name, qml, QUrl::fromLocalFile(fullIconPath), depAssets); +} + +QStringList ContentLibraryView::writeLibMaterialQml(const ModelNode &mat, const QString &qml) +{ + QStringList depListIds; + auto [qmlString, assets] = modelNodeToQmlString(mat, depListIds); + + qmlString.prepend("import QtQuick\nimport QtQuick3D\n\n"); + + auto qmlPath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/materials/" + qml); + auto result = qmlPath.writeFileContents(qmlString.toUtf8()); + if (!result) + qWarning() << __FUNCTION__ << result.error(); + + return assets.values(); +} + +QPair> ContentLibraryView::modelNodeToQmlString(const ModelNode &node, + QStringList &depListIds, + int depth) +{ + QString qml; + QSet assets; + + QString indent = QString(" ").repeated(depth * 4); + + qml += indent + node.simplifiedTypeName() + " {\n"; + + indent = QString(" ").repeated((depth + 1) * 4); + + qml += indent + "id: " + (depth == 0 ? "root" : node.id()) + " \n\n"; + + const QList matProps = node.properties(); + for (const AbstractProperty &p : matProps) { + if (p.isVariantProperty()) { + QVariant pValue = p.toVariantProperty().value(); + QString val; + if (strcmp(pValue.typeName(), "QString") == 0 || strcmp(pValue.typeName(), "QColor") == 0) { + val = QLatin1String("\"%1\"").arg(pValue.toString()); + } else if (strcmp(pValue.typeName(), "QUrl") == 0) { + val = QLatin1String("\"%1\"").arg(pValue.toString()); + assets.insert(pValue.toString()); + } else if (strcmp(pValue.typeName(), "QmlDesigner::Enumeration") == 0) { + val = pValue.value().toString(); + } else { + val = pValue.toString(); + } + + qml += indent + p.name() + ": " + val + "\n"; + } else if (p.isBindingProperty()) { + qml += indent + p.name() + ": " + p.toBindingProperty().expression() + "\n"; + + ModelNode depNode = modelNodeForId(p.toBindingProperty().expression()); + + if (depNode && !depListIds.contains(depNode.id())) { + depListIds.append(depNode.id()); + auto [depQml, depAssets] = modelNodeToQmlString(depNode, depListIds, depth + 1); + qml += "\n" + depQml + "\n"; + assets.unite(depAssets); + } + } + } + + indent = QString(" ").repeated(depth * 4); + + qml += indent + "}\n"; + + return {qml, assets}; +} + ModelNode ContentLibraryView::getBundleMaterialDefaultInstance(const TypeName &type) { ModelNode matLib = Utils3D::materialLibraryNode(this); @@ -539,6 +676,7 @@ ModelNode ContentLibraryView::getBundleMaterialDefaultInstance(const TypeName &t return {}; } + #ifdef QDS_USE_PROJECTSTORAGE ModelNode ContentLibraryView::createMaterial(const TypeName &typeName) { diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h index 48a8fd98f49..d6ce50ed0eb 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h @@ -10,6 +10,8 @@ #include #include +QT_FORWARD_DECLARE_CLASS(QPixmap) + namespace QmlDesigner { class ContentLibraryEffect; @@ -52,6 +54,11 @@ private: void updateBundleUserMaterialsImportedState(); void updateBundleEffectsImportedState(); void updateBundlesQuick3DVersion(); + void addLibMaterial(const ModelNode &mat, const QPixmap &icon); + QStringList writeLibMaterialQml(const ModelNode &mat, const QString &qml); + QPair> modelNodeToQmlString(const ModelNode &node, QStringList &depListIds, + int depth = 0); + #ifdef QDS_USE_PROJECTSTORAGE void applyBundleMaterialToDropTarget(const ModelNode &bundleMat, const TypeName &typeName = {}); #else diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp index 47a1e8d293e..7cf0a875bcf 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp @@ -56,6 +56,13 @@ public: m_pixmaps.insert(node.internalId(), pixmap); } + QPixmap getPixmap(const ModelNode &node) + { + QTC_ASSERT(node, return {}); + + return m_pixmaps.value(node.internalId()); + } + void clearPixmapCache() { m_pixmaps.clear(); @@ -357,6 +364,13 @@ void MaterialBrowserWidget::focusMaterialSection(bool focusMatSec) } } +void MaterialBrowserWidget::addMaterialToContentLibrary() +{ + ModelNode mat = m_materialBrowserModel->selectedMaterial(); + m_materialBrowserView->emitCustomNotification("add_material_to_content_lib", {mat}, + {m_previewImageProvider->getPixmap(mat)}); +} + QString MaterialBrowserWidget::qmlSourcesPath() { #ifdef SHARE_QML_PATH @@ -420,4 +434,10 @@ QPointer MaterialBrowserWidget::materialBrowserTex return m_materialBrowserTexturesModel; } +bool MaterialBrowserWidget::userBundleEnabled() const +{ + // TODO: this method is to be removed after user bundle implementation is complete + return Core::ICore::settings()->value("QML/Designer/UseExperimentalFeatures45", false).toBool(); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h index bfe7ace34d6..97c994e5652 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h @@ -60,6 +60,8 @@ public: Q_INVOKABLE void acceptAssetsDropOnMaterial(int matIndex, const QList &urls); Q_INVOKABLE void acceptTextureDropOnMaterial(int matIndex, const QString &texId); Q_INVOKABLE void focusMaterialSection(bool focusMatSec); + Q_INVOKABLE void addMaterialToContentLibrary(); + Q_INVOKABLE bool userBundleEnabled() const; StudioQuickWidget *quickWidget() const; diff --git a/src/plugins/qmldesigner/utils/asset.cpp b/src/plugins/qmldesigner/utils/asset.cpp index 2984a4d8902..f6753dc0042 100644 --- a/src/plugins/qmldesigner/utils/asset.cpp +++ b/src/plugins/qmldesigner/utils/asset.cpp @@ -1,15 +1,16 @@ // Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include - #include "asset.h" +#include + namespace QmlDesigner { Asset::Asset(const QString &filePath) : m_filePath(filePath) { + m_fileName = filePath.split('/').last(); const QStringList split = filePath.split('.'); if (split.size() > 1) m_suffix = "*." + split.last().toLower(); @@ -175,6 +176,11 @@ const QString Asset::id() const return m_filePath; } +const QString Asset::fileName() const +{ + return m_fileName; +} + bool Asset::isSupported() const { return m_type != Asset::Type::Unknown; diff --git a/src/plugins/qmldesigner/utils/asset.h b/src/plugins/qmldesigner/utils/asset.h index cb09f3a5ee6..1b548a1c738 100644 --- a/src/plugins/qmldesigner/utils/asset.h +++ b/src/plugins/qmldesigner/utils/asset.h @@ -37,6 +37,7 @@ public: const QString suffix() const; const QString id() const; + const QString fileName() const; bool hasSuffix() const; Type type() const; @@ -58,6 +59,7 @@ private: void resolveType(); QString m_filePath; + QString m_fileName; QString m_suffix; Type m_type = Unknown; }; From 6751fc82efefd4923990cd46a31d13e9c47326cb Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 26 Mar 2024 13:58:07 +0100 Subject: [PATCH 101/202] Sqlite: Prevent name collisions for indices There can be unique and not unique indices with the same columns. So we add that attribute to the name to distinguish between the indices. Change-Id: I96eac1f0e95c135d625f09fd5399b16f2293d645 Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- src/libs/sqlite/sqliteindex.h | 19 ++++++++ .../unittests/sqlite/sqliteindex-test.cpp | 27 +++++++++-- .../unittests/sqlite/sqlitetable-test.cpp | 48 ++++++++++--------- 3 files changed, 66 insertions(+), 28 deletions(-) diff --git a/src/libs/sqlite/sqliteindex.h b/src/libs/sqlite/sqliteindex.h index f320fcd5999..7c7a8dbb2ba 100644 --- a/src/libs/sqlite/sqliteindex.h +++ b/src/libs/sqlite/sqliteindex.h @@ -40,6 +40,8 @@ public: return Utils::SmallString::join({"CREATE ", m_indexType == IndexType::Unique ? "UNIQUE " : "", "INDEX IF NOT EXISTS index_", + kindName(), + "_", m_tableName, "_", m_columnNames.join("_"), @@ -64,6 +66,23 @@ public: throw IndexHasNoColumns(); } +private: + std::string_view kindName() const + { + using namespace std::string_view_literals; + + if (m_indexType == IndexType::Unique && m_condition.hasContent()) + return "unique_partial"sv; + + if (m_indexType == IndexType::Unique) + return "unique"sv; + + if (m_condition.hasContent()) + return "partial"sv; + + return "normal"sv; + } + private: Utils::SmallString m_tableName; Utils::SmallStringVector m_columnNames; diff --git a/tests/unit/tests/unittests/sqlite/sqliteindex-test.cpp b/tests/unit/tests/unittests/sqlite/sqliteindex-test.cpp index 9d3fca88ffa..b065c0ab9a4 100644 --- a/tests/unit/tests/unittests/sqlite/sqliteindex-test.cpp +++ b/tests/unit/tests/unittests/sqlite/sqliteindex-test.cpp @@ -17,7 +17,9 @@ TEST(Index, one_column) auto sqlStatement = index.sqlStatement(); - ASSERT_THAT(sqlStatement, Eq("CREATE INDEX IF NOT EXISTS index_tableName_column1 ON tableName(column1)")); + ASSERT_THAT( + sqlStatement, + Eq("CREATE INDEX IF NOT EXISTS index_normal_tableName_column1 ON tableName(column1)")); } TEST(Index, two_column) @@ -26,7 +28,9 @@ TEST(Index, two_column) auto sqlStatement = index.sqlStatement(); - ASSERT_THAT(sqlStatement, Eq("CREATE INDEX IF NOT EXISTS index_tableName_column1_column2 ON tableName(column1, column2)")); + ASSERT_THAT(sqlStatement, + Eq("CREATE INDEX IF NOT EXISTS index_normal_tableName_column1_column2 ON " + "tableName(column1, column2)")); } TEST(Index, empty_table_name) @@ -49,7 +53,8 @@ TEST(Index, unique_index) auto sqlStatement = index.sqlStatement(); - ASSERT_THAT(sqlStatement, Eq("CREATE UNIQUE INDEX IF NOT EXISTS index_tableName_column1 ON tableName(column1)")); + ASSERT_THAT( + sqlStatement, Eq("CREATE UNIQUE INDEX IF NOT EXISTS index_unique_tableName_column1 ON tableName(column1)")); } TEST(Index, condition) @@ -58,8 +63,20 @@ TEST(Index, condition) auto sqlStatement = index.sqlStatement(); + ASSERT_THAT( + sqlStatement, + Eq("CREATE INDEX IF NOT EXISTS index_partial_tableName_column1 ON tableName(column1) WHERE " + "column1 IS NOT NULL")); +} + +TEST(Index, unique_index_with_condition) +{ + Index index{"tableName", {"column1"}, IndexType::Unique, "column1 IS NOT NULL"}; + + auto sqlStatement = index.sqlStatement(); + ASSERT_THAT(sqlStatement, - Eq("CREATE INDEX IF NOT EXISTS index_tableName_column1 ON tableName(column1) WHERE " - "column1 IS NOT NULL")); + Eq("CREATE UNIQUE INDEX IF NOT EXISTS index_unique_partial_tableName_column1 ON " + "tableName(column1) WHERE column1 IS NOT NULL")); } } diff --git a/tests/unit/tests/unittests/sqlite/sqlitetable-test.cpp b/tests/unit/tests/unittests/sqlite/sqlitetable-test.cpp index 7b8189b51e4..e1b0427f3c7 100644 --- a/tests/unit/tests/unittests/sqlite/sqlitetable-test.cpp +++ b/tests/unit/tests/unittests/sqlite/sqlitetable-test.cpp @@ -60,9 +60,9 @@ TEST_F(SqliteTable, add_index) auto index = table.addIndex({column, column2}); - ASSERT_THAT( - Utils::SmallStringView(index.sqlStatement()), - Eq("CREATE INDEX IF NOT EXISTS index_testTable_name_value ON testTable(name, value)")); + ASSERT_THAT(Utils::SmallStringView(index.sqlStatement()), + Eq("CREATE INDEX IF NOT EXISTS index_normal_testTable_name_value ON " + "testTable(name, value)")); } TEST_F(SqliteTable, initialize_table) @@ -92,10 +92,11 @@ TEST_F(SqliteTable, initialize_table_with_index) EXPECT_CALL(databaseMock, execute(Eq("CREATE TABLE testTable(name, value)"))); EXPECT_CALL(databaseMock, - execute(Eq("CREATE INDEX IF NOT EXISTS index_testTable_name ON testTable(name)"))); + execute(Eq( + "CREATE INDEX IF NOT EXISTS index_normal_testTable_name ON testTable(name)"))); EXPECT_CALL(databaseMock, - execute(Eq("CREATE INDEX IF NOT EXISTS index_testTable_value ON testTable(value) " - "WHERE value IS NOT NULL"))); + execute(Eq("CREATE INDEX IF NOT EXISTS index_partial_testTable_value ON " + "testTable(value) WHERE value IS NOT NULL"))); table.initialize(databaseMock); } @@ -110,13 +111,13 @@ TEST_F(SqliteTable, initialize_table_with_unique_index) table.addUniqueIndex({column2}, "value IS NOT NULL"); EXPECT_CALL(databaseMock, execute(Eq("CREATE TABLE testTable(name, value)"))); + EXPECT_CALL( + databaseMock, + execute(Eq( + "CREATE UNIQUE INDEX IF NOT EXISTS index_unique_testTable_name ON testTable(name)"))); EXPECT_CALL(databaseMock, - execute(Eq( - "CREATE UNIQUE INDEX IF NOT EXISTS index_testTable_name ON testTable(name)"))); - EXPECT_CALL(databaseMock, - execute(Eq( - "CREATE UNIQUE INDEX IF NOT EXISTS index_testTable_value ON testTable(value) " - "WHERE value IS NOT NULL"))); + execute(Eq("CREATE UNIQUE INDEX IF NOT EXISTS index_unique_partial_testTable_value " + "ON testTable(value) WHERE value IS NOT NULL"))); table.initialize(databaseMock); } @@ -351,8 +352,8 @@ TEST_F(StrictSqliteTable, add_index) auto index = table.addIndex({column, column2}); ASSERT_THAT(Utils::SmallStringView(index.sqlStatement()), - Eq("CREATE INDEX IF NOT EXISTS index_testTable_name_value ON testTable(name, " - "value)")); + Eq("CREATE INDEX IF NOT EXISTS index_normal_testTable_name_value ON " + "testTable(name, value)")); } TEST_F(StrictSqliteTable, initialize_table) @@ -382,10 +383,11 @@ TEST_F(StrictSqliteTable, initialize_table_with_index) EXPECT_CALL(databaseMock, execute(Eq("CREATE TABLE testTable(name ANY, value ANY) STRICT"))); EXPECT_CALL(databaseMock, - execute(Eq("CREATE INDEX IF NOT EXISTS index_testTable_name ON testTable(name)"))); + execute(Eq( + "CREATE INDEX IF NOT EXISTS index_normal_testTable_name ON testTable(name)"))); EXPECT_CALL(databaseMock, - execute(Eq("CREATE INDEX IF NOT EXISTS index_testTable_value ON testTable(value) " - "WHERE value IS NOT NULL"))); + execute(Eq("CREATE INDEX IF NOT EXISTS index_partial_testTable_value ON " + "testTable(value) WHERE value IS NOT NULL"))); table.initialize(databaseMock); } @@ -400,13 +402,13 @@ TEST_F(StrictSqliteTable, initialize_table_with_unique_index) table.addUniqueIndex({column2}, "value IS NOT NULL"); EXPECT_CALL(databaseMock, execute(Eq("CREATE TABLE testTable(name ANY, value ANY) STRICT"))); + EXPECT_CALL( + databaseMock, + execute(Eq( + "CREATE UNIQUE INDEX IF NOT EXISTS index_unique_testTable_name ON testTable(name)"))); EXPECT_CALL(databaseMock, - execute(Eq( - "CREATE UNIQUE INDEX IF NOT EXISTS index_testTable_name ON testTable(name)"))); - EXPECT_CALL(databaseMock, - execute(Eq( - "CREATE UNIQUE INDEX IF NOT EXISTS index_testTable_value ON testTable(value) " - "WHERE value IS NOT NULL"))); + execute(Eq("CREATE UNIQUE INDEX IF NOT EXISTS index_unique_partial_testTable_value " + "ON testTable(value) WHERE value IS NOT NULL"))); table.initialize(databaseMock); } From b733be3b060752d77ad9dc7ca520a9e43236afa9 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 26 Mar 2024 11:04:25 +0100 Subject: [PATCH 102/202] QmlDesigner: Add indices to improve performance Otherwise we get a full table scan which is really slow. Task-number: QTCREATORBUG-30599 Change-Id: Icbfb30e81420da585581d6250acbc4d88580c2f6 Reviewed-by: Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../qmldesigner/designercore/projectstorage/projectstorage.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index 45a349c22c2..7183f9e9eb8 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -3958,6 +3958,7 @@ private: "majorVersion IS NOT NULL AND minorVersion IS NOT NULL"); table.addIndex({typeIdColumn}); + table.addIndex({moduleIdColumn, nameColumn}); table.initialize(database); } @@ -4121,6 +4122,8 @@ private: parentImportIdColumn}, "majorVersion IS NOT NULL AND minorVersion IS NOT NULL"); + table.addIndex({sourceIdColumn, kindColumn}); + table.initialize(database); } @@ -4685,7 +4688,7 @@ public: database}; mutable ReadStatement<1, 1> selectTypeIdForImportedTypeNameNamesStatement{ "WITH " - " importTypeNames(moduleId, name, kind, majorVersion, minorVersion) AS MATERIALIZED ( " + " importTypeNames(moduleId, name, kind, majorVersion, minorVersion) AS ( " " SELECT moduleId, name, di.kind, majorVersion, minorVersion " " FROM importedTypeNames AS itn JOIN documentImports AS di ON " " importOrSourceId=sourceId " From a1add0e1370471c934126ab15e2671d9357f4553 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 26 Mar 2024 18:28:26 +0100 Subject: [PATCH 103/202] QmlDesigner: Simplify resolving prototypes Th optimizer of Sqlite can not see that both forms are the same. The new form is creating an more performant version. Change-Id: Ifbca0627c20a873aa1cee0851d876c919bef4f05 Reviewed-by: Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../projectstorage/projectstorage.h | 34 ++++++++----------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index 7183f9e9eb8..113eecb5e9e 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -4248,15 +4248,12 @@ public: database}; mutable ReadStatement<1, 2> selectPropertyDeclarationIdByTypeIdAndNameStatement{ "WITH RECURSIVE " - " all_prototype_and_extension(typeId, prototypeId) AS (" - " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL" - " UNION ALL " - " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL)," " typeSelection(typeId, level) AS (" " VALUES(?1, 0) " " UNION ALL " - " SELECT prototypeId, typeSelection.level+1 FROM all_prototype_and_extension JOIN " - " typeSelection USING(typeId)) " + " SELECT prototypeId, ts.level+1 FROM types JOIN typeSelection AS ts USING(typeId) " + " UNION ALL " + " SELECT extensionId, ts.level+1 FROM types JOIN typeSelection AS ts USING(typeId)) " "SELECT propertyDeclarationId FROM propertyDeclarations JOIN typeSelection USING(typeId) " " WHERE name=?2 ORDER BY level LIMIT 1", database}; @@ -4266,14 +4263,14 @@ public: " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL" " UNION ALL " " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL)," - " typeSelection(typeId, level) AS (" - " VALUES(?1, 0) " + " typeSelection(typeId) AS (" + " VALUES(?1) " " UNION ALL " - " SELECT prototypeId, typeSelection.level+1 FROM all_prototype_and_extension JOIN " + " SELECT prototypeId FROM all_prototype_and_extension JOIN " " typeSelection USING(typeId))" "SELECT propertyTypeId, propertyDeclarationId, propertyTraits " " FROM propertyDeclarations JOIN typeSelection USING(typeId) " - " WHERE name=?2 ORDER BY level LIMIT 1", + " WHERE name=?2 LIMIT 1", database}; mutable ReadStatement<1, 1> selectPrototypeIdsInOrderStatement{ "WITH RECURSIVE " @@ -4516,17 +4513,16 @@ public: "DELETE FROM documentImports WHERE sourceId IN carray(?1)", database}; ReadStatement<1, 2> selectPropertyDeclarationIdPrototypeChainDownStatement{ "WITH RECURSIVE " - " all_prototype_and_extension(typeId, prototypeId) AS (" - " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL" - " UNION ALL " - " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL)," " typeSelection(typeId, level) AS (" - " SELECT prototypeId, 0 FROM types WHERE typeId=?1 AND prototypeId IS NOT NULL" + " SELECT prototypeId, 0 FROM types WHERE typeId=?1" + " UNION ALL" + " SELECT extensionId, 0 FROM types WHERE typeId=?1" " UNION ALL " - " SELECT prototypeId, typeSelection.level+1 FROM all_prototype_and_extension JOIN " - " typeSelection USING(typeId))" - "SELECT propertyDeclarationId FROM typeSelection JOIN propertyDeclarations " - " USING(typeId) WHERE name=?2 ORDER BY level LIMIT 1", + " SELECT prototypeId, ts.level+1 FROM types JOIN typeSelection AS ts USING(typeId) " + " UNION ALL " + " SELECT extensionId, ts.level+1 FROM types JOIN typeSelection AS ts USING(typeId)) " + "SELECT propertyDeclarationId FROM propertyDeclarations JOIN typeSelection USING(typeId) " + " WHERE name=?2 ORDER BY level LIMIT 1", database}; WriteStatement<2> updateAliasIdPropertyDeclarationStatement{ "UPDATE propertyDeclarations SET aliasPropertyDeclarationId=?2 WHERE " From b341e372bc7419e1df9be32b09a7310d73dc73ac Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 27 Mar 2024 15:44:29 +0100 Subject: [PATCH 104/202] QmlDesigner: Improve prototypes statement Task-number: QTCREATORBUG-30599 Change-Id: I025e654b39a3182c324b540ab3dc8aa24d32f589 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- .../designercore/projectstorage/projectstorage.h | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index 113eecb5e9e..4bc850ab246 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -4586,16 +4586,15 @@ public: "UPDATE types SET extensionId=?2 WHERE typeId=?1", database}; mutable ReadStatement<1, 1> selectTypeIdsForPrototypeChainIdStatement{ "WITH RECURSIVE " - " all_prototype_and_extension(typeId, prototypeId) AS (" - " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL" + " prototypes(typeId) AS ( " + " SELECT prototypeId FROM types WHERE typeId=?1 " " UNION ALL " - " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL)," - " prototypes(typeId) AS (" - " SELECT prototypeId FROM all_prototype_and_extension WHERE typeId=?" + " SELECT extensionId FROM types WHERE typeId=?1 " " UNION ALL " - " SELECT prototypeId FROM all_prototype_and_extension JOIN " - " prototypes USING(typeId)) " - "SELECT typeId FROM prototypes", + " SELECT prototypeId FROM types JOIN prototypes USING(typeId) " + " UNION ALL " + " SELECT extensionId FROM types JOIN prototypes USING(typeId)) " + "SELECT typeId FROM prototypes WHERE typeId IS NOT NULL", database}; WriteStatement<3> updatePropertyDeclarationAliasIdAndTypeNameIdStatement{ "UPDATE propertyDeclarations SET aliasPropertyDeclarationId=?2, " From 91f3c4c5e0484a779d86859abf956e99c8efffc0 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 27 Mar 2024 16:41:18 +0100 Subject: [PATCH 105/202] QmlDesigner: Improve performance for alias property declarations To avoid a full table scan we have to separate the where clause into three statements and union them. So now it using an index for everything. Task-number: QTCREATORBUG-30599 Change-Id: Icdc368cda4c59e64c626da3c3dc093ee3ae95db4 Reviewed-by: Thomas Hartmann Reviewed-by: Reviewed-by: Qt CI Patch Build Bot --- .../projectstorage/projectstorage.h | 39 +++++++++++++------ 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index 4bc850ab246..aa7b511115b 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -3901,10 +3901,11 @@ private: {Sqlite::PrimaryKey{}}); auto &typeIdColumn = propertyDeclarationTable.addColumn("typeId"); auto &nameColumn = propertyDeclarationTable.addColumn("name"); - propertyDeclarationTable.addForeignKeyColumn("propertyTypeId", - typesTable, - Sqlite::ForeignKeyAction::NoAction, - Sqlite::ForeignKeyAction::Restrict); + auto &propertyTypeIdColumn = propertyDeclarationTable.addForeignKeyColumn( + "propertyTypeId", + typesTable, + Sqlite::ForeignKeyAction::NoAction, + Sqlite::ForeignKeyAction::Restrict); propertyDeclarationTable.addColumn("propertyTraits", Sqlite::StrictColumnType::Integer); propertyDeclarationTable.addColumn("propertyImportedTypeNameId", @@ -3921,6 +3922,7 @@ private: Sqlite::ForeignKeyAction::Restrict); propertyDeclarationTable.addUniqueIndex({typeIdColumn, nameColumn}); + propertyDeclarationTable.addIndex({propertyTypeIdColumn}); propertyDeclarationTable.addIndex({aliasPropertyDeclarationIdColumn}, "aliasPropertyDeclarationId IS NOT NULL"); propertyDeclarationTable.addIndex({aliasPropertyDeclarationTailIdColumn}, @@ -3976,6 +3978,7 @@ private: auto &kindColumn = table.addColumn("kind", Sqlite::StrictColumnType::Integer); table.addUniqueIndex({kindColumn, importOrSourceIdColumn, nameColumn}); + table.addIndex({nameColumn}); table.initialize(database); } @@ -4541,13 +4544,27 @@ public: database}; ReadStatement<5, 1> selectAliasPropertiesDeclarationForPropertiesWithTypeIdStatement{ "SELECT alias.typeId, alias.propertyDeclarationId, alias.propertyImportedTypeNameId, " - "alias.aliasPropertyDeclarationId, alias.aliasPropertyDeclarationTailId FROM " - "propertyDeclarations AS alias JOIN propertyDeclarations AS target ON " - "alias.aliasPropertyDeclarationId=target.propertyDeclarationId OR " - "alias.aliasPropertyDeclarationTailId=target.propertyDeclarationId WHERE " - "alias.propertyTypeId=?1 OR target.typeId=?1 OR alias.propertyImportedTypeNameId IN " - "(SELECT importedTypeNameId FROM exportedTypeNames JOIN importedTypeNames USING(name) " - "WHERE typeId=?1)", + " alias.aliasPropertyDeclarationId, alias.aliasPropertyDeclarationTailId " + "FROM propertyDeclarations AS alias JOIN propertyDeclarations AS target " + " ON alias.aliasPropertyDeclarationId=target.propertyDeclarationId OR " + " alias.aliasPropertyDeclarationTailId=target.propertyDeclarationId " + "WHERE alias.propertyTypeId=?1 " + "UNION ALL " + "SELECT alias.typeId, alias.propertyDeclarationId, alias.propertyImportedTypeNameId, " + " alias.aliasPropertyDeclarationId, alias.aliasPropertyDeclarationTailId " + "FROM propertyDeclarations AS alias JOIN propertyDeclarations AS target " + " ON alias.aliasPropertyDeclarationId=target.propertyDeclarationId OR " + " alias.aliasPropertyDeclarationTailId=target.propertyDeclarationId " + "WHERE target.typeId=?1 " + "UNION ALL " + "SELECT alias.typeId, alias.propertyDeclarationId, alias.propertyImportedTypeNameId, " + " alias.aliasPropertyDeclarationId, alias.aliasPropertyDeclarationTailId " + "FROM propertyDeclarations AS alias JOIN propertyDeclarations AS target " + " ON alias.aliasPropertyDeclarationId=target.propertyDeclarationId OR " + " alias.aliasPropertyDeclarationTailId=target.propertyDeclarationId " + "WHERE alias.propertyImportedTypeNameId IN " + " (SELECT importedTypeNameId FROM exportedTypeNames JOIN importedTypeNames USING(name) " + " WHERE typeId=?1)", database}; ReadStatement<3, 1> selectAliasPropertiesDeclarationForPropertiesWithAliasIdStatement{ "WITH RECURSIVE " From 4e5a0cd02b83f0ea2c7b6066ecb3465488b923de Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 27 Mar 2024 17:07:10 +0100 Subject: [PATCH 106/202] QmlDesigner: Improve performance of prototype walk The optimizer took quite often the slow join first. So now we have one method to walk the prototype chain and then use the type ids in simpler statements to get the end result. Task-number: QTCREATORBUG-30599 Change-Id: I3e9d4ec85ba75801769eb8760fda6e0400300899 Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../designercore/include/projectstorageids.h | 4 + .../projectstorage/projectstorage.h | 230 +++++++----------- .../projectstorage/projectstorageinterface.h | 6 +- tests/unit/tests/mocks/projectstoragemock.cpp | 22 +- tests/unit/tests/mocks/projectstoragemock.h | 39 +-- .../propertyeditorcomponentgenerator-test.cpp | 2 +- .../unit/tests/unittests/model/model-test.cpp | 16 +- 7 files changed, 139 insertions(+), 180 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/include/projectstorageids.h b/src/plugins/qmldesigner/designercore/include/projectstorageids.h index bc66e0d2b20..90c174e04e0 100644 --- a/src/plugins/qmldesigner/designercore/include/projectstorageids.h +++ b/src/plugins/qmldesigner/designercore/include/projectstorageids.h @@ -7,6 +7,8 @@ #include +#include + namespace QmlDesigner { enum class BasicIdType { @@ -29,6 +31,8 @@ enum class BasicIdType { using TypeId = Sqlite::BasicId; using TypeIds = std::vector; +template +using SmallTypeIds = QVarLengthArray; using PropertyDeclarationId = Sqlite::BasicId; using PropertyDeclarationIds = std::vector; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index aa7b511115b..7188d0e814e 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -345,9 +345,11 @@ public: projectStorageCategory(), keyValue("type id", typeId)}; - auto propertyDeclarationIds = selectPropertyDeclarationIdsForTypeStatement - .template valuesWithTransaction< - QVarLengthArray>(typeId); + auto propertyDeclarationIds = Sqlite::withDeferredTransaction(database, [&] { + return fetchPropertyDeclarationIds(typeId); + }); + + std::sort(propertyDeclarationIds.begin(), propertyDeclarationIds.end()); tracer.end(keyValue("property declaration ids", propertyDeclarationIds)); @@ -379,9 +381,9 @@ public: keyValue("type id", typeId), keyValue("property name", propertyName)}; - auto propertyDeclarationId = selectPropertyDeclarationIdForTypeAndPropertyNameStatement - .template valueWithTransaction( - typeId, propertyName); + auto propertyDeclarationId = Sqlite::withDeferredTransaction(database, [&] { + return fetchPropertyDeclarationId(typeId, propertyName); + }); tracer.end(keyValue("property declaration id", propertyDeclarationId)); @@ -663,40 +665,43 @@ public: return typeId; } - TypeIds prototypeIds(TypeId type) const override + SmallTypeIds<16> prototypeIds(TypeId type) const override { using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"get prototypes"_t, projectStorageCategory(), keyValue("type id", type)}; - auto prototypeIds = selectPrototypeIdsForTypeIdInOrderStatement - .template valuesWithTransaction(type); + auto prototypeIds = selectPrototypeAndExtensionIdsStatement + .template valuesWithTransaction>(type); tracer.end(keyValue("type ids", prototypeIds)); return prototypeIds; } - TypeIds prototypeAndSelfIds(TypeId type) const override + SmallTypeIds<16> prototypeAndSelfIds(TypeId typeId) const override { using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"get prototypes and self"_t, projectStorageCategory()}; - TypeIds prototypeAndSelfIds = selectPrototypeAndSelfIdsForTypeIdInOrderStatement - .template valuesWithTransaction(type); + SmallTypeIds<16> prototypeAndSelfIds; + prototypeAndSelfIds.push_back(typeId); + + selectPrototypeAndExtensionIdsStatement.readToWithTransaction(prototypeAndSelfIds, typeId); tracer.end(keyValue("type ids", prototypeAndSelfIds)); return prototypeAndSelfIds; } - TypeIds heirIds(TypeId typeId) const override + SmallTypeIds<64> heirIds(TypeId typeId) const override { using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"get heirs"_t, projectStorageCategory()}; - auto heirIds = selectHeirTypeIdsStatement.template valuesWithTransaction(typeId); + auto heirIds = selectHeirTypeIdsStatement.template valuesWithTransaction>( + typeId); tracer.end(keyValue("type ids", heirIds)); @@ -719,7 +724,8 @@ public: return true; } - auto range = selectPrototypeIdsStatement.template rangeWithTransaction(typeId); + auto range = selectPrototypeAndExtensionIdsStatement.template rangeWithTransaction( + typeId); auto isBasedOn = std::any_of(range.begin(), range.end(), [&](TypeId currentTypeId) { return ((currentTypeId == baseTypeIds) || ...); @@ -2340,6 +2346,52 @@ private: insertAliasPropertyDeclarationStatement.readCallback(callback, typeId, value.name); } + auto fetchPropertyDeclarationIds(TypeId baseTypeId) const + { + QVarLengthArray propertyDeclarationIds; + + selectLocalPropertyDeclarationIdsForTypeStatement.readTo(propertyDeclarationIds, baseTypeId); + + auto range = selectPrototypeAndExtensionIdsStatement.template range(baseTypeId); + + for (TypeId prototype : range) { + selectLocalPropertyDeclarationIdsForTypeStatement.readTo(propertyDeclarationIds, + prototype); + } + + return propertyDeclarationIds; + } + + PropertyDeclarationId fetchNextPropertyDeclarationId(TypeId baseTypeId, + Utils::SmallStringView propertyName) const + { + auto range = selectPrototypeAndExtensionIdsStatement.template range(baseTypeId); + + for (TypeId prototype : range) { + auto propertyDeclarationId = selectPropertyDeclarationIdByTypeIdAndNameStatement + .template value(prototype, + propertyName); + + if (propertyDeclarationId) + return propertyDeclarationId; + } + + return PropertyDeclarationId{}; + } + + PropertyDeclarationId fetchPropertyDeclarationId(TypeId baseTypeId, + Utils::SmallStringView propertyName) const + { + auto propertyDeclarationId = selectPropertyDeclarationIdByTypeIdAndNameStatement + .template value(baseTypeId, + propertyName); + + if (propertyDeclarationId) + return propertyDeclarationId; + + return fetchNextPropertyDeclarationId(baseTypeId, propertyName); + } + void synchronizePropertyDeclarationsInsertProperty( const Storage::Synchronization::PropertyDeclaration &value, SourceId sourceId, TypeId typeId) { @@ -2357,9 +2409,7 @@ private: auto propertyDeclarationId = insertPropertyDeclarationStatement.template value( typeId, value.name, propertyTypeId, value.traits, propertyImportedTypeNameId); - auto nextPropertyDeclarationId = selectPropertyDeclarationIdPrototypeChainDownStatement - .template value(typeId, - value.name); + auto nextPropertyDeclarationId = fetchNextPropertyDeclarationId(typeId, value.name); if (nextPropertyDeclarationId) { updateAliasIdPropertyDeclarationStatement.write(nextPropertyDeclarationId, propertyDeclarationId); @@ -2484,9 +2534,8 @@ private: projectStorageCategory(), keyValue("property declaratio viewn", view)}; - auto nextPropertyDeclarationId = selectPropertyDeclarationIdPrototypeChainDownStatement - .template value(typeId, - view.name); + auto nextPropertyDeclarationId = fetchNextPropertyDeclarationId(typeId, view.name); + if (nextPropertyDeclarationId) { updateAliasPropertyDeclarationByAliasPropertyDeclarationIdStatement .write(nextPropertyDeclarationId, view.id); @@ -3313,7 +3362,7 @@ private: throw PrototypeChainCycle{}; }; - selectTypeIdsForPrototypeChainIdStatement.readCallback(callback, typeId); + selectPrototypeAndExtensionIdsStatement.readCallback(callback, typeId); } void checkForAliasChainCycle(PropertyDeclarationId propertyDeclarationId) const @@ -3586,9 +3635,10 @@ private: keyValue("type id", typeId), keyValue("property name", name)}; - auto propertyDeclaration = selectPropertyDeclarationByTypeIdAndNameStatement - .template optionalValue(typeId, - name); + auto propertyDeclarationId = fetchPropertyDeclarationId(typeId, name); + auto propertyDeclaration = selectPropertyDeclarationResultByPropertyDeclarationIdStatement + .template optionalValue( + propertyDeclarationId); tracer.end(keyValue("property declaration", propertyDeclaration)); @@ -3623,8 +3673,7 @@ private: keyValue("type id", typeId), keyValue("property name", name)}; - auto propertyDeclarationId = selectPropertyDeclarationIdByTypeIdAndNameStatement - .template value(typeId, name); + auto propertyDeclarationId = fetchPropertyDeclarationId(typeId, name); tracer.end(keyValue("property declaration id", propertyDeclarationId)); @@ -4236,57 +4285,11 @@ public: "ORDER BY minorVersion DESC " "LIMIT 1", database}; - mutable ReadStatement<1, 2> selectPrototypeIdStatement{ - "WITH RECURSIVE " - " all_prototype_and_extension(typeId, prototypeId) AS (" - " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL" - " UNION ALL " - " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL)," - " typeSelection(typeId) AS (" - " VALUES(?1) " - " UNION ALL " - " SELECT prototypeId FROM all_prototype_and_extension JOIN typeSelection " - " USING(typeId))" - "SELECT typeId FROM typeSelection WHERE typeId=?2 LIMIT 1", - database}; - mutable ReadStatement<1, 2> selectPropertyDeclarationIdByTypeIdAndNameStatement{ - "WITH RECURSIVE " - " typeSelection(typeId, level) AS (" - " VALUES(?1, 0) " - " UNION ALL " - " SELECT prototypeId, ts.level+1 FROM types JOIN typeSelection AS ts USING(typeId) " - " UNION ALL " - " SELECT extensionId, ts.level+1 FROM types JOIN typeSelection AS ts USING(typeId)) " - "SELECT propertyDeclarationId FROM propertyDeclarations JOIN typeSelection USING(typeId) " - " WHERE name=?2 ORDER BY level LIMIT 1", - database}; - mutable ReadStatement<3, 2> selectPropertyDeclarationByTypeIdAndNameStatement{ - "WITH RECURSIVE " - " all_prototype_and_extension(typeId, prototypeId) AS (" - " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL" - " UNION ALL " - " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL)," - " typeSelection(typeId) AS (" - " VALUES(?1) " - " UNION ALL " - " SELECT prototypeId FROM all_prototype_and_extension JOIN " - " typeSelection USING(typeId))" + mutable ReadStatement<3, 1> selectPropertyDeclarationResultByPropertyDeclarationIdStatement{ "SELECT propertyTypeId, propertyDeclarationId, propertyTraits " - " FROM propertyDeclarations JOIN typeSelection USING(typeId) " - " WHERE name=?2 LIMIT 1", - database}; - mutable ReadStatement<1, 1> selectPrototypeIdsInOrderStatement{ - "WITH RECURSIVE " - " all_prototype_and_extension(typeId, prototypeId) AS (" - " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL" - " UNION ALL " - " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL)," - " typeSelection(typeId, level) AS (" - " VALUES(?1, 0) " - " UNION ALL " - " SELECT prototypeId, typeSelection.level+1 FROM all_prototype_and_extension JOIN " - " typeSelection USING(typeId) WHERE prototypeId IS NOT NULL) " - "SELECT typeId FROM typeSelection ORDER BY level DESC", + "FROM propertyDeclarations " + "WHERE propertyDeclarationId=?1 " + "LIMIT 1", database}; mutable ReadStatement<1, 1> selectSourceContextIdFromSourceContextsBySourceContextPathStatement{ "SELECT sourceContextId FROM sourceContexts WHERE sourceContextPath = ?", database}; @@ -4514,18 +4517,11 @@ public: "DELETE FROM documentImports WHERE sourceId=?1 AND parentImportId=?2", database}; WriteStatement<1> deleteDocumentImportsWithSourceIdsStatement{ "DELETE FROM documentImports WHERE sourceId IN carray(?1)", database}; - ReadStatement<1, 2> selectPropertyDeclarationIdPrototypeChainDownStatement{ - "WITH RECURSIVE " - " typeSelection(typeId, level) AS (" - " SELECT prototypeId, 0 FROM types WHERE typeId=?1" - " UNION ALL" - " SELECT extensionId, 0 FROM types WHERE typeId=?1" - " UNION ALL " - " SELECT prototypeId, ts.level+1 FROM types JOIN typeSelection AS ts USING(typeId) " - " UNION ALL " - " SELECT extensionId, ts.level+1 FROM types JOIN typeSelection AS ts USING(typeId)) " - "SELECT propertyDeclarationId FROM propertyDeclarations JOIN typeSelection USING(typeId) " - " WHERE name=?2 ORDER BY level LIMIT 1", + mutable ReadStatement<1, 2> selectPropertyDeclarationIdByTypeIdAndNameStatement{ + "SELECT propertyDeclarationId " + "FROM propertyDeclarations " + "WHERE typeId=?1 AND name=?2 " + "LIMIT 1", database}; WriteStatement<2> updateAliasIdPropertyDeclarationStatement{ "UPDATE propertyDeclarations SET aliasPropertyDeclarationId=?2 WHERE " @@ -4601,7 +4597,7 @@ public: "UPDATE types SET prototypeId=?2 WHERE typeId=?1", database}; WriteStatement<2> updateTypeExtensionStatement{ "UPDATE types SET extensionId=?2 WHERE typeId=?1", database}; - mutable ReadStatement<1, 1> selectTypeIdsForPrototypeChainIdStatement{ + mutable ReadStatement<1, 1> selectPrototypeAndExtensionIdsStatement{ "WITH RECURSIVE " " prototypes(typeId) AS ( " " SELECT prototypeId FROM types WHERE typeId=?1 " @@ -4793,40 +4789,12 @@ public: "SELECT DISTINCT moduleId, ifnull(majorVersion, -1), ifnull(minorVersion, -1) " "FROM imports", database}; - mutable ReadStatement<1, 1> selectPropertyDeclarationIdsForTypeStatement{ - "WITH RECURSIVE " - " all_prototype_and_extension(typeId, prototypeId) AS (" - " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL" - " UNION ALL " - " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL)," - " typeChain(typeId) AS (" - " VALUES(?1)" - " UNION ALL " - " SELECT prototypeId FROM all_prototype_and_extension JOIN typeChain " - " USING(typeId))" - "SELECT propertyDeclarationId FROM typeChain JOIN propertyDeclarations " - " USING(typeId) ORDER BY propertyDeclarationId", - database}; mutable ReadStatement<1, 1> selectLocalPropertyDeclarationIdsForTypeStatement{ "SELECT propertyDeclarationId " "FROM propertyDeclarations " "WHERE typeId=? " "ORDER BY propertyDeclarationId", database}; - mutable ReadStatement<1, 2> selectPropertyDeclarationIdForTypeAndPropertyNameStatement{ - "WITH RECURSIVE " - " all_prototype_and_extension(typeId, prototypeId) AS (" - " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL" - " UNION ALL " - " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL)," - " typeChain(typeId, level) AS (" - " VALUES(?1, 0)" - " UNION ALL " - " SELECT prototypeId, typeChain.level + 1 FROM all_prototype_and_extension JOIN " - " typeChain USING(typeId))" - "SELECT propertyDeclarationId FROM typeChain JOIN propertyDeclarations " - " USING(typeId) WHERE name=?2 ORDER BY level LIMIT 1", - database}; mutable ReadStatement<1, 2> selectLocalPropertyDeclarationIdForTypeAndPropertyNameStatement{ "SELECT propertyDeclarationId " "FROM propertyDeclarations " @@ -4887,32 +4855,6 @@ public: " prototypes AS p USING(typeId)) " "SELECT typeId FROM prototypes ORDER BY level", database}; - mutable ReadStatement<1, 1> selectPrototypeAndSelfIdsForTypeIdInOrderStatement{ - "WITH RECURSIVE " - " all_prototype_and_extension(typeId, prototypeId) AS (" - " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL" - " UNION ALL " - " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL)," - " typeChain(typeId, level) AS (" - " VALUES(?1, 0)" - " UNION ALL " - " SELECT prototypeId, tc.level+1 FROM all_prototype_and_extension JOIN " - " typeChain AS tc USING(typeId)) " - "SELECT typeId FROM typeChain ORDER BY level", - database}; - mutable ReadStatement<1, 1> selectPrototypeIdsStatement{ - "WITH RECURSIVE " - " all_prototype_and_extension(typeId, prototypeId) AS (" - " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL" - " UNION ALL " - " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL)," - " typeSelection(typeId) AS (" - " SELECT prototypeId FROM all_prototype_and_extension WHERE typeId=?1 " - " UNION ALL " - " SELECT prototypeId FROM all_prototype_and_extension JOIN typeSelection " - " USING(typeId))" - "SELECT typeId FROM typeSelection", - database}; WriteStatement<2> upsertPropertyEditorPathIdStatement{ "INSERT INTO propertyEditorPaths(typeId, pathSourceId) VALUES(?1, ?2) ON CONFLICT DO " "UPDATE SET pathSourceId=excluded.pathSourceId WHERE pathSourceId IS NOT " diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h index 266c6ee7caa..edf35f745cb 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h @@ -64,9 +64,9 @@ public: virtual std::vector<::Utils::SmallString> functionDeclarationNames(TypeId typeId) const = 0; virtual std::optional<::Utils::SmallString> propertyName(PropertyDeclarationId propertyDeclarationId) const = 0; - virtual TypeIds prototypeAndSelfIds(TypeId type) const = 0; - virtual TypeIds prototypeIds(TypeId type) const = 0; - virtual TypeIds heirIds(TypeId typeId) const = 0; + virtual SmallTypeIds<16> prototypeAndSelfIds(TypeId type) const = 0; + virtual SmallTypeIds<16> prototypeIds(TypeId type) const = 0; + virtual SmallTypeIds<64> heirIds(TypeId typeId) const = 0; virtual bool isBasedOn(TypeId, TypeId) const = 0; virtual bool isBasedOn(TypeId, TypeId, TypeId) const = 0; virtual bool isBasedOn(TypeId, TypeId, TypeId, TypeId) const = 0; diff --git a/tests/unit/tests/mocks/projectstoragemock.cpp b/tests/unit/tests/mocks/projectstoragemock.cpp index 83ff85fe9a3..27e5c152d21 100644 --- a/tests/unit/tests/mocks/projectstoragemock.cpp +++ b/tests/unit/tests/mocks/projectstoragemock.cpp @@ -227,7 +227,9 @@ void ProjectStorageMock::setItemLibraryEntries( } namespace { -void addBaseProperties(TypeId typeId, TypeIds baseTypeIds, ProjectStorageMock &storage) +void addBaseProperties(TypeId typeId, + const QmlDesigner::SmallTypeIds<16> &baseTypeIds, + ProjectStorageMock &storage) { for (TypeId baseTypeId : baseTypeIds) { for (const auto &propertyId : storage.localPropertyDeclarationIds(baseTypeId)) { @@ -254,7 +256,7 @@ TypeId ProjectStorageMock::createType(ModuleId moduleId, PropertyDeclarationTraits defaultPropertyTraits, TypeId defaultPropertyTypeId, Storage::TypeTraits typeTraits, - TypeIds baseTypeIds, + const QmlDesigner::SmallTypeIds<16> &baseTypeIds, SourceId sourceId) { if (auto id = typeId(moduleId, typeName)) { @@ -290,10 +292,9 @@ TypeId ProjectStorageMock::createType(ModuleId moduleId, for (TypeId baseTypeId : baseTypeIds) ON_CALL(*this, isBasedOn(Eq(typeId), Eq(baseTypeId))).WillByDefault(Return(true)); - TypeIds selfAndPrototypes; - selfAndPrototypes.reserve(baseTypeIds.size() + 1); + QmlDesigner::SmallTypeIds<16> selfAndPrototypes; selfAndPrototypes.push_back(typeId); - selfAndPrototypes.insert(selfAndPrototypes.end(), baseTypeIds.begin(), baseTypeIds.end()); + std::copy(baseTypeIds.begin(), baseTypeIds.end(), std::back_inserter(selfAndPrototypes)); ON_CALL(*this, prototypeAndSelfIds(Eq(typeId))).WillByDefault(Return(selfAndPrototypes)); ON_CALL(*this, prototypeIds(Eq(typeId))).WillByDefault(Return(baseTypeIds)); @@ -314,7 +315,7 @@ void ProjectStorageMock::removeType(QmlDesigner::ModuleId moduleId, Utils::Small QmlDesigner::TypeId ProjectStorageMock::createType(QmlDesigner::ModuleId moduleId, Utils::SmallStringView typeName, QmlDesigner::Storage::TypeTraits typeTraits, - QmlDesigner::TypeIds baseTypeIds, + const QmlDesigner::SmallTypeIds<16> &baseTypeIds, SourceId sourceId) { return createType(moduleId, typeName, {}, {}, TypeId{}, typeTraits, baseTypeIds, sourceId); @@ -325,7 +326,7 @@ TypeId ProjectStorageMock::createObject(ModuleId moduleId, Utils::SmallStringView defaultPropertyName, PropertyDeclarationTraits defaultPropertyTraits, QmlDesigner::TypeId defaultPropertyTypeId, - TypeIds baseTypeIds, + const QmlDesigner::SmallTypeIds<16> &baseTypeIds, QmlDesigner::SourceId sourceId) { return createType(moduleId, @@ -340,19 +341,20 @@ TypeId ProjectStorageMock::createObject(ModuleId moduleId, TypeId ProjectStorageMock::createObject(ModuleId moduleId, Utils::SmallStringView typeName, - TypeIds baseTypeIds) + const QmlDesigner::SmallTypeIds<16> &baseTypeIds) { return createType(moduleId, typeName, Storage::TypeTraitsKind::Reference, baseTypeIds); } QmlDesigner::TypeId ProjectStorageMock::createValue(QmlDesigner::ModuleId moduleId, Utils::SmallStringView typeName, - QmlDesigner::TypeIds baseTypeIds) + const QmlDesigner::SmallTypeIds<16> &baseTypeIds) { return createType(moduleId, typeName, Storage::TypeTraitsKind::Value, baseTypeIds); } -void ProjectStorageMock::setHeirs(QmlDesigner::TypeId typeId, QmlDesigner::TypeIds heirIds) +void ProjectStorageMock::setHeirs(QmlDesigner::TypeId typeId, + const QmlDesigner::SmallTypeIds<64> &heirIds) { ON_CALL(*this, heirIds(typeId)).WillByDefault(Return(heirIds)); } diff --git a/tests/unit/tests/mocks/projectstoragemock.h b/tests/unit/tests/mocks/projectstoragemock.h index 198e54b370b..ed370bde96f 100644 --- a/tests/unit/tests/mocks/projectstoragemock.h +++ b/tests/unit/tests/mocks/projectstoragemock.h @@ -56,7 +56,7 @@ public: QmlDesigner::Storage::PropertyDeclarationTraits defaultPropertyTraits, QmlDesigner::TypeId defaultPropertyTypeId, QmlDesigner::Storage::TypeTraits typeTraits, - QmlDesigner::TypeIds baseTypeIds = {}, + const QmlDesigner::SmallTypeIds<16> &baseTypeIds = {}, QmlDesigner::SourceId sourceId = QmlDesigner::SourceId{}); void removeType(QmlDesigner::ModuleId moduleId, Utils::SmallStringView typeName); @@ -64,27 +64,26 @@ public: QmlDesigner::TypeId createType(QmlDesigner::ModuleId moduleId, Utils::SmallStringView typeName, QmlDesigner::Storage::TypeTraits typeTraits, - QmlDesigner::TypeIds baseTypeIds = {}, + const QmlDesigner::SmallTypeIds<16> &baseTypeIds = {}, QmlDesigner::SourceId sourceId = QmlDesigner::SourceId{}); - QmlDesigner::TypeId createObject( - QmlDesigner::ModuleId moduleId, - Utils::SmallStringView typeName, - Utils::SmallStringView defaultPropertyName, - QmlDesigner::Storage::PropertyDeclarationTraits defaultPropertyTraits, - QmlDesigner::TypeId defaultPropertyTypeId, - QmlDesigner::TypeIds baseTypeIds = {}, - QmlDesigner::SourceId sourceId = QmlDesigner::SourceId{}); - QmlDesigner::TypeId createObject(QmlDesigner::ModuleId moduleId, Utils::SmallStringView typeName, - QmlDesigner::TypeIds baseTypeIds = {}); + Utils::SmallStringView defaultPropertyName, + QmlDesigner::Storage::PropertyDeclarationTraits defaultPropertyTraits, + QmlDesigner::TypeId defaultPropertyTypeId, + const QmlDesigner::SmallTypeIds<16> &baseTypeIds = {}, + QmlDesigner::SourceId sourceId = QmlDesigner::SourceId{}); + + QmlDesigner::TypeId createObject(QmlDesigner::ModuleId moduleId, + Utils::SmallStringView typeName, + const QmlDesigner::SmallTypeIds<16> &baseTypeIds = {}); QmlDesigner::TypeId createValue(QmlDesigner::ModuleId moduleId, Utils::SmallStringView typeName, - QmlDesigner::TypeIds baseTypeIds = {}); + const QmlDesigner::SmallTypeIds<16> &baseTypeIds = {}); - void setHeirs(QmlDesigner::TypeId typeId, QmlDesigner::TypeIds heirIds); + void setHeirs(QmlDesigner::TypeId typeId, const QmlDesigner::SmallTypeIds<64> &heirIds); QmlDesigner::PropertyDeclarationId createProperty( QmlDesigner::TypeId typeId, @@ -215,9 +214,15 @@ public: 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(QmlDesigner::TypeIds, heirIds, (QmlDesigner::TypeId type), (const, override)); + MOCK_METHOD(QmlDesigner::SmallTypeIds<16>, + prototypeAndSelfIds, + (QmlDesigner::TypeId type), + (const, override)); + MOCK_METHOD(QmlDesigner::SmallTypeIds<16>, + prototypeIds, + (QmlDesigner::TypeId type), + (const, override)); + MOCK_METHOD(QmlDesigner::SmallTypeIds<64>, heirIds, (QmlDesigner::TypeId type), (const, override)); MOCK_METHOD(bool, isBasedOn, (QmlDesigner::TypeId typeId, QmlDesigner::TypeId), (const, override)); MOCK_METHOD(bool, isBasedOn, diff --git a/tests/unit/tests/unittests/componentcore/propertyeditorcomponentgenerator-test.cpp b/tests/unit/tests/unittests/componentcore/propertyeditorcomponentgenerator-test.cpp index 3b9a8bbfe25..d2f2143a737 100644 --- a/tests/unit/tests/unittests/componentcore/propertyeditorcomponentgenerator-test.cpp +++ b/tests/unit/tests/unittests/componentcore/propertyeditorcomponentgenerator-test.cpp @@ -18,7 +18,7 @@ class PropertyEditorComponentGenerator : public ::testing::Test { protected: QmlDesigner::NodeMetaInfo createType(Utils::SmallStringView name, - QmlDesigner::TypeIds baseTypeIds = {}) + const QmlDesigner::SmallTypeIds<16> &baseTypeIds = {}) { auto typeId = projectStorageMock.createValue(qtQuickModuleId, name, baseTypeIds); diff --git a/tests/unit/tests/unittests/model/model-test.cpp b/tests/unit/tests/unittests/model/model-test.cpp index bda942fec0b..bd34a3a6b12 100644 --- a/tests/unit/tests/unittests/model/model-test.cpp +++ b/tests/unit/tests/unittests/model/model-test.cpp @@ -116,11 +116,11 @@ protected: NiceMock pathCacheMock{"/path/foo.qml"}; NiceMock projectStorageMock{pathCacheMock.sourceId}; NiceMock resourceManagementMock; + QmlDesigner::Imports imports = {QmlDesigner::Import::createLibraryImport("QtQuick")}; QmlDesigner::Model model{{projectStorageMock, pathCacheMock}, "Item", - -1, - -1, - nullptr, + imports, + pathCacheMock.path.toQString(), std::make_unique( resourceManagementMock)}; NiceMock viewMock; @@ -541,7 +541,10 @@ TEST_F(Model, by_default_remove_properties_removes_property) TEST_F(Model, by_default_remove_model_node_in_factory_method_calls_removes_node) { model.detachView(&viewMock); - auto newModel = QmlDesigner::Model::create({projectStorageMock, pathCacheMock}, "QtQuick.Item"); + auto newModel = QmlDesigner::Model::create({projectStorageMock, pathCacheMock}, + "Item", + imports, + pathCacheMock.path.toQString()); newModel->attachView(&viewMock); auto node = createNodeWithParent(viewMock.rootModelNode()); @@ -553,7 +556,10 @@ TEST_F(Model, by_default_remove_model_node_in_factory_method_calls_removes_node) TEST_F(Model, by_default_remove_properties_in_factory_method_calls_remove_property) { model.detachView(&viewMock); - auto newModel = QmlDesigner::Model::create({projectStorageMock, pathCacheMock}, "QtQuick.Item"); + auto newModel = QmlDesigner::Model::create({projectStorageMock, pathCacheMock}, + "Item", + imports, + pathCacheMock.path.toQString()); newModel->attachView(&viewMock); rootNode = viewMock.rootModelNode(); auto property = createProperty(rootNode, "yi"); From 7db0c8f742e6ce5eaa68de38b158a2b11aae1b4c Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 2 Apr 2024 15:20:57 +0200 Subject: [PATCH 107/202] QmlDesigner: Disable 3D views for lite designer Task-number: QDS-12102 Change-Id: I4d339220127ce4cb1f5253c430ad8df372f1f0d6 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Thomas Hartmann --- .../components/componentcore/viewmanager.cpp | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp index 05d6f5fdf0c..b011d9fbbf3 100644 --- a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp @@ -67,16 +67,20 @@ public: , collectionView{externalDependencies} , contentLibraryView{externalDependencies} , componentView{externalDependencies} +#ifndef QTC_USE_QML_DESIGNER_LITE , edit3DView{externalDependencies} +#endif , formEditorView{externalDependencies} , textEditorView{externalDependencies} , assetsLibraryView{externalDependencies} , itemLibraryView(imageCache, externalDependencies) , navigatorView{externalDependencies} , propertyEditorView(imageCache, externalDependencies) +#ifndef QTC_USE_QML_DESIGNER_LITE , materialEditorView{externalDependencies} , materialBrowserView{imageCache, externalDependencies} , textureEditorView{imageCache, externalDependencies} +#endif , statesEditorView{externalDependencies} {} @@ -89,16 +93,20 @@ public: CollectionView collectionView; ContentLibraryView contentLibraryView; ComponentView componentView; +#ifndef QTC_USE_QML_DESIGNER_LITE Edit3DView edit3DView; +#endif FormEditorView formEditorView; TextEditorView textEditorView; AssetsLibraryView assetsLibraryView; ItemLibraryView itemLibraryView; NavigatorView navigatorView; PropertyEditorView propertyEditorView; +#ifndef QTC_USE_QML_DESIGNER_LITE MaterialEditorView materialEditorView; MaterialBrowserView materialBrowserView; TextureEditorView textureEditorView; +#endif StatesEditorView statesEditorView; std::vector> additionalViews; @@ -203,6 +211,7 @@ QList ViewManager::views() const QList ViewManager::standardViews() const { +#ifndef QTC_USE_QML_DESIGNER_LITE QList list = {&d->edit3DView, &d->formEditorView, &d->textEditorView, @@ -215,6 +224,16 @@ QList ViewManager::standardViews() const &d->textureEditorView, &d->statesEditorView, &d->designerActionManagerView}; +#else + QList list = {&d->formEditorView, + &d->textEditorView, + &d->assetsLibraryView, + &d->itemLibraryView, + &d->navigatorView, + &d->propertyEditorView, + &d->statesEditorView, + &d->designerActionManagerView}; +#endif if (enableModelEditor()) list.append(&d->collectionView); @@ -384,16 +403,20 @@ QList ViewManager::widgetInfos() const { QList widgetInfoList; +#ifndef QTC_USE_QML_DESIGNER_LITE widgetInfoList.append(d->edit3DView.widgetInfo()); +#endif widgetInfoList.append(d->formEditorView.widgetInfo()); widgetInfoList.append(d->textEditorView.widgetInfo()); widgetInfoList.append(d->assetsLibraryView.widgetInfo()); widgetInfoList.append(d->itemLibraryView.widgetInfo()); widgetInfoList.append(d->navigatorView.widgetInfo()); widgetInfoList.append(d->propertyEditorView.widgetInfo()); +#ifndef QTC_USE_QML_DESIGNER_LITE widgetInfoList.append(d->materialEditorView.widgetInfo()); widgetInfoList.append(d->materialBrowserView.widgetInfo()); widgetInfoList.append(d->textureEditorView.widgetInfo()); +#endif widgetInfoList.append(d->statesEditorView.widgetInfo()); if (enableModelEditor()) widgetInfoList.append(d->collectionView.widgetInfo()); From 94b08f3f1d5a668d821dba5ecb23022f5936cd80 Mon Sep 17 00:00:00 2001 From: Teea Poldsam Date: Wed, 3 Apr 2024 14:57:04 +0300 Subject: [PATCH 108/202] Doc: Update Design Views documents - Update images, icons, and text. - Remove summary and context menu topics. - Reorganize the design views alphabetically in TOC. Fixes: QDS-12363 Change-Id: I3ddb659ec872ebc554c5e7fbbf7cb31d7ebd9b05 Reviewed-by: Leena Miettinen --- .../qtcreator-filesystem-view-design.png | Bin 9449 -> 0 bytes .../creator-file-system-view.qdoc | 21 +- .../creator-open-documents-view.qdoc | 19 +- .../user-interface/creator-projects-view.qdoc | 29 ++- .../images/3d-view-context-menu.png | Bin 10442 -> 0 bytes .../images/3d-view-context-menu.webp | Bin 0 -> 5718 bytes .../images/filesystem-view-design.webp | Bin 0 -> 5944 bytes .../images/icons/add_material.png | Bin 0 -> 1722 bytes .../images/icons/add_texture.png | Bin 0 -> 1716 bytes .../images/icons/reverse_order.png | Bin 0 -> 1740 bytes .../images/material-editor-browser.webp | Bin 42272 -> 36366 bytes .../images/materials-remove-material.png | Bin 15142 -> 0 bytes .../images/materials-remove-material.webp | Bin 0 -> 5594 bytes .../images/navigator-material-texture.png | Bin 4048 -> 0 bytes .../images/navigator-material-texture.webp | Bin 0 -> 2884 bytes .../images/open-documents-view.webp | Bin 0 -> 2338 bytes .../images/projects-view-design.webp | Bin 0 -> 4660 bytes .../images/qmldesigner-canvas-color.png | Bin 4557 -> 0 bytes .../images/qmldesigner-canvas-color.webp | Bin 0 -> 3558 bytes .../images/qmldesigner-element-properties.png | Bin 27358 -> 0 bytes .../qmldesigner-element-properties.webp | Bin 0 -> 12928 bytes .../qmldesigner-form-editor-move-cursor.png | Bin 9983 -> 0 bytes .../qmldesigner-form-editor-move-cursor.webp | Bin 0 -> 3430 bytes .../images/qmldesigner-form-editor.png | Bin 9604 -> 0 bytes .../images/qmldesigner-form-editor.webp | Bin 0 -> 4986 bytes .../images/qmldesigner-preview-size.png | Bin 45589 -> 0 bytes .../images/qmldesigner-preview-size.webp | Bin 0 -> 15954 bytes .../images/qmldesigner-snap-margins.png | Bin 6617 -> 0 bytes .../images/qmldesigner-snap-margins.webp | Bin 0 -> 4052 bytes .../qmldesigner-transition-editor-startup.png | Bin 3840 -> 0 bytes ...qmldesigner-transition-editor-startup.webp | Bin 0 -> 2316 bytes .../images/qmldesigner-transitions.webp | Bin 0 -> 20632 bytes .../images/qmldesigner-zooming.gif | Bin 93266 -> 80130 bytes .../images/qtquick-assets-tab.png | Bin 16953 -> 0 bytes .../images/qtquick-assets-tab.webp | Bin 0 -> 8972 bytes .../qtquick-designer-rotating-items.png | Bin 10790 -> 0 bytes .../qtquick-designer-rotating-items.webp | Bin 0 -> 6428 bytes .../images/qtquick-designer-scaling-items.png | Bin 8340 -> 0 bytes .../qtquick-designer-scaling-items.webp | Bin 0 -> 3348 bytes .../images/qtquick-item-properties-common.png | Bin 5519 -> 0 bytes .../qtquick-item-properties-common.webp | Bin 0 -> 5474 bytes .../images/qtquick-library-context-menu.png | Bin 16734 -> 0 bytes .../images/qtquick-library-context-menu.webp | Bin 0 -> 12268 bytes .../qtquick-transition-editor-settings.png | Bin 6812 -> 0 bytes .../qtquick-transition-editor-settings.webp | Bin 0 -> 4516 bytes .../images/qtquick-transition-editor-view.png | Bin 6750 -> 0 bytes .../qtquick-transition-editor-view.webp | Bin 0 -> 4792 bytes .../images/studio-3d-editor-axis-helper.webp | Bin 30310 -> 19966 bytes .../images/studio-3d-editor-move.webp | Bin 30488 -> 21094 bytes .../images/studio-3d-editor-rotate.webp | Bin 31534 -> 22998 bytes .../images/studio-3d-editor-scale.webp | Bin 30344 -> 21210 bytes .../images/studio-3d-editor.webp | Bin 29906 -> 19690 bytes .../images/studio-3d-split-view.webp | Bin 33988 -> 22818 bytes .../images/studio-curve-editor.png | Bin 20623 -> 0 bytes .../images/studio-curve-editor.webp | Bin 0 -> 13526 bytes .../images/studio-timeline-empty.png | Bin 3423 -> 0 bytes .../images/studio-timeline-empty.webp | Bin 0 -> 2238 bytes doc/qtdesignstudio/images/texture-editor.png | Bin 56206 -> 0 bytes doc/qtdesignstudio/images/texture-editor.webp | Bin 0 -> 37754 bytes .../qtquick-component-context-menu.qdocinc | 4 +- .../qtquick-production-quality-animation.qdoc | 2 +- .../src/qtdesignstudio-terms.qdoc | 4 +- .../src/qtdesignstudio-toc.qdoc | 30 +-- .../qtdesignstudio-3d-editor.qdoc | 131 +---------- .../qtdesignstudio-keyboard-shortcuts.qdoc | 5 +- .../src/views/qtquick-assets.qdoc | 30 +-- .../src/views/qtquick-components-view.qdoc | 30 +-- .../src/views/qtquick-connection-view.qdoc | 33 +-- .../src/views/qtquick-curve-editor.qdoc | 47 +--- .../src/views/qtquick-designer.qdoc | 218 +----------------- .../views/qtquick-easing-curve-editor.qdoc | 4 +- .../src/views/qtquick-effect-maker-view.qdoc | 4 + .../src/views/qtquick-form-editor.qdoc | 72 ++---- .../src/views/qtquick-navigator.qdoc | 82 ++----- .../src/views/qtquick-properties-view.qdoc | 50 +--- .../src/views/qtquick-properties.qdoc | 4 +- .../src/views/qtquick-states-view.qdoc | 8 +- .../src/views/qtquick-text-editor.qdoc | 4 + .../src/views/qtquick-timeline-view.qdoc | 177 +------------- .../src/views/qtquick-timeline.qdoc | 4 +- .../src/views/qtquick-transition-editor.qdoc | 54 +---- .../src/views/studio-content-library.qdoc | 5 + .../src/views/studio-material-editor.qdoc | 21 +- .../src/views/studio-model-editor.qdoc | 4 + .../src/views/studio-qtinsight.qdoc | 4 + .../src/views/studio-texture-editor.qdoc | 10 +- .../src/views/studio-translations.qdoc | 49 +--- 87 files changed, 211 insertions(+), 948 deletions(-) delete mode 100644 doc/qtcreator/images/qtcreator-filesystem-view-design.png delete mode 100644 doc/qtdesignstudio/images/3d-view-context-menu.png create mode 100644 doc/qtdesignstudio/images/3d-view-context-menu.webp create mode 100644 doc/qtdesignstudio/images/filesystem-view-design.webp create mode 100644 doc/qtdesignstudio/images/icons/add_material.png create mode 100644 doc/qtdesignstudio/images/icons/add_texture.png create mode 100644 doc/qtdesignstudio/images/icons/reverse_order.png delete mode 100644 doc/qtdesignstudio/images/materials-remove-material.png create mode 100644 doc/qtdesignstudio/images/materials-remove-material.webp delete mode 100644 doc/qtdesignstudio/images/navigator-material-texture.png create mode 100644 doc/qtdesignstudio/images/navigator-material-texture.webp create mode 100644 doc/qtdesignstudio/images/open-documents-view.webp create mode 100644 doc/qtdesignstudio/images/projects-view-design.webp delete mode 100644 doc/qtdesignstudio/images/qmldesigner-canvas-color.png create mode 100644 doc/qtdesignstudio/images/qmldesigner-canvas-color.webp delete mode 100644 doc/qtdesignstudio/images/qmldesigner-element-properties.png create mode 100644 doc/qtdesignstudio/images/qmldesigner-element-properties.webp delete mode 100644 doc/qtdesignstudio/images/qmldesigner-form-editor-move-cursor.png create mode 100644 doc/qtdesignstudio/images/qmldesigner-form-editor-move-cursor.webp delete mode 100644 doc/qtdesignstudio/images/qmldesigner-form-editor.png create mode 100644 doc/qtdesignstudio/images/qmldesigner-form-editor.webp delete mode 100644 doc/qtdesignstudio/images/qmldesigner-preview-size.png create mode 100644 doc/qtdesignstudio/images/qmldesigner-preview-size.webp delete mode 100644 doc/qtdesignstudio/images/qmldesigner-snap-margins.png create mode 100644 doc/qtdesignstudio/images/qmldesigner-snap-margins.webp delete mode 100644 doc/qtdesignstudio/images/qmldesigner-transition-editor-startup.png create mode 100644 doc/qtdesignstudio/images/qmldesigner-transition-editor-startup.webp create mode 100644 doc/qtdesignstudio/images/qmldesigner-transitions.webp delete mode 100644 doc/qtdesignstudio/images/qtquick-assets-tab.png create mode 100644 doc/qtdesignstudio/images/qtquick-assets-tab.webp delete mode 100644 doc/qtdesignstudio/images/qtquick-designer-rotating-items.png create mode 100644 doc/qtdesignstudio/images/qtquick-designer-rotating-items.webp delete mode 100644 doc/qtdesignstudio/images/qtquick-designer-scaling-items.png create mode 100644 doc/qtdesignstudio/images/qtquick-designer-scaling-items.webp delete mode 100644 doc/qtdesignstudio/images/qtquick-item-properties-common.png create mode 100644 doc/qtdesignstudio/images/qtquick-item-properties-common.webp delete mode 100644 doc/qtdesignstudio/images/qtquick-library-context-menu.png create mode 100644 doc/qtdesignstudio/images/qtquick-library-context-menu.webp delete mode 100644 doc/qtdesignstudio/images/qtquick-transition-editor-settings.png create mode 100644 doc/qtdesignstudio/images/qtquick-transition-editor-settings.webp delete mode 100644 doc/qtdesignstudio/images/qtquick-transition-editor-view.png create mode 100644 doc/qtdesignstudio/images/qtquick-transition-editor-view.webp delete mode 100644 doc/qtdesignstudio/images/studio-curve-editor.png create mode 100644 doc/qtdesignstudio/images/studio-curve-editor.webp delete mode 100644 doc/qtdesignstudio/images/studio-timeline-empty.png create mode 100644 doc/qtdesignstudio/images/studio-timeline-empty.webp delete mode 100644 doc/qtdesignstudio/images/texture-editor.png create mode 100644 doc/qtdesignstudio/images/texture-editor.webp diff --git a/doc/qtcreator/images/qtcreator-filesystem-view-design.png b/doc/qtcreator/images/qtcreator-filesystem-view-design.png deleted file mode 100644 index da8766a9c2faf6cfe4d5c4679ca4e47925337d83..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9449 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 diff --git a/doc/qtcreator/src/user-interface/creator-file-system-view.qdoc b/doc/qtcreator/src/user-interface/creator-file-system-view.qdoc index 24f3b38479c..ee93c4179b8 100644 --- a/doc/qtcreator/src/user-interface/creator-file-system-view.qdoc +++ b/doc/qtcreator/src/user-interface/creator-file-system-view.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2023 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -15,7 +15,11 @@ \title File System + \if defined(qtdesignstudio) + \brief View all the files in the current directory. + \else \brief Shows all the files in the current directory. + \endif \if defined(qtcreator) \note Usually, \l{Navigate with locator}{searching with the locator} @@ -25,7 +29,7 @@ \image qtcreator-filesystem-view.webp {File System view in the sidebar} \else - \image qtcreator-filesystem-view-design.png {File System view} + \image filesystem-view-design.webp {File System view} \endif To move to the root directory of the file system, select \uicontrol Computer @@ -52,6 +56,7 @@ The view displays the path to the active file as bread crumbs. You can move to any directory along the path by clicking it. + \if defined(qtcreator) \section1 File System Context Menu Use the context menu functions to: @@ -64,34 +69,24 @@ that has the file. To specify the terminal to use on Linux and \macos, select \preferences > \uicontrol Environment > \uicontrol System. - \if defined(qtcreator) To use an \l{Terminal} {internal terminal}, select \preferences > \uicontrol Terminal > \uicontrol {Use internal terminal}. - \endif \li Search in the selected directory. \li View file properties, such as name, path, MIME type, default editor, line endings, indentation, owner, size, last read and modified dates, and permissions. - \li Create new files. For more information, see - \if defined(qtdesignstudio) - \l{Adding Files to Projects}. - \else - \l{Create files}. - \endif + \li Create new files. For more information, see {Create files}. \li Rename existing files. To move the file to another directory, enter the relative or absolute path to its new location in addition to the new filename. \li Remove existing files. \li Create new folders. - \if defined(qtcreator) \li Compare the selected file with the currently open file in the diff editor. For more information, see \l{Compare files}. - \endif \li Display the contents of a particular directory in the view. \li Collapse all open folders. \endlist - \if defined(qtcreator) \section1 File System View Toolbar The toolbar in the \uicontrol {File System} view has additional diff --git a/doc/qtcreator/src/user-interface/creator-open-documents-view.qdoc b/doc/qtcreator/src/user-interface/creator-open-documents-view.qdoc index 87760c448c6..67c0134c78d 100644 --- a/doc/qtcreator/src/user-interface/creator-open-documents-view.qdoc +++ b/doc/qtcreator/src/user-interface/creator-open-documents-view.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2023 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -16,13 +16,26 @@ \title Open Documents + \if defined(qtdesignstudio) + \brief View currently open files. + \else \brief Shows currently open files. + \endif + \if defined(qtdesignstudio) + \image open-documents-view.webp {Open Documents view} + \else \image qtcreator-open-documents-view.png {Open Documents view} + \endif You can use the context menu to apply some of the functions also available - in the \uicontrol File menu and in the \l {File System Context Menu} - {File System} view to the file that you select in the view. + in the \uicontrol File menu and in the + \if defined(qtcreator) + \l {File System Context Menu}{File System} + \else + \uicontrol {File System} + \endif + view to the file that you select in the view. In addition, you can: diff --git a/doc/qtcreator/src/user-interface/creator-projects-view.qdoc b/doc/qtcreator/src/user-interface/creator-projects-view.qdoc index b6b74f0993b..d28e6f9ef00 100644 --- a/doc/qtcreator/src/user-interface/creator-projects-view.qdoc +++ b/doc/qtcreator/src/user-interface/creator-projects-view.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2023 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -15,7 +15,11 @@ \title Projects + \if defined(qtdesignstudio) + \brief View a list of the files contained within the open project. + \else \brief Shows a list of projects in a project tree. + \endif \if defined(qtcreator) The project tree has a list of all projects open in the current @@ -29,7 +33,7 @@ is the fastest way to find a particular project, file, class, or function, or almost anything else in your project. \else - \image qtcreator-projects-view-design.png {Projects view} + \image projects-view-design.webp {Projects view} \endif You can use the project tree in the following ways: @@ -43,14 +47,19 @@ Use the \l{Switch between modes} {mode selector} to open the current file in another editor. \endif - \li To bring up a \l{Projects View Context Menu}{context menu} + \li To open a + \if defined(qtcreator) + \l{Projects View Context Menu}{context menu} + \else + context menu + \endif that has the actions most commonly needed, right-click an item in the project tree. For example, through the menu of the project root directory you can, among other actions, run and close the project. \li To see the absolute path of a file, move the mouse pointer over the file name. - \li To move files from one project to another, drag-and-drop them + \li To move files from one project to another, drag them in the project tree. \QC makes the necessary changes to project configuration files. \endlist @@ -58,6 +67,7 @@ \note If you cannot see a file in the \l Projects view, switch to the \uicontrol {File System} view, which shows all the files in the file system. + \if defined(qtcreator) \section1 Projects View Context Menu The \uicontrol Projects view has context menus for managing projects, @@ -65,22 +75,15 @@ projects and subprojects: \list - \if defined(qtcreator) \li Set a project as the active project. - \endif \li Execute \uicontrol Build menu commands. \li Create new files. For more information, see - \if defined(qtdesignstudio) - \l{Adding Files to Projects}. - \else \l{Create files}. - \endif \li Rename existing files. If you change the base name of a file, \QC displays a list of other files with the same base name and offers to rename them as well. If you rename a UI file (.ui), \QC also changes corresponding include statements accordingly. \li Remove existing files. - \if defined(qtcreator) \li Remove existing directories from \l{Import an existing project} {generic projects}. \li Add existing files and directories. @@ -88,16 +91,13 @@ \l{Add libraries to qmake projects}. \li Add and remove subprojects. \li Find unused functions. - \endif \li Search in the selected directory. \li Open a terminal window in the project directory. To specify the terminal to use on Linux and \macos, select \preferences > \uicontrol Environment > \uicontrol System. - \if defined(qtcreator) To use an \l{Terminal}{internal terminal}, select \preferences > \uicontrol Terminal > \uicontrol {Use internal terminal}. - \endif \li Open a terminal window in the project directory that you configured for building or running the project. \li Expand or collapse the tree view to show or hide all files and @@ -114,7 +114,6 @@ the \l {File System} view. To view a project in it, select \uicontrol {Show in File System View}. - \if defined(qtcreator) \section1 Projects View Toolbar The toolbar in the \uicontrol Projects view has additional options. diff --git a/doc/qtdesignstudio/images/3d-view-context-menu.png b/doc/qtdesignstudio/images/3d-view-context-menu.png deleted file mode 100644 index 5b08c568cb4d234c2629c5a6d5aed53a66b67ec0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10442 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/filesystem-view-design.webp b/doc/qtdesignstudio/images/filesystem-view-design.webp new file mode 100644 index 0000000000000000000000000000000000000000..8d59b8548274072e191c1aafa2d40dfe3e1d7d84 GIT binary patch literal 5944 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/icons/add_texture.png b/doc/qtdesignstudio/images/icons/add_texture.png new file mode 100644 index 0000000000000000000000000000000000000000..8b94da637d9632d8cc5873524e24f692df88742d GIT binary patch literal 1716 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7I14-?iy0WWg+Z8+Vb&Z8 z1_qXQnIRD+5xzcF$@#f@i7EL>sd^Q;1q>iyV_#8_n4FzjqL7rDo|$K>^nUk#C56ls zTcvPQUjyF)=hTc$kE){7;3~h6MhI|Z8xtBTx$+|-gpg^Jvqyke^gTP3i$ zR(Zu%AYpwa1+bEmY+EHqkcA2nz5xo(`9-M;mU@P|$p!{S3g(u2DM^XODT&593PuJ- z#`=av`i9232Bua9W>zMa3Q(YAr(jc*l4cd;;s&*>C?(BSDWjyMz)D}gyu4hm+*mKa zC|%#s($Z4jz)0W7NVg~@O}Dr*uOzWTH?LS3W`avc7m0xRdD)WnkfqLBRj99T>Rz?`gLWU6PTpb?&#my%yztcj!{)g`ec)mF*K zz{o_`z(m);IK>Ib3SVC<&%EN2#JuEGPZwLI zirfOd%*+%kOUqXEz% zmP5+3Fy%omZXhPOd;yg;b_$3>EHy91R;ftI-tM!AP896^6yvIzZqnCGQqVJ}KI`92%H&X3GGHU zFaIXnFOYk#$UEh)|6@7XifgZ#XG~(48PRI}_iSVOKKbGoN2apK1cf9qHCJ3{`<8sz zs8U?e{78TQu`-_D@|WxF dpUbVOW8Ao*aKeMVUNWG1#naW#Wt~$(695GgPY3`2 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/icons/reverse_order.png b/doc/qtdesignstudio/images/icons/reverse_order.png new file mode 100644 index 0000000000000000000000000000000000000000..5e661b29cc312b210cc9caed3d1b718998b7202e GIT binary patch literal 1740 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4i*LmhONKMUokK+a29w(7Bet#3xhBt!>lxV%QuQiw3m8Da#=fE;F*!T6L?J0PJu}Z%>HY5gN(z}N zwo2iqz6QPp&Z!xh9#uuD!Bu`C$yM3OmMKd1b_zBXRu#Dgxv3?I3Kh9IdBs*0wn|`g zt@4VkK*IV;3ScEA*|tiKAPW^Dd;=7m^NUgyEcFa^lMM`v6wEF4Qj!vlQxc7J6pRdv zjP(tT^bL)54NR>J%&bf-6`(-LPQj)qCCw_x#SLm%QA(PvQbtKhft9{~d3m{Bxv^e; zQM$gNrKP35fswwEk#12+nr?ArUP)qwZeFo6%mkOz;^d;tf|AVqJOz-6iAnjTCALaR zP-81{3*g4)6+^=*IX_pwBC$Zl^AB z!qpaJW z)RM%M#F9jUic^xU((;RP6H9EBGIJBtQ}qk-(v=k8!D!{2pOatYo1cluGANQ$Q znOMSuD4U`B%QI5*@J2qw_Tv1slJdl&RLAtxyb^+mOCOixixbQ6JDs?wgt!UrbXj zKR9-LLAA2rjXx}T*K?NNEK@913%!!%f z;1yM_k6}9ma@-!=ih6kR#FomFKhxT+FVdQ&MBb@06uv$-2eap literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/material-editor-browser.webp b/doc/qtdesignstudio/images/material-editor-browser.webp index 160c96e877ca70395dfd630bf259e415f3a2da10..1865bf8a5474f4e6c201cfc5a29b886caf61572e 100644 GIT binary patch literal 36366 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 literal 42272 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Ɉ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 diff --git a/doc/qtdesignstudio/images/materials-remove-material.png b/doc/qtdesignstudio/images/materials-remove-material.png deleted file mode 100644 index 9ef0a91e5be01fef54067ad05e81d574e6db631f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15142 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 h99YO9`QyQl|GjhFeoo@r^NNoTB diff --git a/doc/qtdesignstudio/images/materials-remove-material.webp b/doc/qtdesignstudio/images/materials-remove-material.webp new file mode 100644 index 0000000000000000000000000000000000000000..64d1d198913a700c6204387b195619e6fe9f78a6 GIT binary patch literal 5594 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/navigator-material-texture.png b/doc/qtdesignstudio/images/navigator-material-texture.png deleted file mode 100644 index 849625e1ebda9f6228254eee25a4e1a1db93d9c4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4048 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 IESQ)X06&Fs`v3p{ literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/open-documents-view.webp b/doc/qtdesignstudio/images/open-documents-view.webp new file mode 100644 index 0000000000000000000000000000000000000000..c3080b92b55e68d644846e69683894f048602f3e GIT binary patch literal 2338 zcmWIYbaRv9WMBw)bqWXzu<+sIWMI($$sojVFW|y9-pM`oZ|#``4H*;WUff%|+E|M7 z@UjJb94U`4)^1oG^ZNh!{X6B?NzTpQpq9TaH*Z_$@n;d`Z)e0f-L0GWr2Ny*KUuf` ztlhfv>7Py6tKNRA<~BXon8nC&@J0Q$l-ax|x80P;y`8gdyK}VFp95dBuT=-+MhD+} z;u+xPYPgS~Sj4#aQG~IWNb&2a<5$m|G1lC5;mrN5zuq6nRdFpk`^hlg*0FEP+uX-< z8m|Ye&3?14Qee*EmqJ>VtXFL~&MEz||2O~6p1DrXb3&xT*4P}r#`%P2PH)q>D^^oy zHAa2o)49OnFpEd!!*PaxySLvK@lFz~l4#M&QHthW!YF-WLB9~2YKiB$!};r-7_@?F zl|}xRD|k+tBC=pzy6fRJ2L2P~ysf|G5!$1=d|Jw_1x8M;3H&k5nO&#L@=^9{4AayZpL?Yk*w*Boe|z{StC~fUO;Dfx z@;VLHEq!xzp6yihkn)OO_xKU-m6O`xfgAf0CeJc>YpxghRKY9T*}Okc^5pYVx~*^6 zoi~_tm@Vh}`gHwdCgItSvivv_FIjWVpLcSy+$F0l7rEERB-U^&(>?c;=luR^g%1uj zWyX5hyYF7T705B=@{;Y6N)TWQR$Lbr=lBs3g5hx{qr%;?pR)U;0&*gyxD^Mud@CYdR{sk8xXZa&eWmQwwjHH zGuA1I<4n+T$8}3~@lB|FveY5_TAP4;VSxv)p;xy^^Ogzsz1vMYXNQF+lr&CFT&DB& z&s2$H$EJQ>bs*DxgXRv8lgo=IA5?F-e@6y0sHV?mdC43U!xrKed8mrxYWtkQ{ORVl z8;2XSdD8PN?oKymP5o9V`9*kny!uS-+U867$MdeYJ>ES-`EdBn`9aH0FogLX`^pri zx8Qom>?boP{N52^6L-#HuJJC8ZGVEo{wP>=tyiyf-ltn%d9TuWooj1X*P~Y)C-wby zz5D)!Bk5R&qHAW9_7a&bm(Ipk`|R9nzADpW>(zaoPM)a&*PdK3`teVs;<#44uhHV2 zxic4RIy#N9$}%_d?|=DH(c5o59M$9Ms=emTt=#vfRQ*X`_~v=iN!RY|vkYKmO`U3y z#?=}1%is0!UWK6NFItzS)T&OuH09Wf)oKOB!P^_37jBy6R~+qgZ_=`zKX2Xo^d;%R z+k=1me!czqr#<;yp2z3HM<1FFEHvx)*6*0v>Tkz8uWCySYhcrX*T8GT5f@ zCD-1b=Txw4|0#~2``&o_D~fOZ{%=aVac|62y`Zg28xDP*C(&@PLt9o#pCiTL#hjuI zm)Y)|Y?2dmKl)^^z7YFf<0}H(XP5EMa58N^;&5KT>PFT5YjuznV%`tH7(K8I5cvjW(}d{#jW#z2mN`*QoX+Qft3(ei<(A@GyZy2Qa|HAJ#02F#YM)r^PBZKJpNvwh*|i{0 z=IaTGxL5&}6Qz!e@2WX1vz44~b#Fry!|GOnl7D9&e`TG+(xGN^RMo0ld&h*28$x|8 z_DCg)PYCC}W?y1+bZNrT8@6uUT?G~VidoWk3?3(Tb6MMTIDJW;_vn&#eB$IF)rPZP zZ$8*HAM#9nw!pJZ<1AOq`sKeKe(p=3Z+?q!cbvD5?yR~WL9Fo$_t(A*jd-UuG57D| z(EmsV}GIZ@XCA zd-mchzV#j^$_iD>N<|jA-rhf<>A0O1kH7n`$$NQM{dka2zsYs+<71QmnwH;r?s_*!J#=na0F|Ipq`X zr8RJuE>U;f;n%rUU@_kp+2XaABdX4szn@Y4Iytd#-n`ShKU~&(|4v9ZYR-<`(aTw8 zd&J0}E_@^tziYR1f<>8&%8qK+?|NDnlcN~Eo>_P2@~fA}i&wQ@d8Z|pF-7TI>DRG-IOcigz;bG}G)p5wd=8Tvb)Z@Y0&eSCr_=S(`T7%? zREzfM60bD=JLgLOe8!e@U4BnYmb+?|&ucY{>$aC}Wp1CcD!op0$y*MuW|qXrrz%q{ z9x-q(%;jM4Wp$i3S6JZ4q{)-hv~N_uXsCRVx6nXb{4p0(1Pfzfx2jYH=h=T3wpx1& zJhffM%9URAR8#Mw)R7L&lFezxn=W}Wr??J;ckY%x@DXhJqcTn zG=5$)0bK(B-^qE&*)eG*@YSsueV!6!JIyYjfO@O}C z&Q%dY8Ivx~o7Ho4p9r(5BI8+~)1}5qUUkeI)%>y+{hfSj;yFSuUaj(y^mUW3<8Kmj zxiG({L2+t=&ua^vlV5daT4?QD5NEN;{Z+l^)-<(L(F&O_-DVlfcvg07hEBhaBIuh5HZ+y-0t@7$i^%FEjWg?Et-%@`1J6q%Ymlye>Kh`qL Wsk~FCx;*+Ih1(*Q8=5a3o literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/projects-view-design.webp b/doc/qtdesignstudio/images/projects-view-design.webp new file mode 100644 index 0000000000000000000000000000000000000000..6fd1e6333ca7d02e7746149bf3fdf065eb514800 GIT binary patch literal 4660 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 TdMX(h7#KWV{an^LB{Ts5-NrDB diff --git a/doc/qtdesignstudio/images/qmldesigner-canvas-color.webp b/doc/qtdesignstudio/images/qmldesigner-canvas-color.webp new file mode 100644 index 0000000000000000000000000000000000000000..3474076844d5cb1ef54641b79bc3dc9e43105c00 GIT binary patch literal 3558 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 NH7_@}iK;2g3;_R6;mrU5 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/qmldesigner-element-properties.png b/doc/qtdesignstudio/images/qmldesigner-element-properties.png deleted file mode 100644 index 0a8c4a10412ddbeb2b6bc9efbd75d898d4af175a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27358 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 C;+?1f literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/qmldesigner-form-editor-move-cursor.png b/doc/qtdesignstudio/images/qmldesigner-form-editor-move-cursor.png deleted file mode 100644 index 018a94c88f0805c0c729d036c2d65a6e32979905..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9983 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 Pz`)??>gTe~DWM4f8uNbR diff --git a/doc/qtdesignstudio/images/qmldesigner-form-editor-move-cursor.webp b/doc/qtdesignstudio/images/qmldesigner-form-editor-move-cursor.webp new file mode 100644 index 0000000000000000000000000000000000000000..a1b7f83e8d60a68a41ed3f706fcc33e84d4a3fd1 GIT binary patch literal 3430 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

%eQPX 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 TyL-J13=9mOu6{1-oD!M<$!cfN diff --git a/doc/qtdesignstudio/images/qmldesigner-preview-size.webp b/doc/qtdesignstudio/images/qmldesigner-preview-size.webp new file mode 100644 index 0000000000000000000000000000000000000000..37f0c32ecf5576180bd0cfe1897bec5db5d20f62 GIT binary patch literal 15954 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 GfB*oYm*)Hc literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/qmldesigner-snap-margins.png b/doc/qtdesignstudio/images/qmldesigner-snap-margins.png deleted file mode 100644 index 703ed8cc9a524afad899894e7010e5db8dacf95f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6617 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 T?*41HUeUh$FMs)Oc_sz`w{Q)7 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/qmldesigner-transition-editor-startup.png b/doc/qtdesignstudio/images/qmldesigner-transition-editor-startup.png deleted file mode 100644 index d269470e0d00a4e8cc086a40c9b2d10d8b270324..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3840 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 Ib4q9e0LyTc!~g&Q diff --git a/doc/qtdesignstudio/images/qmldesigner-transition-editor-startup.webp b/doc/qtdesignstudio/images/qmldesigner-transition-editor-startup.webp new file mode 100644 index 0000000000000000000000000000000000000000..a246e48411fe6ee305fbe4c8b5e28ad79c7e1ee8 GIT binary patch literal 2316 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/qmldesigner-transitions.webp b/doc/qtdesignstudio/images/qmldesigner-transitions.webp new file mode 100644 index 0000000000000000000000000000000000000000..ca4aae53ffba3dc84984ba379a6a1a853670baf3 GIT binary patch literal 20632 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/qmldesigner-zooming.gif b/doc/qtdesignstudio/images/qmldesigner-zooming.gif index d8d5beb6b3a1ff40a98e38752cdc51932725ba3e..82de8223be54b7edf8c34608e55cdd2307c1611e 100644 GIT binary patch delta 77490 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

s(Y%{&GIZ74;om+G9EN?sI5?FJoobX14r#O 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�sApmq(#hD6(BHx(Zj|%lgW{16A@!g&86TC7_0&t57rlA$QTfCKCHF}z zk#AE>%ir;B%6VCo-~c{d5PZ=22}W(`1s|J18%o?aEI8QwpM}rm#E1D?vYDinLL>r{ z9J@qylg`{IP(I!%VSFO;L_snezp{1PpY};h4w@JwrzvzS^pa{ZOch(xX`DWb=W0{f z@9?J!&ezR%YW}2CA(C+^%lrS+DOR6XZdq!(NoWn*gZr1WE*NY{;7E9JJv~Wq+Fz+F zl5AV!R=WmwEqr&oIreFq25V`{k#!f1wj2mJ`}Y2UWJ8|Uw+-L!KX|q4?pBY@5+SEp nyAwVbe2aHrQ2ZzAo0yWSP@Y+mp%9#0l$x3slJB0G#$XKqrvF)5 literal 93266 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

@L41@{lpP9tB;+YXTn4_vLdhS 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 l3**QOSwFsIAROJQ-J+pqYk2|-#fm;SQV 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 diff --git a/doc/qtdesignstudio/images/qtquick-assets-tab.png b/doc/qtdesignstudio/images/qtquick-assets-tab.png deleted file mode 100644 index 11c4fe54b476486a2e4d5e9597587c6cea512b3a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16953 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 diff --git a/doc/qtdesignstudio/images/qtquick-assets-tab.webp b/doc/qtdesignstudio/images/qtquick-assets-tab.webp new file mode 100644 index 0000000000000000000000000000000000000000..2e65a7edd99921dd9bff68e1e21e15d0e79980ae GIT binary patch literal 8972 zcmWIYbaP`-W?%?+bqWXzu<-e=#K52*d@!3Kl>PR$UKP*3(`}_Q%io_=N@8MIU9xn^ z+{~G){xoN`UpeSIIdm;YO5Ex$w}6wY(&V;Ue|q)y(UQzg5#?D?pRIHsUt~zwDs7$5 z`kpmxf}W*&+wx-1lJ)B@l&55-M5ayBF^-;gW}438FE&QEjX(8Bva>u(zaTw%P4)UU z8&wY{%$Paz{Lk|{-M?3s{8#jp<}8nN5c?p0>tKi;^X(H#TUxibMcdS!SKrYSy)>zB z*OvGElfQ&bm^SnBq#We;l{^UJM~EWxw&wL8iLOXlmzPLJ5MKeDN6`IYIS zOXjk&TyOXlWb7{)F=C-=hq!w;_Ps}!K?f@gYov(+}rn5F3l6!rMM+= zg_%o&TUyvezTLlTe{J5bAKU6GX}xe}%ROt)d;bnMshkX&)V;iV58IW=)fSuoL|tS^ zD9w59_|wpNCKSYh=}1FkV`F3IBBRMOFTJm-(UjhywXVec!PxYXC(mnJU#?fOJB?76gj zxRhvl;L*@jM*pLs4|lB6d9W&HLCKxqrT9;{Gn9wtv$(LS~`Mx?Cg?Tp^zeU8^iaepZCq<4qt7`acKC%vBY`l&#x*f z+Ebew^XDFZXmP0QRPK&N>-;jxShsb~%AM-7;kJr){>nsVt8ZC>Ui_Terc1T%ziXM2 zv(lC+=F5h|2j_9taBCYZ5Zm!Cadl_-(Xajcr`71lSJ;YMCNnXFN@-Xdq*h!wXr?L#M-#<5L z9rxP_yk?<{-bxa4J;cjd9DA3`*iD^2efDIQ#VQd_3R_~CPV1ywJ(gsQ>e=I-Dfjq{ zO{PQl@_R1=#p36)?YQ#!l-6&C8ILuExdm5bzj(A$;y_G)3%90`#DQgxzTLlnK6{4T zq{DJT!sjoBMYBh%ofKG@W+)!CLQs3bdmoFQtiFw$%j@*55uZ{_JEwND z$8C6MAKz-Yz)nxPVgct*2MHS$?K3|<%d@dhENMQn*W~m(bK@V1&h5YdrFQ;y-`6I< z_Mzp;RlVE!J(4V2|Nr=3t6OKX^Ob?cv!W|o6_1;Kc=rF@=DPL0ew>O&$|vnO|J{B5 z4Ud5RZ@$`g{44#K%c4Y~zbrdmg*$O-=E*`Tw&;?Xp`(W`Fq^mmHB2VcqjZNKjN-a~%HI^Dk}=P}R!+_rclZ{9NDQJ{Z#wa@;Oegc9ydew(HpRVV(I(Dg+wfu9I$1AJT zf0l3Wy1>Y@UZvP%|7o2UYPaUD5=}l;Y3lkfcF&i8*;~~*`Gw~0J9qJ)=60`^3IBKB z|GvG!rCsLszo+-ARnK4G$rIr}8E1QAmTcMQRYG2RYmBy4{XB5E?z+8P=gMWVZ>8t; zPrLue*0i|P{>i6zjQ1Yt_WX)bo|>M2v0(e6)LkisVd?n??7ODl%ig?Z?@TfOpa1_) zcHVF3^x)3x@}FT#E*g}r@9i?PX_Y#<_kLa7ggX0^i}q~S|FzJuDueG`Wa#^vqgOa4 zE3QynlK<^j%uV^iz1q!h&E5E0MAYy7umAZpzlY_?org338*Qqa&{MO|Y~DXbbB_s2 z(w{zOf5-ppf85HQozoPzUOD#U^wxtjKUf_-o^Uhm#kG>Mt6LQQ^}ql2$8OUmR`L9| ztS&X*6PB#E`1bKvj;~=%``Ld__dMrh*uwqacIk@C=i&~v{2iKya%yn@AueW@p-~fuUHPHg)=NazTe?xVN^j?Pujx&dIwt>cjoO+tzcMaV>a*V*=gCX5%}j6ybG%Q`$FhQ3 zJD1pAJ!qGCBY0+c!LIs+8>%>*ZXfkO_>xyN=0I|C=UlXPUICZ?tf=q0&nGTaXKTNm@>Fy2le_8$N-8@<&2qf* z^zOFkpS`X(GurIbZDWHOjHjPfRaJLJrM=^MoER+LCQ-hWEox29^p-VVoRL30AL#mX zTwJr~rt2F2FDu(7n8h!=lX><3@dsvYk7_h;JgegB;fYxHj#u&G&le}R=_s>b`l`Fj zb5?kS%Z`_K8Y8rnUrbB?s^h+1`Fy>oVhiKtF7@XRB3_!YN3(zXqPoWWi>t@ww1W8c z*I%wWC%|C7`MrYv)I^O(nL<;TCLB9En>A{2(8bn$I?8Nf%w59s{t7#>Zg2jca3OcM zyY3v%JJma;DXuk;XHL=ob$cJ<#$y4zo4!7LrnGurd6ed{Lu>YDUu9mv?oqd@Fvn`! zmC3a+V$7><`o|x;b8na7y=F(jKE}Iy#J^AJzODAyKwdGgC-Ktpmyx#vr=QSniG7lg z(4yYbviJC=oz+|O4;I~&PhIjMV?)oRCMs_vN=BO54txR zJQetQYi7{a_s&jYeeY8PSAC33j%`Rhabfx8d3W4OCvHA7@3-Wo-7h1t>ovc`JBDAoFDmf%Z^xz&Tpp9yomFmbZHwVZ*rm49Dl6`Z7(=l1rU_mXRT7j= zinv~#u-VFck)Q?RhP7unSC+P$>N?1%`~9t%zee&Vo7{4*$31U1GIn%MU^}u~hBZR6 zm9e4Mzd(aGH8ttOVc~AKS#kI03wQJ#GCJSyazd(W(KP>W=NwZwV|SPRT^9d%=cm3L zk#d_E>L#1kSCu_udgFE^&S%%tyfqj99hrK2eYSJ#k~^2AF0#CwHSzlH7+WsMUHzXP zG`-!(IKxf(^{ORX>-Tg__4Q|bv5t`9q&|6r=`n#9Pp z@bRym7I)__ZfRH){4qF3(~C7pV)Nr;+hy5KyemmK|H-xZt}*vJMyAi27j$>qzxcO# zj(jP{<-i#|O|$d_=kPUrdb3{VVY1@V3d3_)H3v=$=;RA z=VV75^On#*^VQ4iI?tEB^z z&q@7!(Es$9KCUy}|h+&C)x^kes?7x#N!mL}}!YiO8o`;g(@T#vTOd~P4v z94Sl7xF9%4(H5yGxw6yKj#PEuKSBUyJYFQrc~$A-9E_;_J_N;jcQ!huFZUGvzMbT zzL-NSG^y@=u9;w@`s@}a`^V8cV{Dg4B<19%No6bA++8)Pv^7OEKQ&5Lwr$oM+w~{s zyb4abT=7-)np$XXe#ekP9g+t~nu2-THx?fzoxsi=|QvTiO;wvfnTQ!%?^NlVuwEcgB zUBBqs{p+_W=Xoon{_Y4YqYg$%C{MbZJSe_v|HZ_fOSWGbdu9cW_n3XT$Rce+0bq-Yqg& zyY%I}<{L5YZIhhN$2^)cdH2QnjW3@|dUYJNQ7@agI+tO3?6hT?Rb9;Wah)0yRPEyS zL=}84`NlVw#Xx%5y?Nh6}20?q&9MdBiAf-QU$+xsN?!#gh#THSahM$CjJ^>q<8==e3Q#vTN3v1xAPN zs53qe&pvtVrh1^$yyk>s3-)HYxgXJ9;cqrE>*sf^mVlGz{EsP{IG7lzs?Yr^>>796 z{Mf<|+gs%HPwwS5Sa71`lSy>fC5EE8bKkG}c2A&Zjw-)i`<`RztU*&GGM?O-ab>gC zXUFhU-((GAPwJ(!YAg`{|KH@}pRLT+R~-T-Bz;YF?~|xKW|FMCGWT;@q2rD7OY9-59UNv|{2u#0W6u1{uGeDb8h`V?wIw{clj2}Caowtxj1NEjXV@DHS54cxR@9?~KQZkI4L?+a}IK1(b)>=o7O!eQMvR0yH!dK>W7k+!?e~Wed z+)WCTo~(J6(!y}&TSiv(q-j_-ZzZT9clm*(l7bF$!`)5-|N zDf+K{?*;z&t+hh?X5hnFU$QT3wO!Np$M4}&{sl}iD<)gD{#JIqf8O+j;wPDfSCbmW z^HUG9+L%qdG4sleE3O-j7cHK-TCwF%$ooD`uTzFW z3N4IUt9)B$TQt0K_$n(gdD|16BH5*HcAqle8RVyOu6a0FbJ-cjAR8U=$Rybp^=D_dL^{QMYmdHkrDJw= z8+l3)tHoRRI%_7JNP3dut++}u zY3mQJbUCke=X_^Zxt#y^{8^m%@uf=LduGOK@V$>nVys&}cVq4BHG8sFGvu<}Xlq*- z?z=!q;>Z;yVPA2%o|7zoAH+p$ejgJLJ)U8@N}%!#v%TrtuQ}5`>-HQt)Nn5*u)Nn+ zwBN_o{_)+e-xu9CGhF*xdN{d!yQM^(880gb>k={jgIx(5N+;&?a(w@@tnTo4_bC@P z`vpyyz2sl(i)6Wg^?QGH<*fCH;{9;srFB-P_Hhk`H@3T)cZXMtw-_vl{O}^t)=KeD zSHcEY@ppy`W_^5hHl_97MXs0O9k$OJ?Vi4HOZ~^o!TE;0tn~H2>CCLzksn{hywKX6 zwf%{$nC*_pgMx8v1$)yUg|am3?h2T0k{O?tqxEi`Xm_AgupGbJy?a*$J{hg-|Gn{I zQt$C&by-IXuLYh_+GxvO(_V1F_D9|9H#)Uqecu@8bY3`f+Ub42L4+XBEQX`|cl?S! z$~A!_a6=21c>bePkGPyvVl)jz^#q*meD7l5+fyMe*>}P>#`2ErgYf!LR}Kf)4Ttoz z8^2C|zjMiihvzje99r-qWZ@mAWa8bY67g>b*S3~Vnw8~$jap9iHI?qma5>R;!<6%*Ml{D6 z7gc==;lRH4iCXKzlha;bey&~@D7I2;-r3a9k{v$6uMURBpNrVX-(DP{b9Y9*4{QAU zBH^if7Wf?g`|#5yJ>jeQ)kob;x;FBD|0cSA+Q;kVQ#?x7UhjUbG5?Q@a~Fg95)R4k zuUDNUo9yh(tlrLlA;8-!b=rQ{oUf1U?p1qhPpr4zv-x~Q+;;(|4qX5TC;W=U&J1!c#gjZulTNSyvt}7e9Yag8YmMiG=wY_y-Mf=&h zI~@Po&VAj?D)wVhfx`-y`?0#Bo6l~&bYYr#`Q0zvg{p1m%J!|2Q<{2qRYmxmWtq&A z*56Gys;}|T?rZL{^j>HARYxto=ikq>3Mzc4q}uTMxb0E@T^_dl{NdYH7b|WIn_6b% znIMqRsDG;?V&~Uo*K^rxFU?s}{v+1WmhYK~gzt*>nq3oR54WGn`)LvIk=x7Uj`j5= zTZG^IWt4DfQr&#~yD`^4uFvjUzDkr`*3#o7~pKe=?FD)irs{)$d!(ftcrYHBB4IcE2P+23W> z+u2V$A7wUN{UG?yQ18p@BLDImYQNrD?m6+-G*fal*9w>Z32A#`8VnrC@754T)y zG#ewrL}FBQc_8>%CeU~&7@twF?5gDrv0t0QED!JN3Pept$HLh z<3oe!{spbC-&EYHc=1ZAcI`%rOX*&zr$zOeE;*~A$Mb8 z>h1}z4_Pfu^>8~>@wPo?cY=0h@uxo;ir*4IAz)qDz` z|8LGe?FXGViwrJuOqVT`RJ>qebh0wNbDn~HaJ#IU=^TS#KDDk8-iq&UWwgINdZ8nB z+xcsBd%RCVam0_KH}+TWJ?+b%E5$cMon2TpsLz|#FWlz0bz8qjvra({@3k#14yhgz z=51LP3G-xp>gHZJ7w21jmDTB?k%8>v*UZi{-SW79f0_2sBI2QRg=chd z`s_c#oBK;V(^hEik?=UPoxX`Ekw0hXfOZVHSoX)?wRI%M$BunA1kbzmMikbT4)YN%0KDEZ2X6(Vu z$~PxmIOAW%rpk4PVOirsA9ock<+vn=d8*ggKejpXF5Al3arR_NSQ0cv7EuQ?&rOy zFZO*^xp?c{f#b^4kNMt@;$vzzS}Rxo|7YR9C=ECLycNbXj&^gYBt3fAW+uDIMtJt* zlbejf`PBYTti3hCFEMdn!6o1G*QayDu&EVjT=uwbU$y?|pG0Y6%ayx77k_$Vu=1db z&z>*dcP=bzE|a@xIaBAC)yKLylTR+X-Ct}JS@JvAPtD@4T-b~K-CVINrgblT(DYH` z>uQ09-+cjr@(QP3y8KOU5xgMyS=4FrOqmuA#X}DoIQ+%<3@(U7>Tn0ih`cyaE`8|E zg2Q|>&;H8bOH#>pmSB``F|hq&;=4#<-5e%v!*;`zRMrf`uZ*l3zM5}le7mwWSUPJ1 zqv{DKzeS2Jeg~3Xm9klHn35_JJon#}3<=Gn;)=TxrMdNAwqp3=19OA;z}pBRidHZm zowYz;`Y^`=jcF2&3xjewrrV@FJ-z?%t?;e)uIhhUTUho0@r_WbI3q4#Z;&p|7;q=rQSNu8|*(USAt?&T8Oaj87F z%zgLmZ+q|mO35~Pxc%1Jr-988?#?$)1hAgm%_Z|?r}5eK`=3?s&$=63bwE#U`>A8| zr+uzkdu^4jd8iQ+cPjI&QX4NLv1|_x_e!50&y$nf91oy?N$p z`QUB0w#KB)uHJh!dfoMXrZLrXx;CiSuN9rf@V#e>4tTzc1aE>#&sE z3WuVsgM!{YwXbHFPh2-+L&8hhd;6Eoe)DcUfBn__&w`8|Hr?9h8+_P(?V>fTLULlW z)!Bu)wAGDTTG`ggI(fXveW?8NS#izjIYlnVPv6vk5U@bjuU=|tLWb$7#~Y^Tu&FR7 zA7$IGel_zDXa21`)z?2x9Ih|@=ihf_1$WP#)w8nR@vgYC#YK3gD!cIS)tk&xqnEE{ zv$R^@+j38AU(MTFQx-3;o)I87@xlj*&h+h{15eIB-F)drNy6M^Dw}mbTj{8tHay5t zJn5G0WTn-0Ph53AE<0Uu-Tj@_Gb{7|J6*$f2#az~)fD)Ub;WR&>Xuom?7~hf4mz6u zx?6lrB>IV#we&ToeD3|xnmZ3o_+{zR_;Jeb!zYf!b}sxZa=7-khOV|u@s#U{nvoOw zg2Of}y1yc{(k<@S`u`h#wClaQ7hiXWBh@=|?d;e)O9f3AMPK3Zn(<;cD14kaG_QTT z@}@4xV0l{8pY6BhX<{%i>ueZXWd+MQ-0pXb=wZlkj}HOdpsx2^!dzm3ExFFf?>ujouI0q>g-~UQVn(g z>1j5ujE&n3?ysH^k>~YaX4lP=-`Q&)ADv|X{_O8Ew&3f39|Xp=FHH$tr>1P^xZXTC zq~L5^{@XwQG2pa?(3;;0e9 zCTWy*;f4FWf+wtdN}l}O|6pD8&3{j$j-*fZcbX=;h;@Aw+}0v zb~<4xcLm!>3cYj>1X zTwOTxxODy#Kkog9i+|7Ae_nO|$F&uK7jrZh+rB!l(zNfm;Hl6R&V_9S9xuEi~O8y$@l-in@$$2Rge)ejcb=N*qe6PT6%~4!PytK%k6x% z)c(bWQ`SBo+WpsMohhICQftjjwruCZ8)^wpSe9Qu$WmsQppo%s-i~#4_bQJFuK#%U z`PAB?Gl*R@gcC+4x7oVe;k%+IA~<2KBx-@gCToMZYKac@7qzS|ek({_S0V7)%a z`~?q!qnXz&Sy!@W`nDIgpVZgSnX}=fE9)Y&PUStTIW};tDZJ1ux?5wjo}~WUJ8!Oi zUXvBQ>QYvBvti%Sb75;6c*-BuNKL(VDtyUmx4NQGH`%S}D|$KwBYGIRJM|YPT(~lG z&poBZH5T~Y zt)@j$vdJtO7bClmG+nR0ck~bU#3$W1CE6BdMkQHxA8{1;ozvFguXEg!$-Q`jsKGN1 z{VNO)((fvySc+{DNbV3&nzCT|)ISRiMHHMQwCi+)1?;VR43unapXEGddGdd8$cz0> q|3!bTKj4-rXY(;ri)o&cW~nQSf%H$0R>jqROCLMT^f)kql>q?7je6?< literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/qtquick-designer-rotating-items.png b/doc/qtdesignstudio/images/qtquick-designer-rotating-items.png deleted file mode 100644 index f20669be4e63c2a52cf481e398a39c9bcc990b0f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10790 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 diff --git a/doc/qtdesignstudio/images/qtquick-designer-rotating-items.webp b/doc/qtdesignstudio/images/qtquick-designer-rotating-items.webp new file mode 100644 index 0000000000000000000000000000000000000000..17a3a664c0596219ba0a80790d60051d63dee8a9 GIT binary patch literal 6428 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 Wi>hLp!;%0a1$E(?CEI8EFaQ8))1{~Y literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/qtquick-designer-scaling-items.png b/doc/qtdesignstudio/images/qtquick-designer-scaling-items.png deleted file mode 100644 index 891915dae8ecf385166cee1402977152a0b0cf32..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8340 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 diff --git a/doc/qtdesignstudio/images/qtquick-designer-scaling-items.webp b/doc/qtdesignstudio/images/qtquick-designer-scaling-items.webp new file mode 100644 index 0000000000000000000000000000000000000000..cabbd7e02a409cb7af81168c680624195fc3ee4a GIT binary patch literal 3348 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/qtquick-library-context-menu.png b/doc/qtdesignstudio/images/qtquick-library-context-menu.png deleted file mode 100644 index f611ea6f1cef1ef2ddb52e88229a2a0190cb4f4e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16734 zcmeAS@N?(olHy`uVBq!ia0y~yU^HQ1U~K1LVqjp{xYtscfx%JL)5S5Q;?~=}>>(l1 zzn@huzh3WKzjxX4>VKosXRe(rj;!G}UNh+M%p%Q=R2_iG*te`c-U`zCf#zDd>j zsVnrtlrOG(!~e5~-(t`6{=Xg8uK8j0U)1B6O`behBBcE3qhPJq!c6@ddH*!`&g;Lt z7k=JyOZw`OFNf!gURB*z@AGY4tfBlb?fOf98{72eXqlC#8r$z@m~-ptzRRLt+5G0_ z|L3_>sMsL<>rKJ7$rn1$^_c|6e%SqI`)RhdHo;qW-#++Wcl+JG;OS3e?RE*@)nkqA zKDTcEou~h!@^kJl{wK25p5^WQo4qPm->=xZ@$~vTsx$V#58kwU*{Ap|UW~jE7S=Pa zEZWR%5c~6@R-&QZtMIwoY62oJOa8p2zf^qU=Ep2|*R48!`klv|s|_VNIk#8s=DNog z{JT%Cq2~YclDpkqFBY@}Z+?CEc+F*Pv#pjFwR~;=exE(h$$ffr>6V`*df#^?{By7{ z&DEW|e%0%#8Cy@4idVT{4BYZyM1rg+N-CGOO>zc^6j^Nc;)L(!`)jhcjpDhzTQ_FbCiAQ z)&${+pKEhFa-S~oo*(yJ&+e^E(C-QF-oLFq8yx;T^>}8f`|oqp?>}9&E6($I*GKDF zStWO7%y_ppcZ{8DKuQajcWxu@;*1yt#h4Gxe z#H@=)IT-yonC3JHH!-NQI6|qv+6Op6Y=r}yj?IkclV{_)}A{{R1e=L@jZZsJg7>e}!t#Nwq$iJq4V;b_!Wq>46^zWp zxGHwM7BDGzU^08p-?Y9pOTV6-eeKz;HLGXe?XiEM#hG3FS};t_sqX%tYsciYzvq6J zZe1@g^{Qajk2xpub$0GNT0Et5;>Md2@86cI@zcn8F0@THx3gHQmUuQ}ZO*j({i>edl4jTHuk(LVxn{$sXYq@9 zL;ed!SXlQyVDjTA_{KYDv6)KPT|R^NdoEV_I)&chTKzqKe&_$Ct1hj08M{d5{w3oN zf10PSpBHoAKK!ii`I<|s7MsiMulo7p*utON+P0(~`Qsd}WWM%B@zaNIW96LI9jLlB zEx7P}+}@gs)bAzUqI3G2#n$$A)zAE6VO6k6lZ(kj<>8$f5tYTB;TGXX!;i1u^v?H1 z$=$gj%IEiPtKHR`SlxT^cZS=?=Do!+F@Kum!cy7=tHu7cpJUVgB4Je#-T zbx$uvAGJKcPCnYU{vprui`;tC!&K#Fyf?ddHUIFn%EciCV$1JYZMcwpY0d=oO_wy} z4ZE03zC3ucyCu!@{lBGi`#!m?_g&$6({$bRx7*mRUEUUddTstXj~i>d3ZHea^Nf4* zRmy$S_W9-8RJ21v`JaB|^>w$b{GnqzFRZGt&+vNEOWn+{6|1dex847=GCbx zOq8#LE~v`g_{8nyV_%QLg^C>N)6I<@Oxt4l{Z9P3v^J*33pI~cd9(7is;Ac^G_U=5 zM7Jv~ z!SY)h-&_$0ubXa_Ebd%;xz?oSxx8EY3h&JhthpBMy8D8y&GtpI=YD7BT#2!M zTM<~Loxt*-g<-jySY2QxudDd8=+%KwR$FPGb(Oy&p0wBGSE^IUg*r=}wtE#9Rahu)vD0RD!)(z_Q-Wtd zJ3nv#OP3o$*#|gz3oX`$-<_O&;*ZBi-I(cb?Br@+fBVWUX#LDu%J{iZgl}y5mrrkR zUqAmY-tJ^mhIr41o8QXM^memPJNsK(qvUN!@Vc$Sujn?+rgi2#Yz$|ICkbN24?aObo7jH7dOD<}7dm6(<}R6sX24 za01tOPztmDv#^h=$?lJoyB0Brv58G)&ar#H*T4C0cI+>>m}(G4F?7ZM2Tw}FrRP|E zdm!c<@c+Qc&9PNRdWsM0(p6P#s=htCsVsZ@v7g>n<^=**;xpf}yZGHn4gevfx%&`OV({I#ub%F|v?%9@|M<;TDDRBH+T1X zh51!+%e%P!eSR+fd@Yf!Zu+TN-HXH{{}`>wz1*L4f3juo)9LBbeA&&xvnt=md4Apd zG|j%GIkm=iQ zzkcK`T*#?@^7{4fk01U?{Bmx~uP3?6^)ISEoKOCqdTl??mYE^m+MkE3nAK$u&)e|0E;T#b_tnj-es|7!! zU%33#Sh{Vt#heV>6S_q9tC_=8VJW48I*ysPOSanIdgmFwV*YE8D`8jXTf#^rB^a$HRGft1RpvDVLNl zEm%HZGjC;2iBiQXo{XP)1? zye=7oOFFl)R|N5WB-GVNrciN}? zljrGYTyQ(5SMx0|uuZQ(EWPFTS-WYGB?qT3UOBbz?({&p%_od4_WE(3lXrjbGtDj{ z@zdRW;r=JrkLZaRS+`Zsn|2~k_rdosg}Wa9Ihb{J@vdb(k2m{ld?aBKaXE^2{@ysf zrP_10IGrk(eL(!u5z{muXWr^xGxuz9)>{*}d&|Q=M_)Sl`Q8x{o%CO3yZ+{FZ$Bia zWlt7~{Qc&3bnzD*wH>QXPv-8qSztQn;LNT~Thw#C_Ut+rR{N%0X8K)z?OC&zo?Je? z|M7xdoV=&3)OC+sc-ZKg&Zn7zw&ha_B@y{ckZ~A_U zlBwa(1E&3IK6{SO;^t-jkUhC8SFcM6361@~%QXAHMV0dH_g!0_-IB>$ety9bi_Pnp z&%MjcT`n@+T2*K5zE{7P4ktDU7cia6E2#VZ_{4$dld7Yy&Cx$?zbb0`3gJ0SQL{I{ zIiz%amn%y@(i~Yu2JHmq{UjB1epEvEu)v5D8FZ2F&vdXt@U0-x|jjr#FyT1%GcQ0+7@iqAONBd%( zxyyc>nPqync8%ok`LmwQjsN-V+O17S&sQ7#`4`^!^@pbM-x@XRev!ZTc3+FSz+}O0 zJ|}*o1NQ@#ISs;Wb3h(E=g9ng!rV7&tmo{H*WVj`e@^)Ihc&Lw9xre8n?Jm#dC!1fIDq%BWnYXrD%%84@M|7&?4Svf#c=O@wrX|;o zDf@FhQxn_vGjQSUuje_`^%pMNvu~C5oQstczxwKD#i`qzzi^;(*G5$vv)2C(r8W;( z<~WO-duzU1WaSr6&!wy7Q;V+}=ALQ3bk%dG*>>)5`?K@^EGVfjd%ErT%{K8{O7jYH z0u$0oQ_7CM`aM%dG$hh^-qauWPIG&#wwe|A>h8IpAG9sIgI4h^pI%|V_ATG?2adfi zfq$Q${`G2B(Qj_BpUW(o9~-S%xBcd$EUDKuM&|2`HgA3DQ8Vk|-c>!k3`$~aU>?T|9dAEP5nQ6{VdhqerGnvZEXXl*a z@boU;T%(w}%CI$j*1cy_!rq;nl5VzdQ?^=_5OP* z`{lfoR~0*F?qAI~JM_k+nG=m|tlqmEo_%Bat2bYQ4*py=ZNm+b+2LH7r8hN7J+{=u z#ot#giqq}P&b(DWJt^nboJH4<{$ynXg@B)0(Yt+fjs7@w-E*DV9-nc))UZ1HYjmWm zCbQ_l$e8H7({F-;%T4rBD}3I~y0hzf>H6%b`={dfpFMx-YWTsMLY4ZhD?R6BEV-T; zw^R40Wu?s5>aR(wKUp8Pe6-Z*;-x=9w>~)C`rvi%))l4rBGCe-bN(Cbx^vU=9-5x5 z_D*=vDXe~euJ!)<`WN%Nr(0O`JmzMJubdm4y4ZbdYHzQ;bbNf(lye6n!{SXRHa_vX z`1RScz2%$C!uM#XYH!F%cakSY+v*2Q4>?+uPrfOE3F>vnXq;9x3k@E z7R}`6I&t~E`22PCzn`Q&lW!0%s9VvhRG9GK+H?K56Hkj$&!0Mby1ReBed40i;f(1X zHPa5i&FMWJe)}a*R8r7m3q4M2+!WnD<21o1XVsga(bt` zX#P8!vl4zU?&<8jyGG~S$<&t(#;xM|x_RsFS6F}T@1E)H6@5SBs%7NHsg`TIO0Qf0 z3Yl|hwp6O9-*iUx2XD5{Rrm4n^sM~-ZS5!LkibAep-uCTi}OTS2*>YVoBy`p@NDz% z^PjR!+4e+Kbk>UKJaMt@BGaecxHo;8#;g3Rd)8%E2FHf)ekAsAdeY~RY4<<5-TJa? z$u={M-TRg-x?*MZe#WAoFZN#9#y4r(^_70>Uvcp8p7e-{QUbryv_P4h>X6XkG-ppQoDav*0?V}=pyEW@G-tU@lXxEHS)%ku! zimkPU0loheE&lV{c9(zVw6GVS(-Q2G@??3!%C%85&m2p863(aY_N26F^1f?edG0w$ z%8&2diAjyuR!F#cokRU~R9|?_HqHlc*q-nH$ug&wU%l1hY5L{HX7>FaUEPiE=X0Jb z|G;VSTjJb#FSW&o-TMO4c^=4^6ns0P{@{(bh34$gZ{20@?yy?03(v6v8NjBVI(z=& zc{eAk`OoA6iQa{XZau5@J8)wL(yt-fyM@9weAiPxXw@~r*`|NpO-oW1l%TJlt(B21=BhIrpav=_x?Wl@?T&;K*9Su)x#&< zKh65Av`pg5vj-Ei0pzPR=|Db) zySqwPPv0+e`^?S57pE-cx7fd7_hT2s$tRZNf42X-{J(U;H^w<8{1+S=J|8_j{k>H9 z3E?2U(`nzQy!d1xyZeR=^TvP!SJ~(M<$JzXrJPaS2Xm z&*e2<-@Gb2^!O>RbHAj^n=UgH)a6<1mM*9hnqyLa&!J)S&YiBu^$!%@_~H>&R=juf zjMeAGA}TAaJ8vd$vsqiUBaC(V(^Wra-C4q2y*zSuRG)!xPvmTc$@lKRN^=pMmTy_u zwK#y=FL{44sI*BfxU^;(Go#|Bc*vwmHrWezWq+6b@)kKDjG?@6SuO zFTG(F-f4SS)BEPr10}0x{Bob3CO_}Y^_vPu%I-|Pw20g9;gSnm*lwT%ue*A7&!)G{w&a<|c6 zrd4hKKt`zGlFtQZ#&d7pym=R%Z}WfEmPdQmuSmDZo?O1|pys^FWlP>%vJswJuqnq} z_3FzPZczyrL@gXARMVh^JB7earUE67IIjrmc zytsGuo^oitIM3X)^>Hts+ooSW*sUvJkK z7=1e679Z{SJ-+7N)KhzuyP4&~ZSQ8CW}D+I>Njg~t-^ts6DBNpEN&2AvUk_Ji)QJ} zYNxku*=qKJ({I|xIWLNiZoaFRRQj3!{E<_0u1~!7X-?Ve84qs!S%1UoL+LOfN5yo+ESHw?B2N|DvYyH(fh|FGN%qZ(KUB;M&bYj}Gm5 zQoNh#`+cndk$-QMlV+aV9TWENNy>$9cLV)bzn*M%v*?RY+FY%WDM#dQ3yVm`$4sxh zvATH8Hl7{PQ+M8{(f(#_Q5sz*}cNM+*Y?WEn>eF z{M9ghey7kn{;J(wn_b*miNcZ*YM_* zJFo2Ze@|d!#U*zRi}p7kY@=)+b|1DpeSZD&`;PGmhS%<9uUg&QT6IOvJtZ?!D}>kS zfpm04v^1yH!869i1)sl^$YyH>h{msLoVm4i#`VVxJq^~MCQT0Bdj5dMC9Z_fKv^SbjNckULuc3HW7?Rs`?PqW^gZ`lue?tWYqIP3eTr`knD zex==CemNN*unc*)q||qx&dmS6uJ31IYx`^eA-4X@Gxe@H1?9|V#TuV?E_xPzTm8p8 zz3Fql31+R@clGDPb;ssDeA0V=?jnsn+pd;B%1E}`$`+p1zH1Zbxgy?7F~;&$!DZG( zJi6|;W=Kc+f4E)o$TKzhl$`kb6HF(s9t~?VnLVp>;?lnr>FnHzze5XSosFM*vj6|` zdA9Gomz%A2ZTWZbR^YFO;*3L{+I+Q<7LzAW-hUxjbJ^zeQ%$DD+hvs&*ZrHoRgQHOIYOX(yxv#s1S0W zdTpPsjrH}Wvxc8;{9a>bo3iM{+k~*nH(Akk?MEdyYwhdW6#wx|acbA{>N`FWzu1z^ z@2p?j@y$5v|7(M<6^XHjP6^7^YQ1~7v+K#-$32YC18Q%~3X}QORG=3t_2Hcne4q<%WIPUJKJwdp1kwQZrZ%)M`2FY)|jyH$;FvFZTtOF-_(AH+!>>{ zdg@h?WADqtP3^j4w;vTd^=bw4%4IJe+UZaJ_FC|~&CKYU+|HffA|8D_sQLBgLt}B* z>FKS0Po#oB2mP`;VA=9u$<_n=L}qT?x;2OIJGcG$lg~c#@>b@QZ8zM@R&&l;eSg~L zpEZ5^p6%DN;8s%OzFEV#+}K`cU%-LKC4U1L`@deDrhDL~e?gGp!mS7Pam+E{O8Rg{ zaYRr}XpZEIN3jy(sNmnSUg-hQ4%`&~eT@Mixn@^eh?TCcry z{e15hkGnfE&T06} zoF}$+{!RXZp#RofTn||0$d_$i@yYXbdR>^2uFl$k00#93e`d^fjlI96?pgY&wBPn= zixx1PJIJh_9A!T_?B(mj+aj|VUp=o<sGw!L*qokBU4y8?j%%hbQ-!?G&$`g@ z?VqH1cN3FEyU84>%e&OJR56{s=x6MBxn{G;YE{XlU$s-T-HVI;{wi|9c9wq;040zB~2H(`)DKP1jsCO9R9FLc%Pxy^m{m z%T8r|JY{F{)R|?cwiupza`Du2<*A-#r`)fdV&Q&jTl`cu_-UzdYEOiuZpb|IfO(HJ z{fnjw=A`bJ_vwNBsR{R|IPTXd|F`7Mx8|bH<)?V>YlZ*ce@gVf_s%!n8ux$MoL~DFf})Gl;_Bnyx#st@HBOv99d2!|ZTj-q zLHoBn`<{rWZT{p|=2-Vz_387c&yTTNy`073WAnPA=#gCbwr_69|1Zz<+&yO}s2%aQ zppUQS-h`ThZR)!3-#&f%_;>h=zn`uj4YX^Ef5*K!ZhFN0%A%Whets6$NwGP8)X86c z&hsu~XY0e6ChvQip|+GyL`#kXEf z=umcK)LRy_+4FQ$U)$R_o47mQ9AnSlk@RzYW_LXA@}*5ZbDpubtqfD0otl5(6_5J$ zOMlltIPCt7twQ(uft$Ia#*Kg9eAWK@T#w&!PxZvf6Id-6pI>Kqp6AXiHU0cUPL{NH zx8*~_??xmoJa06sa@B@~Iu9!fK5T5c)HY}Fyd+!Z`N2O~cScH@EAz|t>*uZ9x9vdB zQK_x(8e@L_d9&}Wb$`tHY2o+d(iY}FD7Dcx*M9qSYR3lem7i|)OkcTvom^*!-@0|3 z@xIx=Us)7~TR6w|oYm3MJ(;G~)-r$J*Gbt=eO*6JJgB%_NR_uzJzc8wK<3^rZmzkv zre1jP;r2__t3{@BtilY1&)Q6$J$Gj4j6ZJzpX3NCsXzF`^&qG6_N_a2uH3ow>)YAa zX_X%jGuu5Axu0WhKh^fW>Acl_mgf#?hJFu=koYl2Z~EQHw3SusZ~QL`{ii6t?&_Jl zTQ1F*`pTg1Wz&pRdz^(W+2Yi@vfl|U)v;}U88~Uzj;E_KaJW9x`tZ#! z-%5jbOBwO|JSmvdbSX=2i|(znWp^gWxM$DXdhlfFjmb*YJIa=5scp_-o-D=Tw{BhN z85iSo9!s`Qo29&#bxwTX6P~iICZ@86g>Szaxt{a1FTVQa?U98~-o8HeYg_4ojE!x3 zob{A$MaVo{SQin)y6Ttd?K`!9Pwtq$Gu4#Kwbr@#;Y80E=Jm-xcTS93wY8*jvU1mL zF_%X>Wc4)c%@4<1pJ{pbQk35OX=y5-cbLqzS!?69dg{)?8Oc29bM*ABU#6ZrkZJS1 zC^WFe+JCS2gVIGU4@6!^9X(TV?B%032K;&tSmwkF&ar+wbJc>z?2M|-Hi=&~`sN+@ zT;X|k?bB^Z8x7a8q-EL{?pbSje}nZLwr{uNu1vpo=hbVYTX$}xy)4ZPWMNJ}H*uxS z+=5xJtc+jZE%lq|9};-5^JOk~!fc&AYG>`AWSu#n`Nb|&&C;|tGg0hp?>ZF=-sP_% zj{2B9E(yGL&=Zt-_B&Zuc=f-QrQOW`eip5}&i&cPrkMO+JJj@}^zrVM$6iT&*|N^$ z)a?y>&t;nY-m}E2Y;Lo|{R4TqF=wMg=LWAx7BxP=X|Z3=;&_hVq`i02?qB?#VDPhG z+9I8_jizf`_I~wq%-y>uEG+tu!I9GutSKR${{;CNnbkkf%H94ds&4I`WxIkV)mdsh z@(g{nE3hq2aAxMi+yc3ii`PjC?rpW!wAudU4UeMt@tIqHTVB7j-s|4wwOg;9NcMN? z+!LoaIW+a?no^^Xt=HzA^5qNMdW}o{t)Ir!i%MxrI}JBlY92f%6T5mwXWE~+lQ+p1 zFr7PSo;x6 z{P~(r;A)}ZP=j#6rV6Kp7aGpKGnwYAAjG}nP5FV9b<-Qpf>nGrKCm+1CiS@N*9)Jd zX4>dywMCe6-7)=;mY2JCO<36dfXRi0Mk zrxz5-nAuL6b~om|&a+pWS;OS+`6X>T+hvg%dgz?yv>3DF31<%NH-5U)eJ{_}hfl2X zd{=4i+ShmQXi~RqSd4by5*EwPV5h$^wy69vBh>eek@nl;`Z?z6S8eV;TTpr9(3|tp zmnQAs%VYiEiI2?$)$aJrwUwt|Y~^42^0yi|Xl|w#%+lYhd)Cfi;_9t|ro!%5m+m|^ zOE+fwhdsA$d!Mm>V6w=4a(ADvV8%Vy^XB4W^S-%)!e;aN2PX6CL!%1sh3{sZ_F8II zth;bj=C+PMdSNSOUOABI5uADTUPQ{G%IQxh-JW`r-4@&&c(dDT`OihKua@?2vy?W7 zT(@-X*2V*G9nxykTBdjY@=V?Lc9r$h*1*k2Zk76dJabvK>-oXFGjG;3ecV0iRnZ|E z9*7}_uSIQ}nV;en>%CV+TX*voHG419v+WmD4g4-GSaHVpyw0j=-g0l}=3LpjHO$7; z#Pe2!`pISGSFcUKd-siw1H|>eEx)pgCEwknP54zlZ=RA69dGT7y<$Jv= z^AoaCGqp0_b6$r&(`ulpies#o5oPyfx{TIn@!f3UA$y*?ct5Imhix(dz#H zQzmW^2t01&Qu&j$m4AERWz+BpC;t1~`fP4&0k!BJE5 zPwSn_%(vZi_gm1c4ZngP#Qfi3r<^r4O>)uC&(Hm(7D}J<4&Av>*y^oORKZ%UWPQ*$ zdNA+uH$VT1f4ao|Pwno5H+$|(nE3h7*VTL6SE=ncF8LC@w{G6%szawYZoa;5rhVKM z&U0H7j^`EMD?H-Iy-m(aY~%jgk4m~0mSS@Ctg@C;f3EjzHNW*RMY?b9?w<;>`wNdr zFn|4c?qBw_^Izll{{Qffl~3sar$sx%oKG#io6GFOJ@kIg&OCfGcc^Ms2>r}pivlf9>wJ*uAg+v%Lm z+N^W-50lq42H&sxc{j?{?M&kX<-(nzpMHHwXu5pleNG^=u8W9F&7Z_b=dc?o3+H8g z``NT-&74P5#5*U}Eo%@i;N!FikNSB0t%8mIdDEvocRzaZF7JEVrt@~&{uO)n-ss8Q z{n7DO_Z1=070O}9yl0elnG~$6T~hIT*UZSa6+3q-+8${=TE=&Bit3s`_t@HdKNd{6 zbzC~<)`KUO?>yJ;+h5gUwd+=1?LCKylau#8TBKKhC;hE31y!`!4(;t}3t@fNG{p_I65xoy~ zp-u&tytt=E&A<2KS83Ra&4LFb1)iN^p3_k5;p%rayHDBm{3rH}nOYX5dMQU9`QDg4 zM^LuV`29VuY^JS04o%EH+W9(nmnlc-+TyOXx$Bpd9^|<-D@FY7ij%eva*iC(ocFIh z@t)z=Nhw!sy_xwr{hZ(LKfUBS^RW!OyC<*ptnZq$%9Wi_{eetTLC_)3lex`7k@EhT zYu`O>t9a*Lq_VWt^L9>T)5*k4R#n!1^P4aCi~H3E|9>%Q)9u&CICshj_di%}%6fbc z>)TSl#g-SJhL(r*o2GEeWGF5Q3!QuU#P^)cw~y!QeF@>s-4h;JlQxZ`fa%=AtLH7t zj(flSJ9XCbxEGPeQ8$0R(+bUAd+uo)OPpZk*G$EAQ(C+Jo||sE??%@BrL$9>%=-QG ze7Qww?po7%QYHH%!qlf<4^5AavpyGaHA*^mo~YHt1mWH6wmPd9*FN=6dh~hH_Z3mu zuJ5dqxI^dbt=ILsVcpv15ZF9H<#&Vj1C}{6YWo*{SDq$ab^K-pTMb|I_qc5h=TpnA z?*&{apU-E(sJ`r2OW}D&b#Py&K^W3w08em86fpT6;Iv3Q(AmUf!KnVArSQDH_8bij zP#xpqc+j>Tr0JdI0~g0ehyiHqBWIl)58BF06>Pd8bIin7WZ8I{3~s9olriX#WO>y^jLo^1@zCUY+PYrz-BH!xQnT ze~a#`?0dI2=JiYO8lC&ql4jZY&&4y=mrRkCe{tYa;gXAYE=&41s~2oBmpcFNcj%$8 zbg>64b9`ziDNeRfF*<(ZR$p;RQQ5o)CiiAaEY%fplFZ1>eVd{DSEVZV?VFb#TpPZu zYn=Sz)eNc2Wxr3ig}49zGT*-b;i87G3%7roEp^$+*2VT<ZUj=&R}cVih_N2 ztlU0_AHMQF#xwVjRNA~3=eQ+Hn}XvluH}C|c;W=JbZu^CWZb&DYkULOYt5T`^)yds8NV!|n02T{;JU-oN}Z zS);VHbhg38qZ18E=NX*5d-(F{)8*H6tkRaf*Rg8YHfv_|md`8WW;dMw-&FIB$#2?) z?=`M(x3a3;6*m$pKagqkI%HwfG|5}A;lR@=H3$A_XNqc0Km6@SztP)W;Wv2CF;8AQ zBem~D65~X(N9R`yxhBROxi?{ji>LEFPx-}s7Hg;7T^pEk>*1 zcVp`R+l%W*ZYX%Tr}DF=_Tu?2i@R69p0aY@mhx|Nc6R@ZvAKJ^-R9!asY+U*zM8Lu z<{z=#DjKNpFR11ln_pjc#lA-uX9-$~8EwjFo}4cIW6igpW!7J{X4O2eUAgu9wCJo$ zQQOY#o%i4ppwM!RIo5QRZbM40jo}104X0J{^uz2e=&Gx3nGG4!{PVQUzXShzd zQ5HS#S~^RA`t7`W#rkb`9bOj3 zMvuicWoKt?eR4JXS`H`A9P@g|1Dp2$i}LGs`xSg+Nr2}~oxdtc;(Adg$;k zl1ofZT*)P(wk*wuStxa9e9QTJ2UbR%FgZHPwu=|N($l(ZJl#g=zRCebKL44SC4blF6#Q!UpdpTVAhZLb$V8>&TVm$ ztdKqJ%U2|J$#q|5*p}GXxr=tHT2EQ`&MEbwpt;@ynKuPSTYVRGJdz8Q)iek?Wp(;| z|A{-hN^gIYwRYl{xv{hO`mNH}=DD|gq{VfAU9R4Be$LK+QyyMA|MYbDg@Z@5QV)f& zs%~4^Io)S#u+tPK&0tp9ih{0Xb2X-@=Si$AR#Y}*US5!u()i?6lD2c`@=4HP#)WD{ zTqW;n%jaI6P?YI;#iV&#)%!oIMI#rkbK{w#)U@}_`^;Bf>-SxJ^CDT}x?b$Qf{%|bE*AU}f2j2PJX@CMx3<3S@Htd^_t#hLur*71%1>TeT35Gu z<bKex%b2)W4y8L`~YU=6^AFh6vyYm((bj=j8)T|Sl zqwY7yrtS|*?#->)=Bz;VR(Y){buIuoHD7-qDnBUq^M{Gbf)gixF|K%jZ*u+r+FxH@I=3Ivc&r*6Ehy{b z>on!syBixPZwn|EoZGl9V48QX2g|DN)zYl~S^t*)O0Q@07ZEw|^Xg&GilB3zYuB6; zRttKv#7@YmTWgx5<3X2a`z8o*&6{$zt+h4yhE44=o}=eCd?>i(_eJRNl@M>W5Fto<3>JHKtboBPrZ}d74tlBTtn7Ky&sJMv80Zxni^_y=UdnG9m#Ls2A&d9lG zSyfdaGyg%)J%Wu#R1SK2>bUwHGWzr3;osk#2br&^Y$>{$Pll5=`gTS&^STY;wqO1A|3Pv38P8#G?KUdT@^VN$10melzMPfU9k876uzU!E3yYCd>j%KKfBK1Wh^`L9^nc0$EObF=D%3>`*6*QqDZ zMsEu+owQ+z%CtqQf$nEFPq`n|d|~G1^p*cS4^HM&Zp*ZG4Yz8L>e7&0R&Mxq`xSZP ziZPQ zHFe5qIqR}HHx{4yc5Q9+k%g;1etY7%sw2onwMui1&`B3f-tdH9;VV9Q76i@nNnhrl zX!r4N!KR;VPKnpId71q+6x?FoUbLeviE;VwO>dnR1{_FQxPal@dP%=$g_+CdPO}O; zA#yV#zEM&-)zmk{=&83LtNL)ke_?1zDnix*p$8b*)3jJSQw(O z8XV<)C#ZBvm7``wkj2smH}55^mMS~V<65CU=U8x)FxOJktENV-p?A6jU!6O4Y0qWr zLzBH$XG=@1p7-W!(VX%(shOd^wUd?J|8bIL3*Yv2&WGcvrC+b`N5qGJQ^{GT%FW6O zp2H8843Jq|zp}mA=c-fI+NI|_vr?Ce$)1YRln8p{^>Oln#Sc6ePRdXbS%2h+!B>B; z!p=)QJw3C{^8-cp);#6v4YjEMXS4OirGp|invYe_tz12MQ)<_lBlfkgtY6JB=~@?1 zRsK}s)#T?hEvk1%7@W^F4L`zW#cdhWeRFx2$W+!&_Zr>IhE>rgv|aBdmRFr{ejqnX z@AO`Q1pOs_?vFcXKVJ1p$oy7OKmfz@a2d;)^S?|m3AN_*d)2$7PNd4D$4Ymlo+qbP zsOHLz?EyCB@BVz1HkY%h`0(N4;+7VcLsMTY{W5itcCY^eMVBijCGYR;W#^ZlXItI( zVyVoG7pf;_t`j+H!Sd@s`GJ$)J5H>xFTHTPY4P79#i3tQ>(g%ZW>kf&)TtBw++lp( zRA>7)zXn$C2c?^qyt-9$`P=pj#d7PjCU3h@%U#RKc*t%=u~BiE;bgghCn`l5a~Nz_ zc+Qo-lHeUX<%*EE|H1%;rHl?D67TQqojqsHpEoy+xq7B-x^zH6>b;*m=eg(S=L=6Z z&%3jv!E-^7>y%CLT(i$8M01@GG0vR8{8zN%yl{$+@ORnrZjrO5k=vWo`yOnu{TL%- z+4@pebVvL1DKT@D)3Rpp?GDZgIiDV>)4%%U9rJz33bSuENqGAGNSU`U^}yXzhwd!u zI=(x$MfQ*QzS_G>UIf_M`f0woHtX#Mb=PS-t}UzWzIo~Pr85H0`&#a#ojv?2y)k)L z-}Q%n94EE*@4DbJnR~^*f|z;PXM`?ZxOs?!-){5VwolKV%Y1!Qtk#lp>QQ*q_Wg68 zZ?fO}di~DPRz8bGAd>goO5N3*WF21>YQByFD#Z){^)5tQ$&5Go#%tV zkR@tLPSY!$TI+-+gg7p3nf7Ywbp3d>lJ)WXf-SxLv zcj`vd%7=M>i;^`C0wK z4GL8YTVEMmmQ>$dbpD*#wf8*FgL&uVH1xZ|O_+pDxY<~Y|0FgdFo^;eoKJ~(EKYCyrng@ zmkBzZQBbr}pZkAr!K^d;qpk+boj+|(Tu9`%4!Mc1LZiHMX8d$pc4GExFXQ0Ns-Jc> zX`g<3wODG!gPN6G3w74LeRX>(Z=`zK#V1k6E+3lwRQKzoIajXSDT;LcQ&n9%cYDT> zM=xr2ZZXhRUB5@`ZN7cC-}JkjevR%Ce%?1D?KfJjoochIYQ4jx`4dBxvR71=C3OAb z+$8s4$$p)E6EnY-yo{9mnfi0P(mCa|vY!f!7H7@u{5E65$umtSKX}$^U1@Q))D5** zxpQjPjF6>E(iV1Kyy){?&Gb^xhb7Mdjp+XQms+w?`BG)?GMuv-Z^#&SamRxp!x`sZCS%;kkA1sOhFnCSqGQsu(Pk6mN;~ zHOk3zU#xC3XAO^Orm=fMR&L@Iv+b(xIi^W1y`2q~H;UH$Hd(JJ>?XChSi$c4k+MnG zuW%~2#>xvMZgibCEltVyu;~4&Hy(oRD~r8WoYUMO^Zen{cL^u+>y=Mmn4>rM{yetN zFT@SArRHPoK)nPHR|XbL99nqZ%V#C!cWj)OkKp-Y$m0EP=tg zUqgfLPO=Ko@U5LR#bwH+54K$sLN@mF_NvBYdgvOznYQh+0*ln;<36S?mWGag&Sf(t z51;rF$(-VK`<&UAJL2=FeA#l?U-(dsuidMVS8NMswC?4vwGlSv{i*X>^}3eU`ZLDP zmd6g1i!rNjWebd0eB|1!nGR95Q5~0}x~i|tao($WWxnTHYZkdekH4y4u9%vh3Hr>x zbnB5PZ>`H$O!C~VadhbmujIUy^F03=hGZ|b@G;IdIQdz8j?as|&t|Wx%g&ANvi!@u z-1WfRu2pZ;%hw;hQyZtT`uy%4H`;oOcr30SoGf|#_?n#uRJLCU@hdNnJ*-gOgvrU9wA+HB@S`d*Eaxb>nXhFYhf$nXyQ3Zezl7h=4 zP3c?u`;e>MH~9(EH}UGI|I&-unD_S6&)~N`H#LqlY+n3s$*k+%3nG8FC@j6<857vg zHPc|}JpU}QMFOYJL^<`^n#gs$niFz+$(^ZHMl*LBuXOB?^I14eaoIM{;JZOjmRR^4 z$vz=7BT=}GU1QFf&B;F7)tgtOiLz~4#g&!q?p%{`M)#xJ+wCH{EB|>42u%=4Ua2~5 zndw3l=ton9IjFzaw%+-0{aU&SdWmZYx*MX&nJ zR6qAZ;;h#7Gnds52(|rY^Yi_n!qj`!Q)ly|85?K$q$S$lIzHoM^~|j$Gp`<*VZo`E z_+#^dmDl%`biGo{nWgA>RxY?&<%P~PIudMi<})py zckR*VTb8SqSx$FIeKkkKa;B@r%o!`czf)I9lqjgPnj__6dT_((h{~>MDQeeUzY4Ye zmOfW*pM7kLw4rmogBnkLrA49y+Z+QPi$sZc^-wmmA6N)MwK1ymF#AbhQimIaL4rtB zB4mFcCS~xT1tf?>NfaCc@sKExD+&&Q9fL|iU5G>>90w9a@IcN%bt;nMpqrea)Wds+ hek!v#G6(&)f4(uhEApe9F9QPugQu&X%Q~loCIA@%CAt6r diff --git a/doc/qtdesignstudio/images/qtquick-library-context-menu.webp b/doc/qtdesignstudio/images/qtquick-library-context-menu.webp new file mode 100644 index 0000000000000000000000000000000000000000..32fce38c936657ed25be26570170eb947252a240 GIT binary patch literal 12268 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/qtquick-transition-editor-view.png b/doc/qtdesignstudio/images/qtquick-transition-editor-view.png deleted file mode 100644 index 3d747783af568d784e0f0adf0f3e32b6b34432ab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6750 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 Ys~N61r_8v-z`(%Z>FVdQ&MBb@0M8qVNdN!< diff --git a/doc/qtdesignstudio/images/qtquick-transition-editor-view.webp b/doc/qtdesignstudio/images/qtquick-transition-editor-view.webp new file mode 100644 index 0000000000000000000000000000000000000000..b409b5d57fdf1a44a67dda29e9d651547175c2b6 GIT binary patch literal 4792 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/studio-3d-editor-axis-helper.webp b/doc/qtdesignstudio/images/studio-3d-editor-axis-helper.webp index d0a13907e5e713d208dd2887269e22641813cc10..c1388d72ed04a67b7d23e6a834cf1dc9a4eceb6b 100644 GIT binary patch literal 19966 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 diff --git a/doc/qtdesignstudio/images/studio-3d-editor-move.webp b/doc/qtdesignstudio/images/studio-3d-editor-move.webp index d2ff9b9aac5258125b4334dd01a6c2ae6c90b104..ac40c5cf1a01d7aa5890b9fa105e8a450474f03c 100644 GIT binary patch literal 21094 zcmWIYbaRUfVqge&bqWXzuuupJVqm!NiE%EYRvq&qM!UH#^L$olC@)^LadyE5;m-8D z;;T3JUpnmmZ|9^ZS}Fg}--ujL|MFpo9R;}RXmi+y{Yx1`L_$S`@ z@nqp6H@T!Kzdtk0`+9Qf+dun$|LngUKl_>0>%H~`M#txc?>YJN|84hQ`XT%8)H>L& z{a^bs`kDB*=5ONnl>cmhE5B?0tN$-*8|>HCpL=cdh5KLjfBnn5Z_fWC|JVO+e|E0# z{6+sa{jYu%{POOKfAu#OH`T9tck^emZT$1^mk(Iq{8tyy_rjL(U-duvf7{omKly+7 z%jRG61NhJSXIFoC!TIO+AMwBMcf4P5|HuCyRg8a||LOm?FXX@e{>Hf*`}fqHp1-~R z|H-Gd+!H^kT`puecX85EqZN+w>#y5ItW{f>65oZD3_C5a*Cic0^<2WIFfpgX+=p)) z|6Z1Q9BjX^xLV@&@E7Ov7kPL_o!_($L-6XjU^8yOxG$(b#T~i%urVGsJ(&hxlPKm z6G~#uo-Z3Nyb7`SFOmAH_EFHhC6+r6KGOJk@Y>lrkJhuz>#{{FRXZjtexH3JpEc;5 zo#@miPw&Od*W_AS(>@yQcNP*=ZEJj%youw}tCL-#FN|E({6DInoh4b@$U8;qWZi4d zgu^A8nWz4W9b2o=R$9L+zUyVYmRLxWPr|F46^qMvZs9vURjSE)?uPA(sgYYGd|B^( zc`&!9YVn!p!Fqe{*H=m8F`xY^HS6rrjazcLV%KllbwD}y*)c!SiLp*rhu3`j#+jhBJnVXOX$A4_FqBvwyrd3)vv!|LnJHwrp}(igF%u*AHx>g7)_u&{KQ z<8|^6OEhPl*pbE6C$48rWo12ga!TI&&7IcIp9%8>=Q4;SY@ZhVewo`swFiHD5B`?! z4l`$elgEEjcUz?RoR3pi$RF1DS}nOa-QD$WinqFaxUaYP;awfG87}EekZIwp^WDGQ zq2XiV!JCt2T$AmsOJe-j8RS*eWxVK2frnqhrH3jXQ@@rs?qCGL2`hGPJa||7iC=Q= z@iUSh@3|+iMP%oioqs47<9yCXe9E(xDU*IloUk$V=x=Md!+;8JcOI(mcRns1Vt8bs zt$d%JF^`(j1J{4Com!{;@Z_C>+26wxB{{_% z`1YIVZx7fkBc?OGj_Xapf{iH$mmX5z(e?idAJe1&;i|hzJAc2^-((OHDR*-3T08%# zk9*!Fe_nmJdB1bp1+Tv$sRFN@zQib{df$GYIr-?>V_c7yZhi0K<|Eu<^Ia6ZR ziFDm1AGj{$K2833tC_1;onU6t8SXS zr`VY6^^5N>x}sz^XXZ|S=?_xsYo%{}Tatf8ZPp`k|D)embr?MIbnd@e=z6_7gG=L8 zz467RFAPe$mfcwMtw%jp@y?p}+THm))?NG$)Yc_0Iqt%mb7#s6MZP4@SP^e|op` z$tw}p%nZ)Xu+Z??HEHD^*A4Rzw(U_W5piujC0AhgYFk|Dj;o~{yL)?=onQZQ!lkCC zD$C_^S3XP@$`N9*|NgOFq#M%=DE%ZvAe< zi3vUZEScWh=5T3GK2-ZYaKHYEg3~Mhi+od5*^)au{FH*N)w)X3Mty^ zSYNn*N1eefLnQf8pv1(OdkUY=&1F~f{8%>eM40(@fs@87|0oq$RUP`L*L8t2H$Z-! z(`qHr2?ogu^HPl}N1TT@J;8PWp}$*gN%qQ?J! z#*99}TnVX3j&e_?{Cj*R=l3a-ce;~X=F9*1>#xXT{@d3qWZwfn!Q1Z~SO4>pHU1X+ z;?5}_J*RCIhg;O&o}Op_-*tT$!<^b1;j4e2{9=4?$G;Gr2VJ)rHQ#7VH+ZvB-dW{e z;)%^H2S0AOtS6oQs`|h;hgY>L%hIcV%SF#l%=;AS?)Z!OMXQ{k*5cCT|1b3?SE}01*z<3B zZ>O!~$%fNM+Vf;iY&-h2aed0uP7&$OfRnu!l~3)9Yl$#avDM1zj`i8oE_rF^<8#5@ zmud?C>Gr)(c=9f*;+^20TOJeVDT>!c^8de4X5eL1%1@I`kfGdT8dQ{P2XRdr;q!2B`^KB{zE4ZqWW9syE@6+k{7j-)*?Pd!Ju8 zy<+mtL&qeUYu0B{Q zrztZmvRl-9BRujK&qt-K@8ugUE@&z&HvG7$vT{eu%Qf#)%3Un0?ft&U?6;los=2HC z?B0z=RxdNwoIQCZ(ENy`YUc&#sAuX@Y3tohwR(QdU3=uo!@EUmq%OyIN+~V55Nsat z&ht|JmGX-$ho@g}<+$S0>naq%Y;yY4i#E=tZxa3!!~1HrO>>^+$xIBoWxs51%(O=` z7gCt*zv+Mad)egg`liBe2}Y5lw^nA?#C%ciO0v7SDMV`O$(@V7=jiQQJZ;aXDuJMc z@Z1^yS8|zNE6q3&94N3v>PGDS{RjRDK6rFDaFHt)-h+`Q4A z_m%bMo=3CWcJSZR+_W?>Yz1?0IfwDD6CXJYxB{FuCC|$aFqkL4=16;<&HN;jXK|ML z^BdTV9dlGaOmw-Z+jh-4zspYl^GV&9AOBw_tTd2o-L^8&GWN2<8os~h?c8qe34XXb zdEdh;R{KOO&P?YPo$+%+$eGfI&7PZj*D(j|ei)E(F8{3ivBcgJ4qq4T3V7F!j=DHEjGRS7^HZ4^%WR(#f9zC+Cq&YYR~>40(jfBPr8y3+Eu^UGKjLa+Pyob!9wUYGr8cS)zmZjb3A z{3_z&bGBWXD*1UGL+i2lm_^%nU#^g_4*BwvImuD|>BctY8P6wdX})K4PsZdm$%C2i8tu#X&HCUC1q zoGW^^@b&Z9(V*2S_7lYL#PyTqd%t#_B^qaBz@|b4PLW9hPk{=~vlIrga z>%yf^zJC(MGly-X*Gc6BzZuiB+#~w~${zL72MWJYx_TSSRGxe=PMT*_IzrX(+@0`E+ zNu`{S$F24GTW&Xmao65-;4WRs5~<&&r8$3}VQ@gJ>Xxdtr%$j7z6$lezCq^8s|t(A zg41QmIVKy}BbmEn9T(kuIO#b1oabo=HL`1t?|F5mS5f$>Wa_i78%>vdGqw#z!# znb(~OOh-mFmiX?oZ#5VH%df4q zJ-I8OZ_z!bGdGIF82|5lD)&!Iip#w!^}of$c{3S z++x=^E(m?0IE|y;cUJz@gBMztvIUmp?^$qfDNoV@j(LJEa!Ug&qBomoamC*FzauC^ zA!N?s)xUJko@abvzEG<&=WF}URuN;_g-b~_UP>Pww?p5Q~9$?hNP#0BeoV}G3vW|{Y``&!A1OMh&v`qYX{k8JT!*m>Pk zzij`#m0d@l9+}<7QJ^F|ztM8_yiHPZY;U}NG}#=Qo9lO_eJgXn`ai87$*Uz+-2L@G z!S}?oS{3nQKGG+7J{0|vN>bNvshQgJJEiG-u$}jmoc{~gFn~eE)avBv4^HyD+Ian< zYIUirokCyT><4qEeVpxSTJ^%utVBgh&|2ToAVaNs+MMKo7lOV!_rCG^WzOC>b4{k2 zSb>|7Ze#gz<6V1?mbrHy*dHFmQy^r%_Q&k=2@}>nPH3I0YUlX-@XM?;+oX9L4QI>d zK7A}YJ>uuu)voc^Uk5CA*ZHtw_tXT(XAfT`H^^?=E14X&(mJSkm!{D^^3TmpHf1W4;Gi| zy^ZBAx)8K>;kQ3?{3{o!=V8;UnR$zLb=)XOD9yCUeG%I##45O6}1H`-_k5VzzrsI9Z>#CDtiYwj|(m za7V;l)p;=@>PHiFPNkj_-DSu$r}^G>zgK;lk{1rnS8icoDZ9kyzxw;G-t8r87uO{z z>|87%%-QF3?C7*J{Cifu74Dfh(d3-w9=H5;$BPrbOIo>GZP@)xtI+IB$R+topQSG+ zmA1{jn#e8m%F5x8qlD~-Me9{2ceU8sAH2}9<`8Se`*ROo#_cb)8G9YOIE9)i(_T+~Ew z=lQiJmCRb>tMvWN<__6IGp~I(!1DIXL5;6W(Sg0P6ZTuAhOTGkImBsXz3paJOykL2 zMLsDjLwzg-jKDkfD`~C|X)uO6nvpd#YI`go1+ur9b znzz|Lch|o$U-l%>%9meTiQ%^u@1CyxGuPaE)4jM!ZijlH4yUQwqrboJefuAN>{FLY ziQLrXlh-c&EBy3g+Ygn4NB7HjEVo837 zZ}%t8;9K#S?d!L{@24(V_rp=ElP#(1+4Ctk&6i{_YW^#CyY)WwkL;62WkL*2`+RP& zKU{c&&rc_p%T2#Ky6nfa=XO`O-%*_N`psO2Tbemt0r%IwUGq04G~2Yw?O7_EUeuw}195lQ|w+`)BUzTe#cdXy&`U z=8>!G(~J-N`|<2@`uQXC^fz9(e>?d?$j*pu84deomT$jf)OM}(a^n0?W|N<&Kc7cb!~BxG-r6I>cVNJZ|3Iy6JGh=L83~+`}f_wS-t;@ z{;BE9^U3EtI{j|r^)DR8IbVLxI6YTv)=lL-yViTMd8$<7~{y_aucuba8$c)Y%IQMToKFWomQT>aC6j4jfU%_@kC z^^;9olW^~C!Hu&qXDz*>gcT0_kgex9U+1={W1_Rbrp5Lb>%RE>@G(q#w&7Ceg%vyb z&#@(Lj(@oFBg468H|uU_@;v^nHT#c7v(nZnX6~0`;{$pB9Nrx}$Cm&2&brVIi$X4? z`y4quA(i90@RHr!1rn48i^HF|5kf{FZ-vLpSb0H`bWzw-oSfXZ_L)5bY$98i8&8^g)W?)ZQ*Sm zQJ13q-SlPV+T3cLdHMARD-JANCbvf<(?a@9+WHHR-QVX(?)dq5_sX|rH)9{1ketd< z!uZ@ZoZ-cnC3hFwoT*s&;?=9R%k?vqwld{)dvJU&trEW1wEo+qOS^3^|2vqMbJ5lD z&g0Tm{hu_Rm5JB$>&@W3efGmNbNRRX*H3<1+!ngNXJuQgQjW68rpM2(dVUl>Z{>JI zbmqlbQhOilIxPNaS$mwLjM%iFUf=D~cg?8d5i*$(H|H|*ikGQtRQLJ{St9f6h9?P3=J1id8PMa|2U zRV4W2SGd->{#&`{&nKA==5uvZkNhlkt-c*y?=Ve!`n5tnzjBM*jc55j>~DL2FJ^t+ zH@oM5D!+f1I{Eufl7gqGYrlVRx!KXVy4od&9{4i*zUp0euYH{{hdbBd&jl9_MejN^ zS@!#fyxPk#2a>gl%~zy-J9B=fY3;u1^xe0k&(^Mb-JLplP14Wic{{cydi^U9ivGHJ zW6n8|J--Cj{M+I*X{o~O&EEU^E|kjEPB3+SY3O9j=j^k3tN7uT1uv^ZCmM9JEKZcN ziT7HS_%!JC|Jb@0Pt0yqO`f%N@2=zx$HQ%k)$_8mw(?vyPCsyW`a=J$+f7;vQbO{4 z@4WNgXy?Z4C--g`Pkq6X>UHnYvjycW z?dj{+%3lBda4PS7zsyj%>l(|0v)JZJ>wJv~&8jPsHo4!hFZ+Z#U)j6=+h*CUXuf)m zeJkItkhYUil062tM_$Q^+Xpp;Mr@hCz0g;}(Cp;d74?ONzc-xLcUiFOuETZ*ubZZ4 z^6fwQ+9(|6=%_Vu-Lv25!~8uzk2$XWy6a!0x z%g!8aX+O02=O$b0yT1>p{k~+*XXnppSO59W);DVtelPqg>2a`!>!m{HcB6wHbm(jg^RhnzT`R*m6oh)8A2R`;tRL)qL&wXRkPwCEr{>7-I7Ag7mJ7*Z7-Lve&2h?${Er z%RAgyOK*$s-#fl{mpNZbU8N@F`sa<$qgRDnO}=iopIWm`8QidDVEp(~j;lKSWg_d< zgMSu%oqnc>b4|5!L!hbg)CV{27o_}NZnbC1=M^r;-sw1QRSt^WAR_YApiS)y!~Bjs zOYdm^z&Qb8zc&3kWZ1ePLj0xw197+B=FOK+>dF5!jjB#5YOO4p-Tm^x+Wq~P|2+P5{!i`8>ovhl6_Y$yGu|r9i{QE|>2gq9 zR-*D*^yK@&B4v9H=5Q~z$xHP6@xWxi_Ms4%*8P-u#`XALjMaT05^po#RiN*37AW1)?8M z`s`}iP;*cGs*OXV-^}};{>-pFTw5zNwfyPoqf9!JKULqG?tYcms#e{rtf+v2t(i%K zqd>>^>Bl`X7CJ8vgh>Uj5s=F}tX#G4+>Pu>m35CEMa{hH@3g{Z1_J{Fi>|uKZMoV= zo66~$bCkk1#)`G>>5S;CIjUVYx1qqTWzpYX@}E})@x|`>`(rD^q*G#+Z@UG=vMTlm zOm$uLuIyW$|ijQ%f~0UmZ_{>GS$<5@l@Tlm#5#EF~8~XA{*IL*B`_# z?$&u;9wW2y(xO6L&cCnbd#}#5@=rWd&!lj%obO^IL&~O`X0|2Kj~o|Q<)1ocXclXJ zV8;C2Y7fr)Zt2LnZYlE9chdom*VBJIPPzY)Z*q8QkJGxajE~T;*e%{->W5(a2xkr*1b;2Nh$fUbKEu5 zgr@vAnGkWtrM2{esOx8r_i0o7^?x&)U#axHA|&8=j;Y{ezKQvt_>)Ine}9{MHM6^J z;l_w-QmLQ&K6cA>n4}%O|I$M2;RKhD|L2PIon4jLmdj$ZepbubR`y<8p6I$>KW=QFPC zEyX^Yeyr~7zj#Mq--UIt#*rUae3-RN0l+}#zEvQwtZKI_7P z!<(bGy!iT^MW!P{_MRbgY08Xx{hf}|ijN*t3%0&8y_OKy=sRKWwbEcsd!C!&VjtFS zh%*c_XcRD%e^U(BjPwPMOG|UU?`FS>5(X?aQ zQvNRAgZ&kip@z0`&Hfjc%FlhMc&_g8O8+MANox=9IQMk@ZSPwTZ8z8+eRgb_&`+@% z6X%#Yi&oq|5v%!f(gyP`4Y`t>8v*VgV}6wWEPD4vdCJGSqx@VPyzWyHPu&0YRck}u zlS2-h1?}99CEwzkEZ}mYNV2KPF-~LFAGRaC45?B#_dV`g8|=h$nXgpWb?@bZ;A5(l z1?|7)KRqKrcS((J?XuQta^Ih@5`K73+&2_4Nj%c3fhw7^he@6 z6XWCp=`EJCW_rDQ^Vj}X+lpT~A?zpPXU_EyP>pY%9=714b;|?EWoGQXpPyb#Y+302 zXwtPOE(<5=YaDs0`F7GF?zIxKZ`zd~{pVwK47q;fk^he-4dsy167f1tx0w5HPVd^% z^8fRJMFIbP6U-l5#NIkoyVPW2*^b~Bt16mozA9V(Klh^RV9SlO1yZNg*5#1 zpdc@$uvw!0yvSvVll||$Px47pzW=7CnzLvEw~%uF(uTSvv3eZmR`m#Z_o~`1@`yR* z95zL4?Ex)mh94@sY(h<2KSn=}n{V&TXT6cT@Ca|l70>Lc9!LDXy#HtG#BDTj3;%+7 z*UmXRotsv5jd>myIGL;wt?A1Z_}T@ zemr^+zq8|1utV*;1vbGCCnZ{j+rGOR->3dzV(6VmsKeh0R%DtZU zSFuO$|9gBcJv7ojE}EJ9s{YnE!APm!PkfseU0#%OmWy4vcaqL2gCs?zoZXK&Kkr(S zd#i4ZWl-V2ROS9Ho{T$=bL?^CzLs&~idwQ%Lx-&umu`sc`tp5QmK#NOB#Lyu;Nsn` zkalnR8`o5)*!uPIGVg8enpE1vdJ_4_iJ}`F>#ve0(4N=!TG;2*;(kGAf4U+eMfAE%0%igrkVZrSatk? z%es8O<8vq3W(s`E7uuv-@wU8|wKLD@Mo@P4-VANI=_*-UFKO*^FUhTCR)3t=z*=Ci z(9lQryx=>&DD!38nl`@CIHr+&>=hfwa_;5`#!ijP0&9}zfBEK6@UQ-j?C!KDhd)0% z6W$`v!EDEPP3`s}Cl|9@lR_uNUr?VBz3)l-pXm9Etdn&PI^A>l&;Ka&+%~brY(gDz z2iM9ug@pWMZF0P8-YGmIHrPAD?73%JBj0cB-5=+zK3i)M`^56mS|Qi7n}ok5TWK1a z-JI`hasSl0)qW-~qaJmfW3AWTqIqkFhRw!5JJmMF9o?Mp-_NGXoxyLL*6pijZJ$qE z|M;4y083&6iveGM!T0zEYrC7=a*jNQX3cBQsW^F)*?WD_{PNe^|6R%d6k~77e8%;_ z4*_Z2c-vc6JyQ~uqQcv!&1mjbayyX`vs3Th?~ey3UAw9&J>SFjxeKpfbpM~g2k-yR zGux6lyXf3pDM@LyMLKJ_A8-A;_~lX`O(RQdg`0jh$_vcqO*p%#<>|W1O}uUbQ(kNi zl_`o0Hf{a+iB0<7e`SkBjgpavoA#CoqBeOeV!RW{cLl zotYomB~4{CPS+f75HPf~T(u!N$*wY{&DC|o-9?ppj$2uozYLvjQZ~z==e2nZ(qk%&DU&Cw%+|T-SBQ^oiKGbLPXpCV7X?%lUt4mt=Z=Vunlmy6yYJ?h97Ax-GqSZQJ@& z47Sa>>4{tGmt=TPTv7hu%Apj|ABSRN4t|@jxzhAKnBt?KAP>Hhqa``Bkp5qqw-{kF&6 zq!v-e)2(;+L~;p-@86cZYRQ}QbDLDOWF^eaRiqiGxUVjq7H**!vhj!Z(YVWyy6|3*pZId3X&>K`I?di&b7r|7;ef_?U{dQ@fZG@xUkgCPNgfQ6pH5b9bnRLY?yLk$rrxkj2tUU z?B5wpoV5852NUae_f6%_=J&q$-DP9Fb#Y|L&b97?st$mgR`g$G=8K!AqxAD+oQb$V`mW@P2Ag7MjgfIOl7e z^42W%X>oSTOMbl#lsWw1g`Yl&u!-^vjTxF>aw=%ct_P?HA zb!YFYj(Z!TvMc8lJFR*DMcI9!>x_iZNqUak7HEFF;$nJZ(x%zzi3~w=qJkabc}z~8 zJatm(G4tY^zWvK*+Bb@r79Lr`^q#}LA^2}xVz8m%V}UuncV0gCvG}9rQtUb>g1O`8 zuNPZS{AshRlMtO!ruV#=E4eSQ)hleh?TqSwf_=(U8VXh|J5}+yGLG>+@9OFPOlEpL z0*|a5auX6asXqAo>4jvw-9xt61<_ZkBY);>cF{{#<+zkJ&%~Wg+-#d=zSg2=ULTi4 ziClCzo)%VRmbLFE@1DHV>lb|LeEwBQ_eqJjlf;{&nW}d!Z?Lf($oak^&++~P9X;Vz zt$vlKnPgvGOr`>Ic{RdBwO2zuu`B^VsCn z(wGLzr84T(y^hW=ccphQa;?3sKY_ib%q7CIea2RaU<2!aYt({UtN(N79kC44d*o~z zoU~g~zfU*tWp~TQt2c%2?cKQ3C7%2B=Y70IuBtyuzdXLX=gSSz$b6aAg?T&v`!n-) z@EGiW>bU4sxm(Cp!&#ARtgrtD#;#o~Tyc5#x@{Q|J`erEmPv45`n77)_zz9HA))CaqEIhqwjwm$sDmc6gm zWOMT79Y=$cj;k!pyta1Pa_8rcMf;dj)`l~cXB*FxUcesy=Au!xz>8(CUVXm2ZE8T} z5mO_l=PCPIO1Sv%Mb=LKQ+x94r#<@@XkJ~pPF6zn{*BOsLWw`thdtglU1(o=FvI70 z#!uaj6?EIbnsFfe?VgBv8!idYx&PVvTaJ&e)QK*UIWV~bMkN)&+tLBRJ{}!gcceuP7%x z)7qhGmWF1-CJpu93NP>N{(WZO`mcU|8UC_gUZrQSoxQ4Qy~q~}riI`Z7EbHi@>O0-XWo3fevfWB2j5q{2hOcMzB`#NZ9BU&XcJeM z$A?FIvvXq~IJ!hCx9GijBGHo(>>ZWR=Ua7VJ;UqCFMipSinJY^vB6)9`IS^8`y@7& zNoEmQK7u(0SFFu$F1|TsMGr^Wtz&^vzNRWM`>*U+xoN80g)9d@4@-;v7PDR^sedZ8 ztvmDl(+>4?!EGvxH~SYy50-2J!wdy)2k zzk+hKyr+5}p89#R-2e029Q)JPwg0yNl%4%*^4|MjcD`%_a+>q=EnROb7al8XU}^n1@v>5m>fwd*GvB`6d93o#p@^kxcz=1Hc)WSO$sfka zAI`pPTe-IEvOr}?#@^_OX7gq>t=Y8cLS(}Ao^mTUo>LRI zFMO57EuV8}k>bY;9}#9b#Us|sWfO#T_KNJ`FwCw@+qccEF}v3fJW~k=b@? zVm^1aaD2={cd>WjQ#g)2`l7f0Skz%>wXn}SJY3ikRJGf!_Lx61w(vT*NPOls=e7TC zK4dB2+QQ->w)A3xip}i>4Exs@Ie1E+vNp5W)+*gLg{%3UZ*8NA>EujP#a~WlvsaZ` zA7$=+ExApatL@qa_rh-~S9nr)=8J~?e{GX}E1-vQZpy~pSueQuU3DtxNc-`FyWW$Kka+6*z8zFVTGdCPG&OM@AfWd$-VGcy!m+TmQx#_ zsRkq}^zPqj&Uq@jwLC{K>VtvY9RFmg8>f~h*H-u1T$BEA;5J`SpNP*L%R?!CH4W@x zQ(u1lvoP)S9LMEvn2#{HnEW_udazjF^$jWWI*Sv${T`AWdty~%CWJ=uM#imtwN#3+ zhpqIaHus{xOtY0g%<`zQW#6(th%ez%)X{q$*V6c|G>cZdiZ(56-F#PT`{l%g8+6Xv z?KD=l7Uwy??tWm7(XIKf7CgRVk`(aPXGfcz#T5ajoOPTFtu{Q5>*A42doV$0)4oLi zMf2(uCj75^zaake8Ns-nXZNKlPPlk<=k+H)lB3J-OU8$M(E43we9!AnoacFNAvI06 zTm4*(@db>N=7?TayzFRDXqCWF_TlyUynqYBi&j?K=A2e*b&PU7<;OUu-+6QU9mCKU z$|u9_o#~6^mjAT=$FnD%qTveBE;qr~sdt~32_s@lEHS45#zMb<zuSw{Ki&QEJv*y(M(?dH5Mwm5$JgWtZq6iv&YY1965 z{X4GmvUlCe4BzGb+Z%RWd^+_;)F#RNvV>fXRo-TG@{w%~xk+Af#@ClsEnC5`x_O39 zjYmqL{MN$h*W8j`PSU?JFTyodhIjX$!0Gj+^n%U+K)5v3B)@)LahFYbcZmi5zY&?A-#4@H zMpZN)QwGC}T-7NC2Zh57w#(}~1lp#|^FLt1dv}g(#Vgb4zJj{~#OARS-DTg#wSZG4 z#LNAN?D0i~H}?7Nd&MBLQ?qN?e|N42I|bS%N3U9RIaw@M;=;qY8EhUI$}5*YfAqif zy4dlc5{0jwRm^jlEVS;c{CnDVwA(1c;CTML=T%+4AGgoTHE=JMTrPU;%1u#+<2U4P zJe&SIAx|Rb@SFQ=3)7v;-6nisJ))_`)BDxRy2xt%qt1D+oRR8r17v#oqdR5rr*Bxb<$$Iw-&UnnQs2`e4=fubaDRjH|M>k#uOtnwcGdp`5(r;6pXo<57=5dK$P;ygX4>D|ON+w|R+KexTm`E`@da-(VQofl0h zsk|zAw>AFrK|!y&8zmvnrGh4@?_lSecOo)Da;55*qCcm&g?I5gIw<_%*)!|2 zPTW87+?r1>3!Z727>6V?KDeE|fAWE>IWoBuE03nGTcq>ezvQ9yDwBCNOyYYM9r)rX z9rvwCzWcF!wWz*`U**0V3v#S$wI{B(y;P`GvS+LnComn}S1%QSYmq<8VJ z`cam-W15NQ-zPp2E5E&%9$EavK2_(K;i?_e!}k2p$=q|=a_V+F`oSr z)#mk2^4a}&cic_RymNW5@;>8)N$0m_smWhIdP(H?w9VC8tpUng+*;RYBt`^sxBX`C zJm;!3jaQJv__SR7`@@f9wsHhtxF+;>_Fd5>$yeW&seSJL(%0}xHiN4tj@#w4S%LqI z7i!xXvXt*JewkV?cdt?MmBfq}R>GUUZY*CLEYh&+kjn~@XDz?q9KQKYZkgwbr2n=m zrGeoc_Vf45{%^Xw(Kh0`V*tmxgBt})UrlvYoVD0%+L=$mJ)ye$t`(fSStl}C%ulKI zuF1vKynm9!8XvXD2m4m6wmFn{UawDS<)L-9+&k^1cnh}GKlA($QR1xga@EE;@@=Pl zSJ)d&JbK~!$@$XK?8aHuUykxla?(@b2)S(T!{+zbe_!W{dk+>|^X9%__JGTaTo9Fy8+*x9%ACs;FxZBIQN))iSPBg5T<38H9>Qm?V@}8V{|JlgOWZj4~(?k z{^ZKs#k!9VZHcP9YI$qH(Ob{X3w@qiy`#)Sm1Xa~B-yx%PI8mLSXG)HcbBQ)+&2fBli4^~g4D^V&pab-gx=$2uxftAxG= zKGlE~*9+6U zukLpSM|G$4%Gh1`mhCoqK^u?XJHgF2+h2G+totb8vr4j<`INP~*S2aEgZ!<`Jb!}z zxj(d(Vfb^!+RcfVQ&cp$V{OX1LlsF*+8tIizPN4syz$z3H@}C>j0@*EeqVb?>vW8F z@o~|xqskY9UAEp7`lXcQP_inwzK>-YpPR^v3)almD}7FeT^3xUwB2U)<^@}vOYWZE zf4(SV`og)lUvoTA^W0)4D>++AZX3U=+UsM>8d{$op2b{YRTDLRA?JPVnhcM|eVgQ1 z9<@#m3cP7M)5Yp_*)re1b6ht{%2$8AcSq;x!sqkmsY}^s6!jU4yQpi&^&VNScI?+b z{pzRUZ^YNGJ$>hl?625)()BN$pP60ht(cU&V#f9*i&nhh>a+GeWB2W15ZBRXlXBE% zAJ7w6cQg5%?cs$BUnXDb|J;7%zFVH^f1AdA;z4X(AqRV9ie7Dg;*<6JywY;__sWmu zrFU(d+*-qv&Qbi|R_C*F)OxQ^qIWkI<}qfoDZJDDrF(JNab2GJoNLFVGOv^ss6{qw zE{ds1%dS{pcj)o$jdx=9PugIA^NL1vPJyU>LDwRus?+Bir2kGXH{elpFu5MgKIMgX z_H4PL{Y4ji-jvKz;SV_5@;CI9>Cs6}zZcI;|55MxlePYJ`kkb+CM*HlGXI-Ao%!_b z*}7BH_bpuY_g>8WpzG5rubLSPK3BNMx3K#EGNIVJ-CR5VZ_B-XZ2$4CPaJZ-yEjS4 z+O3-Krt7UgtKw^`b==>--I!hdwm&H9kIx0YNg)gmR<}unOk=socBVT*tUfN`@OLF1 z$5V#exlTxMuAkfPb;x_6x8_=*0=7_dy+uKXjMuO$pHvNAe0YV6{drsF7gzW8J&b8* z66=2 zK`dq&Z|OA68)AtZU)5Yxo-$Y66u;Kmr{-kNFZFPq@Y9ErKfjw@JfZF33>nT`&Yk}_ zW6qr8pCGp4e3S|sZ|>KL+GqA;$m`kjoxG>${`Q5o=W@Asr*w7MuOEB=tgXA=e*LTP z#^RfLAv#*CkJ(JPbVYebf1mN~ixOVc(62hpuhs;^)D z-PFF3J-a+-@wS+#+4GxY0#{b()hk!;T_db-m;W+gMQU{LNB^mO5dSKkGVz&@`mfL{Z`wWm(QNy*npjE%Wbu zV!Ur%TwAg|+W6anXr7yePwHQ0^nS>`w)HXP@3stW`{%_&8s(B71sE z{F>~`m33YjNxH$4@1NaUuCV$KYqJw~!pH8j>vo@6@h&5A|Cc~VzCM$cx4h**=#uLiEvYq2>uybcfU+=qg=kw*47d>|1n`O?_yk%k2 zfx;rIlO5^1ld8XcTbcAAftShW#>QDu=hi5<>K(IBBolH!cusEdT-?OAIDlRES#tj8vtcsA^UqC=;@A

-L75<$o;y^z^*w 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 literal 30488 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

`!oB|sqE8KB^92kHKd$=vaGu9$Gy76 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 XSPzRcn9UcElp4+`z`({30OA7x6ZKm^ diff --git a/doc/qtdesignstudio/images/studio-3d-editor-rotate.webp b/doc/qtdesignstudio/images/studio-3d-editor-rotate.webp index 7cd8fe1bf6a628af193539c6a3cad8154c97c79f..368a2d57fd5ba7417b2b47f19c1c9ec201c9b6be 100644 GIT binary patch literal 22998 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 KU|?ei0Pz7-s)(Ba diff --git a/doc/qtdesignstudio/images/studio-3d-editor-scale.webp b/doc/qtdesignstudio/images/studio-3d-editor-scale.webp index be0270d34b0191c0503549a4845419be3d3190bd..6523539449ae9b9372c47418a3949b321e157542 100644 GIT binary patch literal 21210 zcmWIYbaT5D#J~{l>J$(bV4-j1e z>~rsLv$HZ`{#W_W`rq{}`G4wv|7HGn_`m!O@2~IIslS)s)c#KV|EJ1$rzchC)>Q9c z<=;@dM5cKocTJIb!`Xu3%*3%>D6t7ZKrKmVZom%GKY z!|z|-l%hoiYZsI)oT#w=&qI;$J-dp$tLil;2nJp4dj4{0#<@gccJivx&S^?!cPz z>8}>-I+9YjLHL7#zQ?`;=1In@HLZ*sm7g9Lo>}}NS}(J*QU8ya*wfVu?wsM-_e|@~ zbM>_61t-49?+jft@#mhjU9}16fBwjYoS2n*$o0^6^XD5+E6jgG-Y)eEKXRGkcV7wu!$HIBH!@`efD?f+j+08$Wz0(;6wVwj}5%fnz3Cpm(N9$Yn|A{NXh4hqMXGWb|)4--}$}yV@Sc~9oMC1_SruW zs4G%FBvX0eSwUaE%bk0jJNJrkSE)VwVn4GgQoOft>I#{k8ebbHZhT%E8kgd&E+6mf zEq-`k$81KH?;B)VIQ7p@(J8urJJQrTb7nzxwZc6%=k^JC*LPL_J8~}JX!D~pzYUlR zMZ`>Y2G*r9@U|nbyv2laG8$xV(49k59*@wS4<_3 z2O}zGxrCh#yt(H9&+1wIxgTv^Y~1fV-F*DS{b|Jgt9u$=G5wVDcAWZ0@67%mA@?#f zFGRMj-FoP)SLlVdy*wC>jwi@Hkg6ko-7(q)cN zpvJOo)lxdqwGB2rQdZB`O#WE=Y7uW%UEY^B&$(h1gCtoOcE0$(SZ~A1`JEY4yCTd^ zN$bh4DB&r!$QI9#|CMre zO6m0zmn)+jH!o7~_ux#PTFZOy!2^A#%-*O?|F%Y_mc6{Fcia;Kx!mmYVf)p-Vn{~c^=*qkcY^KJh=wyPA>N zpXowez7@x7dEpo*ncMO6?2T5u&sNmywSA){Gv)ouT?=Qkm?eeJOv;#g!t`h|x-+t<#_cA@!AvEbfwu|KQiZA)gbiR#NGJNTU0QWjn$ z8#?jN-Obe}izMaR{<5!pGV|etdznwdErV~ga&JDp_;27(VX@qE9_**qvmP{MoO<HNL&{O+TLCj_}tbxKx0uw%X$Z?^3B`u^vu zCaA1jfAtOf|5f5QMVE^_f6f_Q@SD^B`o8Nc3ym6oZg)BKZtDlVm6v{=KeMm&<)0Ik zFNFhKqs;f^v)rE`9>y@E`<22Ao45LkhwW-&-<xCA94$QZfD+V;99Bi?aF*hJNHY~)A*imc6>hnUQ_Wj>A8}%)1$Tg89(SPXI$rh zx;~&Z&+PMQoduopH$vr>eZQ~1;`axqpTBC$n|`Y7`y=Uh_U_96n~&#*{@H)z)y>lu z&pO{*)ZX5k^Y-vsy@YV7gTKzT#a-C>F=_9Wmbhzv20I@ymc;+fd))8Xo6p>_`h+^m z94=SEsVC-DFI^?E{=n?ax$8B}=g1~s;oE-cPEtAl!rcocZa!vHWd7=DhhOJO1WPR0a%QsbG>PWv`ZX2WuEukoq;A++a9(t$?#5># za{rzv?#^@xX1e~LQP$yN|IGa@A3Ma3uaUJ1`C(*i{qE50fBwJcJ=$$_y2e<|^WXjd zGjo$J-Is0pWB-4`t=dz*_qVTAee@%Sw`%$u?)edqr%%k$cqthte!2APp7^+lI|3qj zHFp2#mx^xqXmq8OP3?+% zr@P{HrUuKzw*2b-Dl4WGd^X*BS8MvyZ~BHRKilUoTM~I>;i2g@3vZp@X29^PFO|cp zHXtm=D_roi$vKfjyzUKx{8d?d)(Chn|GK=?Dzi4y;s2u>?{02?ptkGrS>M?wvs|s? zs{XvP|Nnd1)RQqc`TN+Pyyq>sBQ@nwwpWKL)7)Semx6p&l~WQ|%(gq4FTE$RY=_Y8 zJEtaYK6CKQ`HM`~QWx(ty0Ip=tXEBD&SmG6Rf*x7RJP^G|6cPWf7eRBWVbJlx4(8A zIXpk;Sf*-WonY@#WxrbfFYlImRj!-NdaFx*X}_z6BpbJUykJ+?!@E7Ale?@qjdZ^T zaLksLJGC~2<+SvQq}+Kbjt`!?Ff5drN7K2WitR`2!3rag;q*}S;Km#nN-w%+VaTbAx_ z&4(p7PWL=J$YOi@)@Cz?^ajpX37;9HMXH~&Pkui!`h1Ln%J)kmSJzICvO0dL^w#sF z_kA22-}I~3?LPAASa}`);a&sXE&S&9t(K}(ah_XU;<#viw*vpuUuA8T{?QUmU-cg! zdGk9eD(04U?v2Z5r_cJ&oIma3M#-MFB|;5#MAo2aqI5uzkXcL^jT<=C?@pv;@oN4<+t37 zf@k=0wd;t#FneKs?BDctg71w_IEL-2d8%ev{>4+#M%vo6Ev8O$pV5BV#<@Qw10|X6 zonPE5|5fmQU39F<(P?i!OAHpA54dy-Ed^!PXVO}SBynlHy*iyF;4cJ@_v=jRQ*|7$G*uD!W>=3$ZGR4LhI zHxti@RqEEA%ef}MKIYGpTdeM_hDUyFe{j3`{iRrY<{ut0yKFcWs~^hhABk1exG3`9 zE6U!bvi&{lOP`w$DoX2DzAfeR>q%P2rR~4x$}-NKv!fpSTgJR{x%IBcK)+7 zx}P}Mxbk$Rq@JJ%Um?Kl#yyrEF?Fn6zHCM)Lds$Pb z_?s@O>6pFUQH2)Gslw=uFlvj&vU)=%c0zsiX(r%E6cHawFT?=dHYPv*>-By7K3Uo zLsyq>ZR6Ud{uPoW{dClHsj(4QOHcYs6^0}+wDh`G8=6N^HFDu!3^)26z z_Rq^MvRH2Y_@*OfnO7U%-bh2?S!oMB{~LXYe^Zrc+q$TY=XTtw?*EsrWj<)-+@03P zVPhoEue46P^D3v=(FZd{6%%gXeBj96N{|A1ZgeIy zM?dc?m-orb%$EaLIC|wQ%Y42kXftduYwJBDoLRceIRB&kz1bXx&z=-|HaD{4a^j{% zsb$Mu_TP%yZnW30Y_XE+l=Z93JnA<6J$`d;Ug0E-Gn}Hk8mznb`8(}y{;W~i#PZEs zgyA~BR;!quuWb6(STU3P?f19cm(afV{<~!J#3Mdito7q||8@Sj%%p?skNyo!*VW&- z@>y01?z_9q{;|AcLsYWF++);%Z@7iW{n)zsx* z%V&^{d*r=-lHvWEhq^+!Jwvq3(n|Dqi+udlcl=k2?JlbY#h3Y# zZM6LET{e1fR-QpgetVL@ra$?vfuD~rxDa7so z_>9+2*V0aCG_?y4@`|~i@v#5k+V(zs)7d4>)tVhKx0g$~)J$5r#i;)Jfd_i5zS%SM zrZh*ZyeV=`{QUItRli#E7k8swFE9Hmy*gu8$^FHpx#E7azvQi5^zw3D@6|1_R{0lq zZR=j{`^!A+V#&+>Zm(|X&Bdyv(8YSn3v-qL|Mn!4ZBw+bafJQbzrEsPe&Dq890}s8 zr(ej_tyHM3KfmSg`|7yQ1v83@ToT?1Zo0T~d;9g2@Z%*5=iA!Mxi@3xXZc0a(_24X zJ|q-#OQ7fC%SfF`)2~;2SzDBQta_coE&WU4Yi|jD{Jqmeddai~x09xPJP*&LtzyXe zeD$}Sf!pdwQghyR*z-+&v-FsI%r1VtW$C*&Y_?u|gZt_B*w5)Y_SR>5mvtSrRu9y# zjr2CYf5zh6m9syD8(iKtHyxQ<;gOm7`}>_X#*E7S=FTT3`KF6JRGx7)WOwH$hms|? zcK!Q5W76c!%$+K!)0BKYx!1frt&@6{MJoT)qMH`q4jIg@Ykg|swP*L+rR?($pYT|t zFxB@Fwca(Eu8o{G@n*R>eLNB6dik$Y=SlsZ^Ca=$!CRAZ zU;Aw^*D;^5Z3S_z3CjWi9@bk~RrVgehSw7M6A$nSkEDY>o zM`l<1`<)D|TWRoCd-`U#y=>3%Bi=MF@`t=1G3m8%ll>V0Gox%BU!S}h1n_iXo zI;F(Re&6)};5n1j-jbjzU$Qo>`0bh$&JlB_=I1tpXMHI<6?w`(#@#(5Q(4XX+wy?2 z_K(bj2MZS$@Kh|YsDAwGvE##*`CXUio|G4R>veFQjh@PrmovPQ6SsDjv-VG5bh(?VS}nNu`0gvN z>$ghwR=-(bnW18I=65yswx*07Jr8v@PI}vFlJZ*J!>L2U@P1Iok-f|H3XOlieOA&a z6X3%0XN&ao)Nc(*`HFYKeq<(l2q_;_z58@(*u71vFRwrFasGPAODwlnQRK|GV4HXG zKUBhst3}ovdpa;DYdQ)eute@N~- zu)%k(%;v=%zGt34D(YO(o2$6fMaCGi@eI z((X_F<9j1K*_z?i>;>m1?-ZX^|7kO4?ey2ZAM+jwtX_1dl2gL-o&$5@-HF>yI?ASh z*>zNFP15|z@Xx2F3RE0Cr_boR<=>~bLXnHlyei-mjR<9DJ@@8s_U_JU(#tbV7YO7m zJ^sY<@^j8V6TkayuCWo@+UK9=voq&?h&R*nf2-rQCKvyZoV0k?GjFjh$@_KO_iHXs z|DE;!lj@wKS1x&4ScTr7cgd!`>>&Gj=V=?&mOriRD`8%G#h?9{`y-1}#r*Kd0N zAbj%uU0*rX3LIXkeko1VEiGy2*F5)JyGQxlvK1fC*gZP?>i)*9Lc#1j?Q=>MZr|;C zzc)Va<>4pKuKzt(_Il0DNz=OQmV~88Hn3HRNpl9@^wnlB*L)J(xor0-CCi21GN*9p zSKqald2u;=w;TVPqpfT$!biVb?3}gwcd5q9th4h`TaAg7g`ho1K|!E2fwF_*+G<_nGuFS>LB$u(`KN zz@G8s)k!J;etQWQ><{OC=Ip%h$e#$0|B7KtT0RtfTJ!4C`9B(d-+!>bV0A|LtAq9BmtM~~7v*)I_mm z%2$4(_tvr}`~OI#i&k2{F#aFk7L`A%^JGI|%IXx6lGoi&^>Y-nr}QK^9sBp^(tPzv z)(f^9to``>^UW;zuWD+K^4@R0X_S&T!@_(|tW)xN&chQ6eqYr1T~w|+-#KA3oA#Qo z_YztyG)1)+sK(oDoF0DK_VC|`_L(Z%a;Awgt!m>t{8s-*7PH~MQ!`fuH-GzN-elYL z&U)^hD;s+4gpwpzJl)dBV<63Cvp9Bg>I}}(W2`C8{5cbK{@wpt&NK6)OkLIVckd#d zs=e7(ZxyX|_vr6Qxl#W?s9tXVw70i9R$XUWl5xf23Dcc|JJxEU88YaV0B%x&-};g|8$v8eKR_9+M9Q(pk++iM~$*WX*S1<)h^Aw zxo_bXwz1HLdtQ-A~ zPfXn#{h?<2jbkB?Gt5tZue1JeuWDQ6C#L6j?oH+segAHTsGNIt=1$&~QSDD>Y)&y- z<9%sm(%YjiqpnWonpmbD^(=G|+xLUM+Fah>; zkJ~XV|9k(gou48i7vFA@GSn?*eQRxgwQ#cg9QOd_t+!@x`+Z`e*)5mFGg7Q|&m8`r zbAR^t$vb#9>Yq+JUcT_A*{dCV=lF}3TzMFn-sd8CZ0fzN4V+chojdeya4!?Hjay-| zB6?vL+nWtL?a%I7ao^xY#Ch%DnF7tzG;2T4;-2!JOL+A zcF4E=={@e~lfs`hWK-T&?f=4~|G)5t;^D(<)+hA`-d${QW?S!*4RS@DT%Rhd+uZZp zI44_icK+wmJM(XbP~?yOCQ9>OmsTeqO)=cR!Nsw=&!p9KW85}bvuO)v>;L`8x@eQ| z`WyTGymj~9{j(%?70xMI}r2{yct%T|doK`|WKbX``@n_XGLoUicJYeZ1rU zQtt;hK5|Q@8m^r<|MQ|}Z|WbvZK@ESyg~ECuAf^P&hM=e^qTPBf45%C)Vj4D|I1Hu zwj8f``fGz4s#~o@xOod(es(%TMt=n6x}@I zl1a|DeciQDcUWS-DcA0r7gwNDx~NI{^zPfmcjqnLS~D^J{rp2auBt>G-e^#1^Pc(t z`I6UYYmY!w$vqEwAh`&Yp9sK!Nwg`LNqM-LltiSckd3<9_+u(W@n4iHTp%r4PD? zj(kh9XWp{lLU|wS*|@cd^PhN_9K5SP)$Difub$Htp)vbpXE=K`aLvhb$@x+7W>e-9 zwyT!jaciGXJJI+Vj7PKA&6U%!7H{?;bzRBPDs%Jnm3 zW}Z>_v}gT^$?v6-xo@90oU{I3Yhj=R0~jdOXz^Hl4p?zAT{Y?9+^QBEgKufhOLoSb z2=dQ5vG-ur?eX3uYRV9;O3{c*1DzATaEm%H63-(ESn zG^ON5v!l#>&FDj$N_06zJ;a~K_0A3Pkmz3j z4~}X|f96`Ith>cCYg*D#xzdNh|1?kcefNoFyLVL~D&?=wQUGJ86Qvh^nRRJ%tiXvXmxqs( zq-Bb0-Z=ig=xq7Z#(;C;^Wk~|K~ldE37{iPIspm_DybrSjKwJyY_|9Zpq&Pi0L z+o1o-W|O>Dm8myfCf7d8$j&LSXiU8(ZKkWD*KYJ_x+a6KkYZEDH7TyJX z5C}PvvwGRXtDYYFdO7|zd#aWN#kw|ZoTgyy+r;qI+ov__)Cc9oTt;#`>t!c@Trt;v zO?T+Dn{9{8e-}hIEYK{?+jwEY-d_@r|K4s)=$gd&!RE?!6QLcxaz(LAYaT{kEeqZ~ zX>rP(@WTZw+Y57Aa<`UNE6oqxeL{6%%iF&SZn0Mi)V2F#9@-t(ZeJSzq9C>HyR!2s zVZGj6LJy-h7fLrQvj3xyYxL`q-UE3l#w4$|2cs93-kx>rK>BTtmlvBYqMOS%?!0in z<$sylg84`Jug_2W8T@(O|Gk17M$@|P+*WC}=@F>oT$cB<{Mv8zKk*kCTi;(5sZa7T zjkiDRsw*kBebEE?#o^Cp{*T$}qv5Ro^^1$>$^hAobJm&)TEE)x?s4eD7l}@ajU~)6 ze$K(azp1W!DgI{N=AoqliTm6^y zYNmK?J(7Lme8ug<2J8~g8?R{Ee#JaxM9g4ooR-|wro>sE=_dMr- znDxu^e>_&no;|geE&cSOU1zqQ*0tNgx3XFK%nKQl$#?svz1LI-zb4GpVrg0Gl##e| zL4TvL3&Zk0`Ln00LVo}Ft5NhQ|KWj%z_gavUnS;+2Z?wDdP>|qktqG7G?T+aa?;#= zZQwCow_Dq%Ci?RJ2n^F=URn?|kB8?{+pJW*RY9+cdpOpm@+4e- z+}D+){ot6z1irmfI)(OQbz{5=85~>PuITaj?E;`@2snQZT$W$`6TZ?#Hz^ow*J zeq<Alg}$m6rS{t@!XX_ z|2lSsH8D-9oEbNx|BLYOaXT=Yd_NcNqG`Qy=knq=JI;F-R9D$>x=k--_ld1pea8Cu zgrZ5dMS?r_$M$+8tgkT+)^*=p*teil|3e<*#g_66%}Ebc6K15fy}3ANr&-%Cr{A~E z2-oD9{lD`zTesp~QOl8KHyQ4W|KG>*q-2)X#!2rvs+!*}@2Q!4!Rv?pUJD<8S60;+ z`^$%TA`>*4|GP|fpE{*bU1`n^e(6-*IrqN=78%)XdfWZra;Cl7hdDP?&#gQ2r2C$B z=2XEyxtU9U{_@cAJbR=t@591>+dj&RvXU{#gNBOVPrX(4AZk=Bs!SedK(&2E6 zjSD+&9sXkO`KuQ^7^keiBk?{VP3DHm z_gzg9iC)J!=EmHM;3@L4PPesPBb@!x;PN5o?o>G@5k^0SI(2n?wBkS%Fj`{%C|hdrqy@x>P7WRrx-lU_e<#aME`XtOA6z*U0$!a`@xsF z?VB6hPApwtGI@qj#2wYxy?5?yWw4KY_o=(RBuek`>`6>d1PvxXPCio}7bWW-wVWw3 zeZnHAmY0u;7XFbLy?)Nu ziW3v|2;?%lSMM=#^1I)W6S{pqpYohb=`#Hj%^5a(spSZ-=i%by5no&st;*43>c7Uw z!u^x(qR*=hcB}c^-=$N@IQbxBZ%^BqMfVbmHZr%BY~!n2KWnN?-0g4Nyp_KW^i9+J zma$UAWzs(>(~0UgTYvAkdH!;9|Cd8Y`p%h^ES$ge0Ka+;TUGnrq}fgv)ayjpbZQ%y zHto<~7{vIAuVAC8kx)2CbZ^(hSzXVHvipkm_takYV&1gDx@)$u@Sc8txvkH?waZM+ z`EBWC_~46p#bKXSvc{*h)=rr)jj`g@tknz8KI(SP(c?aFH|qBcvp+MUUrju^lKD!- zs`qavG&e7pCt$wpul)1muMe-@e-t)n#|MYqQyZ0)A4YsBsukzC(mmy*DF1}>YRa!B zaGc2x3^_1SNiR)SE~j8l^5wsFn^acFJzu^c)%uFfyQ>K+&t7^h%-NK%@W>BeK7q)n zxd!i^I?Qc}y}YjG^tm}#1@^DrJoAioyR^aK(A>>exs9Xy*dOMai5^}zbLPFLhb7la z-<&0!!10XX-j$iP61-ar7e6%j`c~&vbR>V*EACq@=ib~(Z+K{(rI1)ql~cY%rGx$3 zjm?^W+s_^=ce8X|y>)_Oh0SEWsb4$`Ial+QuGmVAbd)+NN z&Te?AfK{gTYrA@%{(6yTUdY{e_#irdDsaf3#@*`k$ffTNXS} zdiY*@j)KGUH>>Q5O0OI3FkkyHZ*gUy!ogyetf}hD?y!7|dzcoqXGZkT#rqa0=l$)U zTzT{8p9dnj|7LNOG3z~?tIcLqdCN=DIM?UEwa`s+&+abXSk0g+5xJy%qC>CMB>o4| zeH{l>Yn_D;ZT)VfwlC*Py2NGY`}MX={arFoOzgh?diZha|MWALE2Q(L^xj~Lz46kQ z-NZ4r&3$?I!i?4DHmx#V#-XyWIoCgHLfAuwo<*LIAG_{;*d6=4Mpv}`#`fLQbZ3gc zyRc?ggVx5$i^JNC?VKjxI+z^QGq0v>kHv<#vznckk7U_bwlvPx+|gp+C|}57b@n(z z-r;)TH%BWxw{#mHI{)*3fv(?it30nY=PxF(d=)Xd`RlW>#OEHik5!HRjMHa)F*q*u zO3ps%;Gt0S_euQg;?z4Ou59556I&9xB7KLjtlo!PdnVQ^7JoKr*&sVrPwA0p)u-O$ z4lLOUrx!K~{$5=_*U~8@>`#2S?BSEIQp2WaoqHxb@l#6o)7!kKp6`)a!oT%mU)F~+ zWpAe6{5i3=KhM1E_A{X$%}atiqn5q9;e3-hvE!G}p)+l(%``7x`oEXC=EC850==1s zIlt^{`Io62EfT=vx+NvtUQ_W&+w3C?XLn1as2yJU)AIVhm8JW?oKM*zRiMUkf70t| zE`G6*O$eV(;c!q@|N4Y{j@YS>SXxK&-?p>1)ZX8x70B{pI~c!l&Sky^30o=XMb_e z*Pe7Z%w;yx$?|WOs^v!Ie_+1c^5>sG zU(?Q?849Komdx-9QFzu?zP=$-cd@ivg@~kt_p<-jW;4C|9r35A_}b$i zE6(s>42{@1Q)=dqne%zO^iD_2xHQMVb@fw`qUNu;@@#gOOzNeq3vQLzys`9nx+$7( z?>=>@)a@Ni{Tgy19A8booD{GOFh1X~K2F}^AM=eVvDZBxzGf}hqu?ie^?%0SUxF9B zOl`vt`kZM9FwLzHIE8$=brS+ z{pm8P<@Tk>Q?@&5IgYGJtCKxC{pTUKSH`oV4)>($T`at{H}Ao;HPhYO#XmKwTI6e& zOzV7DlYZRy(&_^CJ?5*|EPQ2}c7luJAS56F08EZhIl0$BFaQ z6~~T$$3#~+Iv#LL3OeC=uDERR|A{t7|F=wa``|9~e)g(KIXwN#t}V-zciQOfyTg}R z{N=)#W&VegoM$QOZ4 z-{h}=%ZUsA@|MqL=Wm}OSSnu^vGFY@r;*YV$ynh)mzz4P{+d`^d3&*B^`b5ly_E(Z zwHF>-oE{y{HZ3A*!_5x)_S;F@cID3dd8k^2p=`-ZF0aUke|iIET;i@QWA-e`ahk7w zNi`_B?`t%Fg!%a*pZZw`qf^Wza$+8)n3=aL%x+XMpJmzix}y1W)4XG;3c_l^OrMYa zy=OmJeO8gnVUPMLr#D_>E0GnFseZyS@o(U}=9^mUmuwW>?Q+8*}3-dm5I%&g0v$^hK%Q`w7OZl5!76|*VGrXeNuTwOa(O4*7GiX+&w<$vw|A*{>Sxnt?BwnI1X{FM)>Z1n6{w};Q zchtA&bYK2<#LzKN{LX~7JwHFBOpRjbS-L4sC)tMkv(>8!br$pa7uMhFvoTp&+RAcb z*K)?hz=f)(oO~r5LNoK@-iTZ}k$3sx|8Hw%?uy#xczT)fSCcDp>tlpV@AEb0=J34U zb=%TY#OM>-Tju1YdDh$0vbP`0w~IRnBMStYX^|<&KDi{gr2WFfT?g(QC_% zu1gEA8_w8rxxh3x$~^z4v7?Kmi{QWiQBiM%=XvyIX)0d&Vx0af>hg1s?GG-pm@kYv z)Tu7#@bDb>|Cn%ROG{sS`#{gE1-5GQ+}+ea$mWYNAAKNe(G?Qvcqd{<&Dw*);_{5* zdya=~X#X0&^utx-7707suy4PO4$H>f=Gd#ky~|?H7n7foJ zTD?@%xDVf2SNOW;v+7mxqrMxsEwT<)8`$JYIu-s%(~g*Ye2Ur^%?CdmzF4v{8NBY+ zSG9XI@p`eG#AOrndo~w}ZYuA7+U?GCW7mV_*XI0|Gc))tedA@N&}y;j`E7suT;@!% z>`S;`lrLX?^XpBoIgbvO9NpTk!_@I~A7{3grm@d0PFrO`oyW{QW*cIaI0CORYHn>= zrx3eGDS3YJoRa?YAsgA(t1W%bv9Y0kHB)xa+}v%C4ENS^Iec&VT-V=pe6PDxk%Elj z-Y|#RWf7U7r)7_ySQsnVfA4Ml($9C*{Ejtb+%@dp_iD+}{gc>#m!%o~ie!ls+NbClH@_v#HN+n_Ag}NxL_n4~opVShGNgKe(#m zW?g^6vu_#x*{1h0LuFq#M$cCG8QCK7jZ@;7%(GvYwanzC5^i)K$y9NAd+3YKDutc1 z`kqwou3OQ*n&D-1rS&z(o6@TC3l#qU{$?c+d*q|&+DQT5wwdnUR`oN~X@^j@d(P&{ z-7noEf)=b`TsQCN{@r$pLZPLHx-4KjG7ihR&^9c_bVibwrIfo}RhMl4G*!?!TGO^_dby z6BlauZ(FhCZ%WL8<~a$|JrDY?ej0Uj>WbpkcXqJ7uYD#jaP0ZB%U9-JK6`QhCc7ht zmRr5p)wADE%Pz5N%J-YOd}?}J!%y$V6EF29T<$E&mbPt4-u6E4 zC|kg~KU&Aq*<>7_iu8WUoMM@#{k1hHNZ8%2TdF`Wt~I1;gPq+8zP5wRQ$A*+oIhs zzsE>A=(h(2MF!+7x^U&3U%}OmbzzZe_X3Y_x7@FNp}TvL!tKVXi)_w6*ICx}({ZZ& zjEkQno=t0<+_vQTQk$qizYDI?R-Ja|O>8z!oLpKs;X}WuviW!4kQx?-bJI>%*~YIt zo>9`G7L@svZ_bzQGaAdEq%S?~*DUhL&?|M1INzStm))ylH%*9I-?yMeajlK-?uQd4 z=lCRNoZq>+W_z7HgOmdG^orKU@i4=loUCrm=9tU!AShG8fg8 zPWtmss{i)*ql>FC-|JtwA4`px^H2Ysmg|0L5xag_MfTk*ueKI0eBvSBdT{+R^T&?v zbsH=X*hD~D|F5ZZnug4^5x^vT#tggb}@s_M>PD&_*7QSA(s#IgurFFNIO9Bo$U5#Ni zop5;5zSBE3_~Qch@Jq`0HV8fY{psX~y=U(%dU_$h>*f(H;RllJCrfm)pHx;|Y?s|_ zsIzHD+~dgX<|@s$rlsrO|MuVd z)zLi(M-%#%JKVFg+q#n>KW+22Ew=+3KZ&eJzOv+Wz@5mg=?o!i(vt7o?i4vFZR~fl z`YHTzSHS+9L(JK`t~-4DYbcl}H}4$yZ8I~kcL$Yg`EBcLo^pWQl*8WMAzU|ZUN$MWe0*xxWS>i4>$PU)zF)UJbn?2jJR+R#O3yc5xw@(860+p1e8+3IeK!m$JL&xDpvaf-O}rTZ_p&YN$aO@(jx{S)+U z48OY5*(iRY{JN)Z8L38rQ!Ik7`SnI5N^9886`9Pl-F}YMB&&-(Nng*@s<<@GwYy@e z$bR*Ed&2q+ZoUsr-L&?Qk2M9#yOrLtb6UX7Y3>wPB*JreAFwqtcXMK z_I#1OXEdJ~nRU**W!vV@q`J1^v;C2cO1$2u8qWt=Fmlh9k8sI6wf?8ee*Y-$1O4Bb z9z57JRs5S6x8A~KzRyLcA9{KGapv2Vr(sv{g&%!U{!S+7N0LPJgo>@A ze8&$u=$Udzt6VcJQ{Vco%x2k?D>h=1Pb2?NR;~FFbA(y+@tM=_>pc7|o?dM2HgRuR ze%pLT(v}?W#D&f?UP_BI$`@rSaS3K$H&vSDuyJZsz~g^v zO|}+`^SkfRFSB&0yg7qcXX_c$8^KKJ-(H{I-7u-sGjB3`Z+^EKtHI@I{C^^Xi)N*N z{P+GOyT#;3`@Q1-p1fP>xIVW&`jl9X$x$uIKLwF{G=hCSZ>4Q{_^(k~ExpGnIpGG2 ztnF5YuJ4=IMgM;pa98iEYs(5I7H+RfIp@M=#=t0p$-i85T~>*m(Rgfc&+*EJt^g1YGuz*CVPo5??Juu| zOXg+>8Bgh#I~SOCFiT;@$2B1{{-;@scHPo~8A zijs=uTJNAUeY^=)>y-LF#|h2Y{Um9fJ@d}XKjwE#E_(V>+voheVDHVji??svm2jOi zw1w{}^Viouk4>r$)<2M}{{7B(=@U#*N2m3~$v$ZO=KAVe#cqqoTRRVL_#9sp_JvRC zwMF6S1g}*yLZPot51vPv%b4(&#brYx=d%qqA16!9*+K74kwjr zY~_5U^(X)KM$V;Glh0hqivMx-_HBukt2W7tSEQ%r@7NSl8OeNFK-g68g!lEkUwm&k z=Xj)4+8&;J@5JBC<1TO5%JU>APHOu0I{(LuYuqjO>=*X5E)>=;n|#7Z@3&3a&HoE1v$v#k+U@#!IE;(({>~ITX#Cq4c)w;{Cs;;t%!aDetbB zvgL#O@3m0(mlK|@V>{OQyK{EogjHGsHxh1fukV@l zDv$BXcExE-(~tgsdS1-&`bpmAWoPR7GJU2qZJg47;P%vs-ZwdJw^o1Nxv{z7*R;~x zkG6Ak-xWQ2Mzd*~qf%a9<5dPQuGuU4=?`xcJQrCuqnA9P$7PoB|g`Ruh(?f3X~lOH~tbW$$ET$#sANLbBw zOR9G0x0NT8QonB5@o)eBEt4N~hMwdIa;bQzsk!pWhpFoOOWKb=BXWpYZZ+5=+!*k!L@twWIvW&QTD z$Yx{L=(4=!EZ^3yy~Dg#A#*q5%VU+F*>+02St=$_{_ypo3IBhsO>0r=yU+SpS|t7Y zgHl0jv(wuG_xOGCMvtRJQaohDX9&^l{+?^Yy+lo7dR$IR~I`ak>>-TuU7q@iI&JJwu`*uqHip}~8 zX7MfD=WSFSL>w>Woo}4EqRja3a=RGa%%%VGS#91vkxb1#@_w%YNA9Gr4Krdny&Q{e z?v_oj_scn%C7wFpK7nw+p<|I zIg4u>_yQzPiOiFYlX_gi$DOvpP_XjU6F%!F3tt74bDg$M)|sjG>;6vD$x6#MigKF# zTHmq#%-S!%9x*mu{?<0vJvIMKWkTEy`5g9LHTaT_L%DK}MCvx&s*xc|(g%-}+$qY?}G&aL1GS58iCQ?xb=}^6LKuNty*)?OYc~ zn9u*9Kj&fHm1jS$+;&tyvf236j^#-!y_}_{gfwNlyM)@VFZIlBslNO1$IENI4L5I2 zh>OL#rkTZ2KyZueTXin^)d*Vb-!nP|0tZQSlHn~$y5xw>ofIl+G!PuDB_Dz0R7 zdf+>;e3nL!uMy*h7yVA_LRLf;`5n7y@J?66=8nf{RqwUW`TEihR_dEhF?g`CnD1ZW zjs-;(xe3nfteX$4)QO$j`n_4iIAqS>$6+_u-JE^V_`{0j-*Oez_azFyEX}#3{A|VI zM_(QKT@TfiC(L(S>&B6~kMXU5t?^@Np{@7xueXL7|BGLkz409H=3lda7VGQyc52Nk zGh$RWp!^nERw|W0&!S z{O=2sdrQJurme1(l&&t@-hS+=Qo%BJ0i#Q&zqPP(Z17?DRPy0S-mbZ^TxJuzp0AIR z+P%@H=Wy_qJw1l|T@_ZnDKe~=-fME1;f#VGFNgnzmVm$S=GwGbe4W)3U&|8uT6vP_Ly3=-CiKK@8{Fdt1l%JLpM%hKElVFa8*)S zGW29vre*en8E>DZ%PuQ1_con$XqWLPsqQ&5PflNMdc?w5Yg*Fp($^{+GNC$Y#mXt= z&mTNVn^*Z}(fv-dn&0n3YJzlE@a?jA;7%WuHf3v zTdSnk`F$x)js4vlVj!9j{Qby!aoJm6*E$5bbqe+*KMFaQR>in*`NQ<&6X{kfSa;>V z?7lzw+|>+ck%sSHohM4xR0)c)gzxu%^^rsCYQdA7y9dR-df8t-=b*at@ICjSvznhK z8QrWt_A;q%#X;YOh1=7Ff@U7oPuuRGWWKIX&3>lN<-)kmg@4}gu<+z=wR0DIdXcS) zVf_TI>ko{NyvwgiOSs9dK3|0WMrmcQr1Op`i}OSxXFgx?-R8`muhzdrkCw@*Ji1o# zXV%V8okx3a@GwqW@%_r#}wCeggXj}}+ z-9wI{T{FKH2VCB`WZTd8=@ZuVEcQ6o#x1{_duE&5<@8;b_2&AdO*y`*@mPCoz~yMU zn%@ViT;r#f&cCg4@!+P7+zy)>TrYWjnf|z-RP*M!MXkY0dSag%n$4=Iw=s*b`n1|O zMx8xP&A5ZM|a1Q2= z*-?M!L-@aY3nPsrSzE&Q6mZGxoZpiXbtcrt5aYQw)3IQr>Y}&R9mC7IhMY@a3lCAm&MESk2_|(Ojv%#-C>FK`ToUtqqGax|6M#bad)>8nc>CII6ZH$iA@p#UAa0 z?>IANR~jYwzg1i0yG?nyv+1>DL4~iJtGU}2bqZD9+Zd((G$m@m>~+15js*96PvV>} zpYi-U+oc?H7VY`(_uN0vwr{G>1(m}#6Eu1qgEsH~se5SoO?89be9fv&c5xlH zPHQ>x-`>iZVWz)CT#`{+PxPuctE~34jrUg`Sys#=^hC_ap|<9j>Z!*(*TN3Bmzqia>#(|>s|g=r9vAduN|DV;$wU4-7T$6SB^F)%zWe}U1Kyy_H4aSM#cTlmuC8{ zzV@wjmih7{mCQ6+D%p3Wyz0CP?v_Hs_b9>wD z_(0vQTVfK-3g0qKd2madP3!Zz?{{NPZhYj?qqIs?PJO-c`xDdcpY40TI{)t0Z%^{m z=A1frMf_L)sjKGPom(3>)F>KFuh6PfK}`p0ND{ z)05;KPB&x269^&$EKU4NR|Ms)53@xlbu4!%ha^Y#w|2?}-Z~J?} zw$$u!ngfgJrKz>6__sQz^lF|~QT=s+|IqoCz!XXTjUBIlvE9FVPxVg&x;(ztAf4l@rL;iwk%6ppHwxo zXRFuQCh^El`<$7tCvCla`m^fmEgcUzl;`KkZ=1q+wcD)Oy41laLT260?CCDbx=QDi z`T7Do73W`FZ8PJ9#uC0MGU9c-u~wqT- z-%Z=~{gZXpgSUP+k7l1*$m+?F9`>MeLr?RzbHXb#H|`8Jxzcc?w2U#|dip|BLje(~ zKZ5+C&I`W_RleSEDR{;AzkGS~+FaixEi-r|e>!28w*0f*hRX2|m$mcNc)&*ifRnpP4y3OZeESJ2#B^OJ$V5u8h*-5xsvmGh}H53+uN- zb8TbJ|CsyZ-5a@&bt^mH?NAFjHf75H37PTAA5;@g1vk%IRbtM?oss@%)1!>QpYx`% zy7aVKB^7jT`_HNQ`}E!|2ZQ7fS59~^Io!F$jbAp zd!CUM=l{sR>%-xDxkmL<&zk?(x&F*G(chgeaz3beaOr8{hz3vH5nK>#m z?3E+~KR-~Z;JBFLdS=_(BMFwT)uMVz#fwu+xz=eNeeAjJY;kpEll8Wf{zWmO^7P-jfWO_|t1Ih`uP5f5HsL?xXR&{lmiXk{ zpL;D1>;08ix}fbdyFh8Fjv%XVt&6~V!vNboN1ojM@Z){^;$3F_3?WO@|9N+H@0t89GhGumNO&BYyI9(P z*N^A7^r|*`%#S@L`bXI+_&`uq_YLXHg-<>{DV#3cT;cnwYkt|YLQgHN^#SsWfA#5T zsouQwr)j5JdYfY9e}xB!;wEKj=#{KI<5PAbQ*55N_Uc-nwF=8F&efci9oB8R>$bI8 z6VG7_7w(0L#=T2t^L%yh<#x5d%FfyTqNpKMdD+ANT%0q`?0v`eV1I1W7Y!NigVvK@ z_wO`3do)Vm=>8xMp5`}_;yYQNN8gKF9Q}R0k>cBL6Q7)M^48gLPbBHGB$JcqrayOt zqSB9EdnIG0$9gz(mA|I#4mWe2vjT=@-&5~Kz9|=%f4o;yVsg%Op@@QjDK|qq=C8Vz z+%ETkC2jM`8(KS7zx2p7w7+(%ZrkF=Op3uWr=FRgTX^T`W2SfZshS2b(TuifLUo79jIep3#{izL8AIeOO)E8a+DOK#}#<|DOdF0v1 z9{=UC$K=CCqt2r%84EToS{Oc0Tk4*j*2I`Q^HUoBZU6RuVfbZ(NWc9P8xxN>{k>4r zt=wfA>NS5tJhK{uNX|i%Vz#vPTkd@B5%BSUSK-6Mf5WW(`N~TnZCn8w&W#+3CO@x9 z%#u!?qRoG@;_ZVUlNMd6bxN%d&P(ZFdujVH;hNt8v74+?7EU5zQ%*KqS8D8=Y`46# z$zb}&Z!Iqz0@h}E&pz|$%*HaNw5lr0jdvVb4{c{;{a1bT@7w)qSI_L6I61!e#{>1x zK^}pJ+Dw_}*|Se8*1uN3-eJcS*2eHAs$-w8?!LVA)3V7)C;0ZbiI~6p24!et=WBY8xz}(oNcy!;2+-nwEuH0RV1q3%Jat6L>dTunKXq8^5jXa0R zJCo~s_Whn6+j=g?&qrJSpwZ^5x#c%!JB9nLd_McdUY-vd<;`-{jbHh%)(M{WDsB!3 zZ@u67eL*tedT(X^dX^u55u_JUUCFuf>*QD0p1ozM|NUjEz{ZVx&NsiCdgF1%pZ(J! z89w(#J$8%Rab@w(N{v~!d0y(wYjxcBwS+xz_qE?AI&D)P_i?srE)3*%)>~7d8}?0h z!W=tuj)*IUd9M0j^onP7JWz@Y(rA4hl(gxYdC7-a%jJJO^UQJG|C-JAp^e{-wnY|} zyVoy2zG$7B#saUv2~Uo@b}^dlID9bmf>N5B)Ah-oA1<5SflH9n~^ozfn22x4*Rg2>Kz+I>$fp|)jVM5x1qC7AnuF)Hg-Po z6GdhLCrght=J0P{@qJ&_iI->m%1=3ZA5ZtMRD4~tbgJo@D<%r__?+|}UXwg<=h!A+ z|2Z~n2O`=I{&d;S95~-9-KcN1&g5nr{~a?6l(ahwcF(jw_0`JZpi=8nzYwi3YtD~f zSv2m=Kc%;>OUi`d?)_;6pY*P>muzrav!ZyD<%SRsql4dKE>vC1o_MJEU!G4)vG=tF zX3EZgbk?8zO(!?6t?2s0nZ9$)IrhiIZH`@eF-SpSkJ8J}_8|%e@?*T)oK1yiZcO%+-CAl`)0%Lu`Pt`vmNFZv|C_&+4E8$4Qg+PJ>JqQ= zgB6?SW@XqKix&RaCmHyuK-Dq_&oZcSr~MJDu8Wf1xk#%-WDC zdPT8Ompu+Xn&w-$-thi}eJvSkPnIt#JDFj7`b-(~WTu;~s%cj1-d}&#VZZy{sfOGj zg~^`x1sbDcPCj6`dMB!R>eu_GkFGlR;F6R^V|FWc4g<9D2 zY8jjCZTGi$T5Y8{uZ@qtb@TGhowmb`Wg&O^(JS4y*ZYW zuF+>+(wY7C_dj;=H1W0XyS^;4Ej3Zwf3(_$@$@OFnAb`-mig_=$o*xp=R&XTU51{& zLM$hDaXtS0;#t_IPW}apFM6#nul&Az)ntj@zmpDgNvt|@@j%UqcAh>RySHs=5AFp; zM~6DL=Y7;@SYYC1{LPzZow>Wg5|*_tY`QJKoeRvQbmpur_gQ}TWaTU|;rz!Z=7(h3 z2p#wCx_j!u2BDqmlOIn15&6s_cuFTrLp=M-pyqp@?JI9Zn{X$j1o8T7%~>$z&XY4! zFR!s%mz9#T+(G=RiF#IQkgB`QPtFG)Qr3l;IEAfWdnxn${yqHyuLWF#mDEBT{_Eb+ zI(w+B#CziAmmUl4{aPP7PV?k@o1S$*@bsdj)jfT--N(f2+%mE*w^^vpP+nniQgLz4 zbI1R-ACiPNaPdbzT5p-jrP1#Y=KR3d>5q@7Nu%2-YlW*NhAy$qa~(yMgX)V+S(*cM zXJ}`i{$wutyx#Zpvxj-s0qR)~O?R$;-`e&yVoglxsj6#vk&H`t?pe%YU|;|Mw&{sB literal 30344 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 diff --git a/doc/qtdesignstudio/images/studio-3d-editor.webp b/doc/qtdesignstudio/images/studio-3d-editor.webp index 903ad69b25d95d315a4dd58d700fd2831eea2279..2218505917ea09389a4cb47e7d2562d8f2ad6655 100644 GIT binary patch literal 19690 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�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 diff --git a/doc/qtdesignstudio/images/studio-3d-split-view.webp b/doc/qtdesignstudio/images/studio-3d-split-view.webp index 630fdb48fe40dc3e3dd692406bc64a41f34b0952..aec301c91f57041830290f5796beafde90536883 100644 GIT binary patch literal 22818 zcmWIYbaRu6WMBw)bqWXzuu$NOWMH`Pig7NZRz34VM!UIg^L(__7cWvS{C!}9aA#_s z4@Y}h??(T}Y3>|+AOFAkoaE^5u<_Yv$FKb^vgY?X?q$ z_r>(jHRJI!mm`Pt#?mlzf`~A?~eZ) z|78Dg{M-FkeS`Sx_4CqK$G?+n`M>yo|KAheTmRSo4t@9h_x=_7Kl6R!L;D@-@5KA< zzbD_e{@wj=^+nYT_W$3^tKaj#@n!yt`z1f8m$My~zffBFZ~nUe&->HlTVp{$LQqs42RvP&$l!(?Z07niO=!Ira1uz8PaydT+{azm#}yI>Q`=6 zyz}zhb93I6JTG2uJ=gNi%X82D$}OA9c9wm;$za{Y9r)?Rt+(@*=KU8t>)diY!(iV< zw`H33F&mxR?=VZ8al32&v27~nyEXR?|Ef_G`INwPb4qAhp!CMfb&>AhzFbL}#_KAy zZg0$+MNM~N7Du{Ve120+z-2{MgU6aHCQEP0Iq%l+u=cM`He9Q9X1;r0qgj5+q>i_z z+E$(`w)j|79qoL6rhxPcn;FcTYSy#=zP0DyERDstM8X6g9(Uu@^|>MNW^crk;NH>) zzPqpK_kUk`(f5ST-kq%lJ?2~X>^UeG8)@jYm(A0&OW5(Ye⁡AI~q$=qZ0cA?Z=p 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

oEecOJkzs3t8-Px_j6kZq}G}PD1lBYen0< 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%0D4_f9PxR7{JD;c@-SklBT(`)9)0-bo61ejJhW*6- zJCe4ute$=B>|@11wJ%)jzC3PR`F3w7qr)umR(F{x(Mg>jH@y9$^v3>b^eJh+Wg9=6 znP+yn%~-14ek9*dp;qw3%c}4^T~D6dNiQsIre9yR$IQcQr;hM}G|qjDx93b&6!~I$ zdiT-?bF2h-#rj(n4$inbuk=#X#_rO6;cZjrh~Hkf_S4L$c=uQ5Ew%Ug7xGzYnYV?k zwR7Unv;P%#JKCp7Cu#C3?KR8f-XBpJGZtDe;rZoZElyI-3p6uhy+y%qD%C<;IM!HE-M{KC^_BSn`|+Xkyau zI`&IqLGo^gxU{5^Oa168 zP5NN3oh7z3m+@KC>}|{T9{M`tAKU5bvQX{g(@I_%Tt2wJJ7vd8&uf!EN=DW@>bjh~ zW+yZ|D|?rRR_kn^SEpEvpC2srnkRNQ*@0=J!Q*C!b%z%H<7jXAbhUeWyU~Hr1BNmV z3k^M9dw!nTw_;yWM-9)a6%~sZWlI+S4*RoJsW0hb&A)8#;&PQEs?iG`n;1UWd^pz5 zbgSQ=CkNInU)@z|r1HbXLO;<%OYrfJZ{d*)=NLA6ZfpMiJNM3~h}EiILM<0=KKZ%p z?EKDj!IZW|AM_@<2;IA|<7q&T{HYYn?o(+$Z`#|j&R1%TiEGI?n#TMvRi=E_XNeC- zzd8xN41RIHZN~K}%!zZppB7+@T4Q=`uio95$!CM_UT3T<4Bz%DsBz-)!XJHtckfM9 z+iorYw)VM3aI9{6rNpCcL7MCB&%8Ri>Ri;;Ro#q*O$U~VtrXRxqdcJIky@k>{{%|x>F zH904}VSBybT%Y@&w|~Q{TBkW(^Deua9_UtYTOBl`CVac8#?fE$+A0@MoMU{o>xc}y z%ob1A-w`i1Uun6oJK=Xf%jxQre7T4@m%3{n z{c(*nyJ$9b`{hdMf*nDk&zR0~v_{))kXVr<(YV|zT-p2}w_a&G$L{*vPs#SL9aH6_ z3WTlZAA3+D`zdO9&h>{5Ub7Q?B{XFAF=t&#)4Fc=#oOSAOOs=^mG!!p4qn@j?mjZ{ zZh^Y~=OFukcCQWVbR)N3omT#Sr^S@$88R~!-GgOkyE*4CvN$?tQR=4`-}h_x?!IQZ zq34*hyy16~<6P#(-HZ7a?&?3=aMYt?$LeO4`}>)`x?Qeh{qVqW$CP=@Po) zI^D+dWxeuSuD>sTWeWb~`LS5Wc^C7G+Bo(`Yq!YQqN!2I+D(%lTLs5=2^{$8Al~?k zd);$8o!l6odr4d#hRj|}QkS)TzwOFkwOhDAd?J&o`_EIqwJz6fKDcE0r*rr2P00AP zZ?&`zlgF9v;NX3|0Vbl?mM;5oM*rD8p58Meg4V%*y@JA5xkWchZ(mjz(IuaMyv%FC z)ZeBvs%O7xRG-+n@Q&G!gu{34`Yzp*DY2a0BzJ{G!#?qU=|$|*9S@ zu6c56kJh8!*~yGa&n?PyJgvmnZgy`sxnllbD(ZXlHT?xWuTmY-lFq42?z+QezNdQi z!V8lYp3n(f88>TFQkVE_4b>HbouOaqGWV>x$uE>%<~()L=lrwm%4~9Vsk8K|XWx=2 zmYI0uiuMj&h2F2Tz2Bcz-p6`heqpWcj7=HN+IFp#FJG?SA{@5k{{c>8UfIbPu6?j; z5~?*f|MZW!UPM0L$4Kz`(Y`6XPEoei3z9;<4%Z01I}O_Y7ecZO|Z+KQL9&o*7I+;Sy&9+0E&=hvmVoo!?wXePK;66FkCbsX#?@MmGdq3rf0)&m$r+N6HC@c3Vu5JSAXisj*X{N zf>qA%;MWz*+Sko;L61GQ=&{@5gC}k_J1RTuTwGLH7WRYTq@+N?bZHy6X#!$)eTS~6 z?bS9qpJ4Yq^&4~l=4ZXk2Rr|pPfEKbdP;m+z!SfMiFr5gZYZnne(me!(y!V+f7Q|t zB0u;R_p$$HUYGeYAi+7};jRsPZ)U5=$2rfM{rj`xMD9C3>>H;uFPwV&u97LBD=;yho1>`a{MLi5!LOnD_Dg0e~A)ju0HukclI3DyCw@o zRX*DA+zI>k)#R&Ah}Nz36;swsdR6=Oi^{a?S-&$@u$0|$V_otgR`L3`glkC;?y<*- z+HT*nV#W-q3=NVE9S6peJGerF&eeyJx)i@$!GCJ*z(PjBMmJ zD-Ic#y}c`pckeE;oHy;l?HLgz+3#MgzjbESm5m{hd+jQ7M7ZPIe;k}~vnVjIoOx#F z5}mp86qdXwclmale_42N%{#v-JpKk^PE%Ze-}rd@;NL`rLoyR3@>+}5l^P_UVGf^J z$rF;kGy6}|$p_~r{@qqvnDYDg6aAgXt`*N)T+6_tWO4A)#?*f-MzK<9Qf9@omE~fa zr%pR%p=NOC=CLN>z5@GNJEo(HY$P_Wm~dyJ?ea~5E(hLBdzTol>rwf9UZPu&Xs5~9 zb@To#zA3VLyNMRt-`M0;XTwe|)j09EWJ#Ds$4e27s5MSayfHh4x!>{?|GRFjCUx+B zRGQi1iudg|-#A7Ya^0vX%;FQBQGD(~CeztFT3XJx3(l%}tQCql`|I=Tl~NCRt<^)q z&Qx5DmiTPEHmC4T--n3W&$lXXX-aTkvhY<-p3eI3p0%dnlM64_2v3*hD0N$8Uy-ru zdr6D0yYRbSr@vA&WY!$}`9V?Q#_`D2njsCPlM_yD-WdC#pk|t2bXRM}uiG1UeC%vJ zu;hM9P<~p;^LrzOC`n7lO+P;`6&o6eqh}ouhj@9~p(bdw#cQ0&{&ObZ3&gIqI zZNUl{o8-H8=i@jjHkX`s2n~!12rxez9OWW<@_b>SE zpHs4M_V&j91Ex2W9QWuqspo4N2bZ>Mzp!|{clDbm@fCW`?n^m;ACrq&lDDgre&9-BS+BrcKLFkGIeF-JgxPb^C4NT#pT0|db$Hp4b@tmQ??0VYU0K!jqWxgo zql?cT*{a3Qn7CA&t5czD=WO#el7;_voo(2?cBc3WwMb7Jxt(R9@)dPztA1KaNi3|q zZ|8EKGyCf_vxmoxj2@aRYh*tN46{!;$Tep5+?Z$*bs4yk3gF0PyAdnN6?!-YBJmsd_SNgBy z_O3G7xO0>0=I##)9iQF>OwU<8(ff@u|J0jTGY*Kp{%=%ub5%^rx8D!7U45gIYNz=w z%d=ygtQc)M-PnueSd^+)PJ@e(U9q0ukKAefrY}Doakbgp8GhVQ)~8@@Q~QZx=g$A# z&Byc}{`o4{aqaxCkZVVT7yW6s`F(!##plevHu0>bXFd7NqQhf*<5*t0bKLwQzWq;L zXq-UlyjU9xlWQ~06CWB`Z)G-1KeFmr^7M5=P10)9#cw~(d>gD_z}0b$-}3eu<9MzW z(PuZ-OZ;A`f1oGFEZ$3TM#`S$D?b?O<)2uo>XyuR@O+}R?d#nO>vt{h72SShwbiG8 zx<@~8a3#JJy|gvi?6yetYwk|X*XOu7bxj0!b~R4e8m1HYC$9APimJ`u3_^l@QlE5} zZkTa5r#@%Oi#w}Wu5oeX+}&=%w$Q1BzO7Gl<>yHq z)^oK zuf}ZEnJeL`mVWzof#B-nnT5F~SGU&P^qTD!p=NydWtV4AYWT0V?ZM@D%wwH&fBl_i z)HOd?CECd;f`Ng7!8Z7;{`_@+Dz~_vc&cSv?{@G}!=#HpQ%?%N-njc)yY`_~%IpdJ zHk{H)RZb86+o#SKzV*mK#qL-0LZb~6&votn$Hu_TE39mL?ix4qQ%5KJij4QC9Ry1^ zd(;#)S+>XpXtPWIU;ccm#1C=B6J9pquXdMvUw-&O{me|qUa1+Y^UoXo`Ne;A%IY_+ zzP_{bpKIwIGJ3MeZ&JnelJkqsoetk4uIY6>)%*0`#*}F<`kGr~JHoOx+%FzBEc|sc zV)N%+=eB0Q*%+jE`Ns!N#YbNoobReB%54j?R^DuL-l0c1bd&ye-;XP;EaZ91bQjj& zj7yj=tZ`F!(zaiL6i%;cB&RSo) zL+uC8biovh!j7}Ik4m4HeEhWOsk2RG^{Ia^lsD&ehV-4<>%TI%qa^gUEAN(?msbkx zyI(IBmRXT1zjpOXKA(c~aaNTcN0smXI4T++_a*s%*xHQTrllLN?g_c~FevMK{z}%* z#`V7Cv3VQjZ+yJI`-iAN=>@*58y}X6=)P1;`X!!peM(Up|8>2xsDrm&TrN2DFvvcx5YFg+R8lG+FtY)ex7oN*aVmNf?$KM3M&C}O%w>6f|xp-9kDPKS*^X4S$iN8*9Yci-!pWk{^ zeN#fIiPSl{OUj~!{ULVVUth1>F4=PYg3hJSdU=Ppe*gYV@WX=#XG?XC_v*xo^<4JP z6BP8l9`hu>ebNKf6API)>lZ(k`KOmQZKH$Z1XVA)^FLLSStNGPc&Ed6_xzlteY!Wl z9l9ZPTb(5|pN}JTdhqSPK1SuK8ONNbG?!F)>#*`)gD{Dm88 zO2W@p@|Mq^I$ukDduPRYPtRCC?R6_#Y%YsccJ{r$>CvCe%AFLyK;lbd?hl=6(~se+ zPp&y>;B)W7mc!QvVM9=E%e8QO7Qx-d(j--aLg|kH+N@ZV*z5m|NRXxGB z%kcESNZ!!4aP|mw>ENZ`g>AOZxuCFr-_u>Di(h`I)k>|biw)CGQM;t6xT3w4$01mD z#vpD;Jos{dB$s2drZaORDxwY)a z?mg4Cr|dq}ZXR*yq)Efk7k?McnSEZ+J#y=b<NHaVzYvm!j4*Pyejd2 zH1)Rn<&9fbU0N2GwRJ+Q(D&&F_bl&vxGMGbUqPSPBKf`Ze!tKE$gbi2D4A(;o7eQ8 z)rZA1^E*PkY*=4Nn||w>TKHJ`_|Y4h_r&&|68$W9D88c9U_!&{Qg;hZLoKVvhXcYa z>cxdF`;_n}CjCx`4Q^k%dEX?r`iSV65nrwM&R+c4`&4{*b<$Zv}+st>SOdP~10VPNApKT1^pyf2&n5BpYm+`sZQM<4+m;jbq$<{&Sk; zOn+&R#Nt1vR{i|3_r~o!yHoUyg_JuVESh3JrK#}h+?BJ+xFk>CDmgUk-IJJA6YM`% zg>)8i-H5-Z_j6(Qx|y&1!}r{t*;Bga#*M(eGFR<;9|nC%2$z?QF^tH2Ubok=sf+Pj zw~Cx{2|LHT-Fow5vJNl^vtM2`hx@kDrim_T>PJ=87oQEirliZ(lVTjjx(^0=tpTdn?D<$02aH`G+BrCgRj zCfM1~dMuLd<>#yWf49lpQJ+(o)-$`%rLRXQTxo*Ka$hss&x^k3C%0Ztn$gT_`^{-z zXuywUYEIWK+U5M8eAegv+-Pl=zMPVL*JhV1dCNPDdmgV|^LUQI5(7tF{+Ao4^{?99?d4L!nP#rS0$!y}fVY-U+*UvOCvC9o%_RcJ+zdtuf(3i#UEI_37PTTwQX@b?=RSwnL^B z{2>v4XGMj@$7?^6u4mMiNWb%yO~d+oqec6*CME{KuRAlmBNvO;^eb4H&2X48Uq@zJ zUHs1@av|RW-M&N~P|rUiEO9(w>3q|mPU8Zmxrvt#UOKQU(M|Xp14pCu*1DQGM>Lvq zEhVh-cOK_1*eKD-w(w$Hzg_U1?;&TW?d&*}%<${pYs)Ze=~G2_crNXDt34~`kIT-9~3y9I9BiX!@R<9QZyWI^;&qG_&*9)xQ4Obx-iL zDEFE3AsJ0+sZEar?}k6Pr<3NbTg1IL%aD1mmHg5}`qMmdCQaBy0WDS~my#UlFR~`~6vgv-gUo>VlKn zhvGeMy%h?|DNGABt+xNdn-}4<=Ix1H-u<%qRofNBLUR9d7t~D4PD+2@l{K-Xrjrr`s&2V2(P>kAU~s&rbquyptRsM*c;Gj%3ApS$nV{BXY0 z#>s!DtBA}fF0dCqbZX|&lZMdC13kCS~tllqrx3+KZd=<6Cf>p6@A!?iztFQ0T zyU4OUW7~^+yVZ_x&uF|QFIM5(|9Y-L{lU6aFoT|@m zvC~t3g`f1nPrDOLe;;dQ#$wAo$>gyYvoy35!iz=CSUdQ9ZuxmC7e;i+3KzKc!u&mA#{N_`b# z5M+`ay5Zn4{;ou^*~LMc;Z0Jyw{{sW7SY+Ir^s^eZV#(Bd#bnXrM~s@r5ex9a6Rfv za-FqY>aC5`ms8JJ&CG-%Y}9#g)w6hfU3@w8+?)EghsD3X_I?Op4_=pb@-~mrd{ay1 z{bt`)&w1IWt+>Bu>dUFmQrXWO{Cr*gi`MpI45}~s6&Jly^|dj6E_m@&kc0l(xmS)o zm?^v^`F>X0v?a6Oe&GlYzOhADyYGHj&x2%^r{ z3)igrQvIQIO|17mm4u7?#Vc!`LFI@-`sIDxcSEx zA+CU@OBr@_E6v@y_{e(?hS%0V((eA2ymtHV9G;sZAHzPkK1~qwIn8-7roaAVALHQ- zJiCMv8n~QvI+w2BuNHaq-~5{1QRq2{c;sslkzycdv&{0+Ma{QrOR)AUEKdb$UR=-aew|- zwq;jXnjh`>sSr`jT;?+O#_Z=z$G%^&ZL7&JVAZi_y`Pi#+}*)?Px6h^3IAWQKl#(l zXu6hRN@Zeg6Z^}cO>G<2edhgpcCpUpgm+HW+ zeN+4N{)ziLwLh+1yfCG~W$H4&DYyS_INdSj!0KB*TnvvIKTAED(3cs@t*(`Op;CO# zk3*Xm7F}2$t}0Tz^_ZdNgMU9tCfImv{W0q%BlAC<4*QM?i&aeC$q49jO!0WhGxM6L zcByf}-7CI68tjta3r}41_xnCCNxiZ3^2)^99)cfuPWx4@)qAAA|MU_UCypfHqz4Zd zl*c@O5N>Fvx=OP_bb?&qLxFNfg-H#UU$0a4yl_Tnjn?Ln-><$MiDFDv`N%7WRmkTI#j(1*=ZN{IkmzPjuv+zB|##>d%K~+upO!d0G4QaNVWr-~1T` zvnDU(WKMY|#>>WWq%_63&CFoI(G@b9y(^Z4_UX*nUy*eD`>*AT0*o{k{3_hr@7@`y zEL8WZiO(hfQc0$Gd+?gjNd_VZeG8&x>TiK+9VMB)rX$Xk>BEN1iJvZ)u>HV&PfyYexssLMz*RS<;MjX7C zuLiR04xjF^{t&Y_%dTmMJ04BZJXiEuc*ipl=l3;#os}ALieDzZyFNdB@3N^qPqr^m zSA95_XOp4%vDsxgrz~$AoNFRhyQb-EudIJ)1xH@a4Y!C?=DC}0Exj)(5xP>JC(*7d zS?G;QOu^)8#tjF8>|8yHeuquo{C&%vw~yyov9m66?Q8VEI793tm&N_9(|Z5>6?>hx zdSi_BrusvoH6LBdKNSk@Ss=1w72A7z%hYckH-j{pPORn54f<+-*MouY_QA!!&v5gK z1Rh$d^;pnf;+e7GpZojYhfg`VSUCIr3PIKn%vOCT&sTn)I&XPqYJI^DzlIM#cLik^ zuTRtzKgi`FUOaF4%ahqV_$uP~{95+xaNGAc*i5Cy_GPS<;-?+Ug+wLp@AkUH`@Hyj z^5>9!)w>>+=O-|D)a#wS+rUuje%tk^xzAbg1=n6*SY@)Pd{K1CrvpC}FB&K?Y+fTV zE8xup)rnWd6Pt&;-3zROPkZx zJ!E%Z+5G>W?($g2)tg>t_|$m2b%%&fDquMG*TQ#kTgmSqtalS%Xa4G9-fq&61bzHc#%g^~uVIJ5y%`z(R-sg1)N<3i!_VHhPLb)E{Vd^j+d&?cc?Rdi z6O=q}vSu^07p*f1*>hOXTsGi9OU1mK#vQ@OrXJbs`~T5nlXbVAvn_bCx*}Tbz|6D8 zd?|Y_zF1JMBl+fL9m`$G{pSl8tef^dY`wbtL7SIOuAh?bMatH9b=d7XuydkC$h=2K z=Nirr?qFEH>5sa|#${<63YHvrl5i$&?lv*sNAKUhoWx~uPGA~$_?~jXT=iWFD^t$| z^*_8hZ$ZIEtqaAh64!5+FTZ)>=*{GLvvT)LbvevAIXS&d_W^%P*sfj&pule zyfdbEf&DkeM%%b!UFZL=dvmJ)^;JjdKRZggr`R2QGrr#rlfo~a_0V&ZHjGJOe;+&1f0ZP! zq)NR&RMV%oqUQZagtNlryfyOs^jL2+UY~a_`Iqo#l|L4}w|kNx_(mSugOhUugOwZhMWncFWi{;xJ_UQrdU^nB-@bn^+*KFqp(^5S)i+3U(5 zE$KbunsWH?Pk*0_6*~GKr$nob@+GgA%D(_qVX|ELS+^Wuo)WJ7xal!hOGXyK6_X3vH|Ya#XXqfOpHhwC2ZM zXJvI1r)~^>Ts-Te>ulDd-;#&&9)5oIkV9fxWed}$Yrf|n7#(!KR(@`oVB1BB)wN$M zy>ff4c}*Bt-0%1w>XSTZy+7yE|9xCHiac|Fyo#72AK+Sb$77M)ml-?^mplHwSuUb} zXR5_QyC*A;a(-9!I9IYmUjB7MZoIa{GL^fp{;uACvw~ad)<%|Z+b#M3x-WJ5RvY@~ z?fi!k%0+ok9bU4`;jwGC-4L(QbLrH=#@u(O#oIkt=X)k^{@f|<$*?baXHaU`64At6 zkC+bUwbq%1biAGLSirgd+LnXzf!imqdHwPEu7x49-s^oTdAVLOkG*xlsr7Etwg0Qc z@C*9Z*GsjfFS%B1vb>-3>jU+-Z~B(}csJ$AqkH={&$%ga?Ny4ma`T$)=a2uqqmz-% zVab_xTj<~Y7!L2H`P=3TdFtFa^ka`L=RW@PW>dUE84k`ei_~5IxaQ64m%ba^AEf*i zxYmDHK<@6$6(6>h`TdVP$+Dq#5*zn6Z~erDlY15G6U8GJYP#>pUH?_${O7>6Mj8qg z3BOp2UVK!2#$U_yqPsB0tU~Tv-{k$vC0^*?IX!Q!yMiFY!7Fl0KgpW0m|jd;-FIb2 zV$}S#r{X`HzjAE-DV^XJwRN+M7rdCcZR@A$^R$gMTpFzw6i%v2UtGD5(eGEdXhBI= z$N9x`GK%>^tkl=aZaQn)8W?^zq_OeGcfUP<%mZC#-oI;^R%!j3B{?>o;oPK}#eP*^ z;?(la-8|uXqG8>Hy)UP||F-Y9veAuCoxj?B^n;YrkBaTi-(X>pbGclT<)!u8z@x9P z9_%l7+g-fuR&~a^9U7_OyAEmTDGPkjod4;xe97;Q1-q1k_vt;XjEhjNQGZ$1ronS) zLD1p5{1+O6?;4-}^;77kB9Ewm?7u%+XS4nndor7rmvij7mHg!JoTXomzmB~V`c!{+ zR?*w8SHd53nWem&LNY}+ak6Fhov#&Mckzpk^kDIDc@s*7T%n) zpu?(UY{UFT2ONX%qXJ;sr+Q7-A>l^MtG=Gd&> zzD+mb6yHzVrH9t@Ph2UGde&Me)%uTOgTJkyMZWOgj!)tdGuk)H`E&+eUwPQ(gW*wz zHwWr^@)ozqv{|rDpDm+S%dFbz6#kSar!z-&(zNMpK^{drYuPtFk$HXZEsLr|HtWs^ z9cw1tRZAj{X=_egd&WV0JwiNy6+Ee~ivN)vR*FtAKq=ez$#+-iR&{bw3`S12oU^! z>eWoYU7Ay3x~8q^lap-V5t#Dvma>)j?%huh_?{>=x8qLm{?@|89j{T@*4Vd3@c#cP zucHzbHQfqwKfQ~-X1)Ahz|lL-7DxZD&0MXtU`FNJwO90R%8CmmJm9*t_u5=3-w!Pp zt-q|TDgVt8YIvo8Lu8_VhKR@fLWYU@er6sExYtarFEA{NJ0)To>|*+u#b{-k<`w?> z>uEPH?-6=MltG1J0l%ozd;@A0Rm=9T)UMr=3xH!I-R z*B54Gjq`t;TOuFL-6!Hd<(6;MbIuojg-TN@X6@d4V!PhM>yiKVopzicTIPOc)BpA> zu0h&=(v2y_=5 zc7L1~eWpje%=kl5UhLMZvbULMOj$N>Tg99ux~01lBeEK^cK+NqtMLv41BZZxvu_rc z<{|&uKrP|T7IJ1=`E=Ep&NrWVmLmB6kZs|G=WW$#%lXuk`2zM$QL!tNwcw07#-i9~ zV$7=~r7Ut@**M34NA+20)+aM|D7#FMnDOZ{gY4FecTU>nMfl1u-w}A=`@R_cjgr@y z7nJ^~l$mL_gDqh3ujbr5bt7Nl_PWHEyrr2%jRDFLwkf~kMBcW%H1uYjE)|p3I`4Oc znEyNH=lAv5fA|-?c%{SjtC!*C0Yh#r$9vNpzJFDi8*_7dhTfk}1KWQlJ2rFHhrWMw zOQVgYV~TM?ca(jx>YbZ4i*i9TcC9sIdxn)xC~m`TJ`XwPQZa3yCW*)6l$o@1Of4X4VDEPo|YG>#%jV zneBWpxw7c|y+d#9I`+P}ZF>CkgUJPU7iSA5zY}qBefaQ3w!5Zz$ZF$9<=PVja$RSZ zd<_(u#5pHSs_~Sgx{afDQq>!mPW!i^lf;XA;{T>APkXELaZ7M;@X6T3MP2T??YEos z&Um*qdw#Y!ptB=$=V9a21DsK8dc_VmOSIUeOB|W^SIUc@JCaL& z%w7@lq`M)C;i;go-h#pf%vT?D@iw>$OsanUZn{ADu4NM*ZrEeO@@;1IpD5S7br+uT zF?4L`xEy^j_^zlF`^0z0zWc}3a@3#L6S(zD#^NC1^ya%-hpYn_T?5swp79kIzGzdI z6r%axlH))@(<$wx%aj6gdle@a2_C$1Am&=s@_;+ZcjjMPS|NADN-8^!W&4+&m~P(_ zo#I&*YYy$nX4hWzZI<8587@q3o#wpSxIAI5)4q?kUrpz5ZvAn1uZ4)u`bN*aS*9y$ zEOg%amtWu3^jl0`ZC=Fd1a_U^#!%yuomXV%tov>EnfZEi8gHG-PdVjx+*dqTFt=Rt zk(Z8S*~G5bvnGDysf9eh*%QK=%56-uKl$X&+#{Smx){PXO|fZs@!CG($b}OX zK_!lg;mzh>cd1>P_W5ng?DW5p5l*!&JJa|~Z(2CSJoT7Wf6V;kvt)7GrN$kz^pmFr zKh<8#(i(MoyZSSOl)W=A#4HZhpDDZjWyhC;nwl=F7x0&CK zVnKVy)thuq7Ot3Q z5iQ9wM`T9)ohn^6rv=Z1=YRL=(2bh*Ra_w6RY3C|tJ6`9r3K5}ws9eZ1wV0L&_ z*O}wa-S?$t2zJfxH5H9zGn`X$adj>0#5GPUC2yIlD`Y*HR{7F@&Og)LCqEQz)vT<2 z@lq$jbw=9jAG;m-_gy!8e(U3os`it+|9-!_GHl)XsKo7Y=d9N^H}wZcbt3fxZ>~lFCN`0MLXHO8V}C*=zAWWHS7MGv#pcA{qpMwZsvb_ zZ$_o)PL8Tg%|1@rQT6|2p)>61)ik^8%r@|G<#kXFr zPB9V4-gItx@>QoQ>$9h;=k3ki8M097vHkJFD8c=$tB z?6E&xy3-xOXc1^80RYTE4H`=SlOq$X(_v zr@lWs)6kc=a*F5{2j_-Y4o?-kdfm?)IlFbuMA?v%T`Cid=3JPT?7i>S%i^+hCFjqp zQg*Ll;@p1re1!UrZ<;HWWh~M|8VZba3pd(j2|d(zcrLdmEt)%@wN=w0K635xo2UIg zHx-@pJkkDOL2_EybK^HB@4dbJP*-bqR^<67{tuSKhk9Qxl`H)@Wt-kAd6g}b60@c( zWHo(jd+(KvM~=#c%@^_*XQe&anXLF@e$pGaDNE8r%%aoZ`E(d~Pg0q4`PebG%MFL_ zsjGZ@`f7RUlZ4LR{|9&9@7i=d4!8a?OixjyTa?CvW?)-wz~y=avY z?=C;(9`{A*V`=>+H=!BRrvzsih@PF#7*jj#=-I3@ziytn()VoVfgd3SthqAlGG?Fk zh|<3i^~Te(oX_jdgSl&4R;&+_Tokh5*TwLKFAJab*iY&#Y;+Ua*jg`P8(C6b6W}1P ztt-?0>AYEvh5x!RtCb6ceJkg$n0COa!!JKl@yO>dWw*_Wc6QvjS8w%Q_udMvFXCOx z{4SsY{u^|B`BNkGt0F+^zXhXUq5R{QGjd<6o^6@>@5|?x@+qru&J@)!`gN zR9cR!>+=;eUR*ZYz}WO9IrIOmKX>ZOjgq~>|IP6&scJA1xM{(#^#uE)w%nZz+IkHW z{(0GK)pN`-+mvl7^NufM`Tt4}y(lx@SFJBzPSiYeqE5_u)mNTBo2%MXqok~sV8>ra}3E%%>bG`Y7zdHI=h zVf-@FZCjfzThz$*3zW5<5Pa#kXkxF|ufrVLMS80Hm^@>85?)zXHcl&3c1!nY620qn z%9VZH`@?(}vMwq7nPc>B|Lg0e8_zi&c{^osXLPc${3jjb64NJwuJ^VvTub`Lru=^D zZi{z~66&oTdpRFm>$q+PHzN@fN%&70@-I6mK^+Mcs+B02`dbmW% z@ICvEjjN5immg@6e&%-lrc!i5n)Erd!wmcM7IZ(13Dmk+$D1Nzaxql&tZzj>#){C5ZM`QLPSCsh1V zzINZ#x_QNorT?E?mkp|YmvI00?LMwdk9!Q4jg`ad?tNN3&703Lqo(ZPXQL(a?`Ax> zslydy(Y^Nx%S$uiezuZ&I~P}077fnpN7!ASs-G&g&2G-+tO|5}6C!Z>pVSZWUH_lV z+$!OgyWP;$FVpI;{-RjUKVHiYE|Dp1<`a4EpCq&Ds9eF3>{sf#KR@UgO**qY_YMd5 zER)l+%f6loSN>)y)mu2DDA~U9YF68N>(}2yS4vz8USNOakw)?!*{%zTz9qXeCL1d@ z=+9P4&Hun`_4af6rh7^6CaWdcz9?4O{V7P%t99v=ETPFlvgZy3y!fXUc$rcA^P3G0 zc?=9`QQ7x*JxbFtDWCFlTTd_RtB6LCbv1uQgD$B*zY-PxPei$Iud9RFt)piYbN@6- zoPHVcW#`|jz(%R{f9{$|8Z=*=bIZPCby%RXL~Go;DM5MaMMv9=r{0;X@K7~rrfJy6 zm7kS+kMW->2#{L(Nag>=!@B-{*T2s>R`lb;xyr-atX;KUd!+EnZZG=z^or1C->4Mh zn6g{{UO$X!T;|5?+5chsK^~E9KTrLS-|-rA$PVEmSICuIJ4t){R$su4GX+dgv5Tq3h~(v8cxS5!|#J2(W1ygAl2lmA@>$F)jn z;nqOcj7q=vI~}}(n|v!9i}&mCS^xallfb(AU52Nu?+VAXJqi=d4cYn2xMI6^1~0n$ zwc2b>^)feoqraDrJvrkd^EOJ-cVP^ltdqkIIlb zv-tJJbybD0&dBzDZTr|Y``F7D*6WJo%frjWRRO*=Q1bgM@#is{y>tHU zU(fH?ve1?BO~RVFOD;~?`rvQefttRgjc(g(+aS|?b3SMLJ7azW!_wKVv0U}P zy0za*t-fZaq48|CXHx#3Pg^;fluqxw>2;K))b>D9R6^0(+bhn!DBd`0@hORwPC@U} z?lsGL9?Pz9X8o#lbmms$G99_{y*ZBzqeH@P9RK~h>bSs^go^D<9Q*!H)_HOk{|C+x!H+Weo9lY>gX~vTi=O*@ZZ#%qsm&^$^M~AN; zXD0b)H6LU<-x@S||EfJ3M2_foo@aA=ufIQUW9sp_J^{|BjtE4wEL3a_oTaj3k=5ex z=VBM?Y?4+iW7BN6`R?$};$u$71fI1OYCbyZZSWnl@?;mUuL~{ z;%xbp>C)`I4v~V=GJ@MaH0Yi`nC9-o^G5Err%AzSN6m7R&Zsrk5$k(bXMS#yFnxBp zb>|zyc+TvdwFUdn|NS2RYmd3aiPL}ISqF=>d=xvDzk8|MmEeM_GTVO_c#C^}%Ur=; zyez=}cjbq#6NFNJ=jERJXm}|0d81p)i?r?(jSQFSbvw0WT4$V!TIldmBzO()(}iIZyeBrOgA=i1%4D?n->_{r^iX z;d7@tOOEgXgX%4IH&=vu*-Y3!eO(CehLeX`OUsQ^79LsCZ1r!seW!8JXs;C1xnQR|6+6`O^-SFHZLAUS!joF6UhL?^Tm^AHA8p z=I2Sp&#U5V?f*ZDKN>fwa-TtGNwXH?ZT@<{GfVPU^jc^Cu07UTF~9zeypV z{kF)}Ee+kWReH*!U8QE0B7t85)`V3~En&Zr^32in#C8RNp9cS#1WVe?rhRv;S$=NS zANQ7j)v;~t$G@D}n8G|I|Fe|uI&smP*1SP#@7`W6+Pd(*g5n&9>Z3c@-mF{ddDkR3 z?MHq8lWoZj4J#RceLC`U!vuS-2F<`P$KK1`o7c7Y$Qn~Q&CiR!C#B1Ui*>fY*cmze6l@6`?FAnOz0c7b z@^ZS=e@SJt&Dmnbq!=B2+#p@OYucN)hMS)hZnar3If!NV#);ato0jh?TH$)^tF_J3 z``jE4HU=(l3D!yZe?tG|!@4J(FJ8^8{`esI*)DmB{d;65%#UV}{5-i|s=_mR!;&L8 z_B@`K-P$%Q=fte**SPvFFEQd;#@nEEM+E%ktyJ&HTU+kzR%!fPS5@?5!f`9_$t#mS z)u?gCNFT|59C2i4<8TkNNa1;7Q35t>(gdy?r%14*ZTkA#kI3ibYaw(4J{w=|X&K zTf4SYxgXykAj@*N{)BJ+lDc)h_nYpD#)S7@o1pVlPwt-NiH$B3ESV)rrM{)CV>|oF z-M#WlRZR2o@B$J3`W0Kn1Me8WZMK%&lCFEy>`BcePnnv;X;muEDt5g(+SzVoS6Y2| z(#lH&}2ANAVqxRMr})EZy*aecJpsBkeEUM;n-SCWc=6YG1)p zw82$kYtHS88>Q=hKVah9&iau{d))-~6;Z|Edrf~#&QX1K^>jkj0iSFJ*GuMRR&sOY zSS#f`Saw*mPFAPXX3i$(o)xn;xhf_-Wyj>{!sW=S1s%5j2Zl`t2g{*U;pM_ z&-*2l{0_y$@gA{gvZ!u~Rf&~O`Ecw)-rX3Tw_i9VW<6MHuC&{}<7BJN=dj7%p(o3> zx&8Ef@#Zc!_ujx_ZHB^^cOEXC=jJ+(lWj&znz&t-yMX&b&HYLkSTHdTj2W;pG; z61V8X)xCU$d&|Dww_EDAd*|F%-&W>{QvY86J@a6{$c|NC!GtHcc5#pg=J4mMuc;r%@2p+l;fxw*}6+1&oU zwzitf=9>O_%wq7-v1o0$ujSEM3Eg>-7B%zkKUCbf@|3as+wMSbr=5PMJ8aLd&5tgx&3laKB&2H(7Mjh$nK-%bXJ{4trv&y zFLpb7PCInN{f%mR%{*1@6BH9Uueawv5a7PdogaHw@Wsi+3JeSkOtWI;BooBVj(yp< z>Vi_@=k0gX_pcR;bM$DL`%6fEQ^|9!^Z%|_^zPo*m|%3L37U2sZx zXNciue#@wBmJSjI(n?B8*9I)$}jL$D8aUWK5OL%xrV=j*K6#}j~jo~@bG1Aes&nSt&wYd9rRq7S8nj;=<~r z=D_(m>$Cr#Ss=Hmknv)Sggg6&v-)Wwd)JDD|CzzyoI18ho?%CogH23P85u=#K5 z^!4nmyVK61xS;X%qb+_HO&u!B1M_#s%%1dJ>}*K5w|cL~E~T0u&NE{7#&GO(G?rJ< z+F*Bxv-e^N=c+ZP(fZQbC$~f>42jdtP_h87!Zjlx$#CyzzKYyPL_k zg`1tDJ-*#5{BUj2tO?1^>C?Cbn_9kY`1$Etj}3;G>BLHOI|Qm;i9D-yJJXWTv{%UQ%LmSCe;zfCag^Yq(GJ-nIp zgjOB$T4YgB@#^^f4Z^$nZ4!+27Vb$t^rrm+lkD41@#=s6R$R4yu=e2F{sni$KRtCg zrPVK2V0`7uktHXJxg#3RRLcdt4P<_{s_9?B$E#c2D)$+`ak|O7bC-;T#>8KFDb`Ys z1{RZ5ChALyOqV&h>U>VYT=$5nDf_=Hb`zYqnNKQSr)*EnPlsc>j#%ekR0)at8#ysn z?yq@UnTz%9qUdQKCcNGJlKE8U47M5(WA#HjTjm~7nfUqV&xO0@MZc=&n|uA#LDSR6 ztS>Yqs3Q238uMOev{6xpS)u+3-=Wkwk;ixKdsPf{va~vp5-*39Xp+G z&b_A$p5p@%0l|)*ehdr@7EYeN%x^&=3<3-Z3=9m63`{T@B*&rwmg52CNc#_9Hdqgf UGnma6kdzwED8RtRkOJZZ00(wYqyPW_ diff --git a/doc/qtdesignstudio/images/studio-curve-editor.png b/doc/qtdesignstudio/images/studio-curve-editor.png deleted file mode 100644 index 01cd7f5ab4f8bbe47f47dfde94307ecbc290a7ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20623 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 W{<(*IBp&F1Z1HsUb6Mw<&;$S`&`Nm# diff --git a/doc/qtdesignstudio/images/studio-curve-editor.webp b/doc/qtdesignstudio/images/studio-curve-editor.webp new file mode 100644 index 0000000000000000000000000000000000000000..79624624d1e7fd8a7a98d33b538e6339d5f3d309 GIT binary patch literal 13526 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/studio-timeline-empty.png b/doc/qtdesignstudio/images/studio-timeline-empty.png deleted file mode 100644 index 861a1021bbadffecfda66b546d96945ede6f5fde..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3423 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 diff --git a/doc/qtdesignstudio/images/studio-timeline-empty.webp b/doc/qtdesignstudio/images/studio-timeline-empty.webp new file mode 100644 index 0000000000000000000000000000000000000000..16aa728a523dbbfa7d55f1198176a77d726d6d70 GIT binary patch literal 2238 zcmWIYbaUIr!N3si>J$(bVBxcpgMmR`rpbvxoMrpA+|a91&0IRgFDIp5oOK6z4SV_ho#*M+Qq zKV3^{idNqFkBfQqbuJeVxi|eE% zUKcHF)-P@OIPEV(=hm&;bB!6?Pq5`px^%}SZHmtWFQeWq6-&-K*WCZ!yrjCdq2r%- zlJv=mOCqB8`pt~ZEfAOe`u?5d+_~+Yv*cdBoObggBd@Tjh23>)vxN^ANu&xG@SM@f zII*HNctPWT){m<_{^}|PR{G}6V$xHy3jIAvYmxgxi79Oznl6Hg5lxZDvMwztJFk^i zpQ`fSXGv#pe9h6S|EiN++@!gbS50!c8Oy`$;VZ|Ww8-n|J5kS795YYY9iQ1_I$2n| zi{*T1W>Vtet`mvR&p+x`JGbb$W!ddp!JYUlydoLxgY0Bx!%dRc#$E~)s zh|ITEIyp-DMC*i4j|GA+x$pY$z(9P0$O~`-jnXCKbFrima*!NIG;JkM)R5CoYRj~F5D15x5%R^kdtq9P~X%rrv4l|@GX&p&wvgIDol@`{TW{r?xT!j!|{!sF1D z2U~yLRbA)Iy6D;7rPUrRLN8AKZNENk-NnDlO*<~c#BR#wbv*Rhx}>zT)9k^4%=aGI z#lH^r1lIq8FOU{hK}YdG*qT^B$LVbjf(7h~H_p>$`vR^O@{*bM;pVI7tMW-Bnof@I-UP ze6Avu^PIn|&u6t4Z0p%rB)>W0yM*6an|o^>8X5QRZ#=wEkuQPiPrsar!sO<6a~ilT zJ9#F4l9;SLDbv|OWR3Y(mkS-M9z^9`tK}ECBEHlmS|%^*#^>DhIma`cqc4lDiAx;EZmOs)C~}`FztOI(+7G{_l=FV(EF6yZ1zvTJB_qudbR`l}-F-Rpy?WTafhJ zPA>OB-IoRPT`OBHgo2GGHYf>ji!xY#ex|80obrg|!uARdN(E z6qi^|+8pfM@ko1#yQId`7>%s#4IYXt#RVJOWVk2K>|xyKv9*16S{g^-o|aPXn;J`_ zXFt?kW_R$+%Uc)Seuv-w zqu+Lu&NZ|dU#yq^_j+sBrx$UrZp&Y~cxIcjO|J9)%vB})jBn3soY=rQxrNiF;62;d z>Z~mnR~(<*`6m9<4wEwOnwM;+**~#5C@pe!DVX#A?VUA>#>_=#3N6kl83|JCu7x*b z!bI8^i5=BU5PW>lQSr-cHJ|SbLMClaWj+6@NciJQ@1q-Duo#8wX#KI?e#z;a-?hCi zo?m{it2*&MFJ5A8%?_7|(w`zT-_);`t5oB22@RAnYZpD57!bWvuI%Q~uKLSUUdr*U zpXsRL8P}<{XvV`T$<)~^LbR{4Sm$INk9*XQ;Xd%&hRAwL362_#om7bSvJ@TRh^MgNJeH8p$yZ=%~!jvx#-Ad+P zL=_y;*j)29_}mf=+t;&9(m5y45ZV0ll5N5%)r|PhOa1FV$W5u7w25sMpXOo}^QX~; zyS{a5RqoiA8XENdW1V3_8r##HCncAsTU0e4mDv|`C1%dN^-9N>o1H$G`%Zm#uvvD` zuP2?$C;bpRSg_4zk3x#xRBe-26Rh2*KC#&U_F>(Lix}<l5?jz0>Ki=jorNZl^4} z-nDC|iu8>B`90yYxtwh>_S{_OXZLeP-HA3k!%Ykq>Zkm=xkt@lX0COh=-$sCCU+iP z*;a9(V)Nq3{&w^3YfX&h)wNm1bNdF1nsJ1uS3~55oUlh%m$6k`IC3D?cZrMe~S* zi)raw{p@~5)SYlU-1jH8Pow|j`Ddn$C)f0rH1~=baL;!E^>kl{TmMWd*H&3>_6SX~T&*8UWrN!QVQ@*afYwK@#t?{w);urfN(4%Mb zf<{DX_pkr8OvZ!D(hZH`%hxfty$=5SoHyi8%A|k$7S7}r*N<&$SF$lTzQWD`0Nsj3 AhX4Qo literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/texture-editor.png b/doc/qtdesignstudio/images/texture-editor.png deleted file mode 100644 index e8eecbd13b60a920b3f0fd6f5a719f7b8c914217..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 56206 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/src/components/qtquick-component-context-menu.qdocinc b/doc/qtdesignstudio/src/components/qtquick-component-context-menu.qdocinc index 7ab83cc737e..7fb95a9880e 100644 --- a/doc/qtdesignstudio/src/components/qtquick-component-context-menu.qdocinc +++ b/doc/qtdesignstudio/src/components/qtquick-component-context-menu.qdocinc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -26,7 +26,7 @@ \li Group \li \l{Organizing Components} \row - \li Position + \li Positioner \li \l{Using Positioners} \row \li Layout diff --git a/doc/qtdesignstudio/src/overviews/qtquick-production-quality-animation.qdoc b/doc/qtdesignstudio/src/overviews/qtquick-production-quality-animation.qdoc index fcf1a4c4559..068a7c140bc 100644 --- a/doc/qtdesignstudio/src/overviews/qtquick-production-quality-animation.qdoc +++ b/doc/qtdesignstudio/src/overviews/qtquick-production-quality-animation.qdoc @@ -28,7 +28,7 @@ to upload the draw primitives to the graphics hardware. The frames-per-second (FPS) refresh rate of animations is displayed in the - \uicontrol FPS field on the \l{Summary of Main Toolbar Actions}{toolbar} + \uicontrol FPS field on the toolbar in the \uicontrol Design mode. To improve the FPS rate, application developers should: diff --git a/doc/qtdesignstudio/src/qtdesignstudio-terms.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-terms.qdoc index 9c517f9846c..70ff56517cb 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-terms.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-terms.qdoc @@ -30,7 +30,7 @@ An \e asset is an image, font file, 3D model, or other supported file that you add to your \l{glossary-project}{project}. - \image qtquick-assets-tab.png "Assets" + \image qtquick-assets-tab.webp "Assets" Assets are packaged with \l{glossary-component}{components} for delivery to users. @@ -187,7 +187,7 @@ also be modified by another component, unless a particular component type has explicitly disallowed this for a specific property. - \image qtquick-item-properties-common.png "Properties view" + \image qtquick-item-properties-common.webp "Properties view" Read more about properties: diff --git a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc index 76779962795..d98beda4f36 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc @@ -16,30 +16,30 @@ \list \li \l{2D} \li \l{3D} - \li \l{Material Editor and Browser} - \li \l{Components} \li \l{Assets} - \li \l{Navigator} - \li \l{Properties} - \li \l{Connections} - \li \l{States} - \li \l{Translations} - \li \l{Transitions} - \li \l{Timeline} - \li \l{Curves} \li \l{Code} \generatelist studio-how-to-code \generatelist studio-how-to-refactor-code \generatelist studio-how-to-search \generatelist studio-preferences-code - \li \l{Projects} - \li \l{File System} - \li \l{Open Documents} + \li \l{Components} + \li \l{Connections} \li \l{Content Library} - \li \l{Texture Editor} - \li \l{Qt Insight} + \li \l{Curves} \li \l{Effect Composer} + \li \l{File System} + \li \l{Material Editor and Browser} \li \l{Model Editor} + \li \l{Navigator} + \li \l{Open Documents} + \li \l{Projects} + \li \l{Properties} + \li \l{Qt Insight} + \li \l{States} + \li \l{Texture Editor} + \li \l{Timeline} + \li \l{Transitions} + \li \l{Translations} \endlist \li \l{Managing Workspaces} \li \l{Manage sessions} diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc index 376f6bbf1f3..ebe842a5551 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -6,8 +6,12 @@ \page studio-3d-editor.html \nextpage studio-material-editor.html + \ingroup studio-views + \title 3D + \brief Edit a 3D scene. + When editing a 3D scene, you view the scene in the \uicontrol{3D} view. You can change the projection of the view by switching between \e {perspective camera} and \e {orthographic camera} modes. When using the @@ -25,7 +29,7 @@ components from \uicontrol Components > \inlineimage icons/plus.png > \uicontrol {Qt Quick 3D} > \uicontrol {Qt Quick 3D}. - You can use the \l{Summary of the 3D View Toolbar Buttons}{toolbar buttons} + You can use the toolbar buttons to \e transform 3D components and manipulate the 3D scene. Transformation refers to moving, rotating, or scaling of a component. The \e pivot of the component is used as the origin for transformations. You can set a @@ -56,7 +60,7 @@ \li Delete components \endlist - \image 3d-view-context-menu.png + \image 3d-view-context-menu.webp "The context menu in the 3D view" To refresh the contents of the \uicontrol{3D} view, press \key P or select the \inlineimage icons/reset.png @@ -468,125 +472,4 @@ Select \uicontrol{Reset All Viewports} to reset the shading of the scene in all of the splits. - \section1 Summary of the 3D View Toolbar Buttons - - The \uicontrol{3D} view toolbar contains the following buttons: - - \table - \header - \li Button - \li Tooltip - \li Keyboard Shortcut - \li Read More - \row - \li \inlineimage icons/select_group.png - \inlineimage icons/select_item.png - \li Toggle Group/Single Selection Mode - \li \key Q - \li \l{Selecting Components} - \row - \li \inlineimage icons/move_off.png - \li Activate the Move Tool - \li \key W - \li \l{moving components 3d view}{Moving Components} - \row - \li \inlineimage icons/rotate_off.png - \li Activate Rotate Tool - \li \key E - \li \l{Rotating Components} - \row - \li \inlineimage icons/scale_off.png - \li Activate Scale Tool - \li \key R - \li \l{Scaling Components} - \row - \li \inlineimage icons/fit_selected.png - \li Fit Selected Object to View - \li \key F - \li \l{Controlling the 3D View Camera} - \row - \li \inlineimage icons/perspective_camera.png - \inlineimage icons/orthographic_camera.png - \li Toggle Perspective/Orthographic Edit Camera - \li \key T - \li \l{Controlling the 3D View Camera} - \row - \li \inlineimage icons/global.png - \li Toggle Global/Local Orientation - \li \key Y - \li \l{Using Global and Local Orientation} - \row - \li \inlineimage icons/edit_light_off.png - \inlineimage icons/edit_light_on.png - \li Toggle Edit Light On/Off - \li \key U - \li \l{Using Edit Light} - \row - \li \inlineimage icons/snapping-3d.png - \li Toggle Snapping During Node Drag - \li \key Shift + \key Tab - \li \l{Snapping} - \row - \li \inlineimage icons/snapping-3d-conf.png - \li Open Snap Configuration Dialog - \li - \li \l{Configuring Snapping} - \row - \li \inlineimage icons/align-camera-on.png - \li Align Selected Cameras to View - \li - \li\l{Aligning Views and Cameras} - \row - \li \inlineimage icons/align-view-on.png - \li Align View to Selected Camera - \li - \li \l{Aligning Views and Cameras} - \row - \li \inlineimage icons/camera_speed.png - \li Open camera speed configuration dialog - \li - \li\l{Using Fly Mode} - \row - \li \inlineimage icons/visibilityon.png - \li Visibility Toggles - \li - \li \l{Toggling Visibility} - \row - \li \inlineimage icons/3d-background-color.png - \li Background Color Actions - \li - \li \l{Changing Colors} - \row - \li \inlineimage icons/split-view.png - \li Toggle Split View On/Off - \li \key Ctrl + \key Alt + \key Q - \li \l{Using Split View} - \row - \li \inlineimage icons/particles-seek.png - \li Seek Particle System Time - \li - \li \l{Particle Editor} - \row - \li \inlineimage icons/particle-animation-on.png - \li Toggle Particle Animation - \li \key V - \li \l{Particle Editor} - \row - \li \inlineimage icons/particle-play.png - \inlineimage icons/particle-pause.png - \li Play/Pause Particles - \li \key , - \li \l{Particle Editor} - \row - \li \inlineimage icons/particle-restart.png - \li Restart Particles - \li \key / - \li \l{Particle Editor} - \row - \li \inlineimage icons/reset.png - \li Reset View - \li \key P - \li - \endtable - */ diff --git a/doc/qtdesignstudio/src/reference/qtdesignstudio-keyboard-shortcuts.qdoc b/doc/qtdesignstudio/src/reference/qtdesignstudio-keyboard-shortcuts.qdoc index dd69b68ecd1..21b195b5224 100644 --- a/doc/qtdesignstudio/src/reference/qtdesignstudio-keyboard-shortcuts.qdoc +++ b/doc/qtdesignstudio/src/reference/qtdesignstudio-keyboard-shortcuts.qdoc @@ -204,7 +204,10 @@ \li Action \li Keyboard shortcut \row - \li Open the QML file that defines the selected component + \li Open the project with \uicontrol {Live Preview}. + \li \key{Alt+P} (\key{Opt+P} on \macos) + \row + \li Open the QML file that defines the selected component. \li \key{F2} \row \li Jump to the \uicontrol {Code} view. diff --git a/doc/qtdesignstudio/src/views/qtquick-assets.qdoc b/doc/qtdesignstudio/src/views/qtquick-assets.qdoc index b95bf1e889e..fad871debd4 100644 --- a/doc/qtdesignstudio/src/views/qtquick-assets.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-assets.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -6,43 +6,31 @@ \previouspage quick-components-view.html \nextpage qtquick-navigator.html + \ingroup studio-views + \title Assets + \brief Select assets such as images and fonts to use in your application. + The \uicontrol Assets view lists available assets. \uicontrol {Assets} displays the images and other files - that you add to the project folder by dragging-and-dropping external asset + that you add to the project folder by dragging external asset files to \QDS or by selecting \inlineimage icons/plus.png . For more information about importing assets to \QDS, see \l {Importing 2D Assets} and \l {Importing 3D Assets}. - To add assets to your UI, drag-and-drop them from + To add assets to your UI, drag them from \uicontrol Assets to the \l Navigator, \l {2D}, or \l {3D} view. To add multiple assets to your UI simultaneously, multiselect them first by holding \key Ctrl and clicking the asset files you wish to select. - \image qtquick-assets-tab.png "Assets view" + \image qtquick-assets-tab.webp "Assets view" - When you drag-and-drop assets from \uicontrol Assets to the \l Navigator + When you drag assets from \uicontrol Assets to the \l Navigator or \l {2D} view, component instances with a suitable type are automatically created for you. For example, instances of the \l{Images}{Image} component will be created for graphics files. - \section1 Context Menu Commands - - \image qtquick-library-context-menu.png "Context menu commands in Assets" - - To use the context menu commands in \uicontrol Assets, right-click the - name of a folder and select one of the following commands: - - \list - \li \uicontrol {Expand All}: expands all folders. - \li \uicontrol {Collapse All}: collapses all folders. - \li \uicontrol {Rename Folder}: prompts you to enter a new name - for the folder. - \li \uicontrol {New Folder}: creates a new folder. - \li \uicontrol {Delete Folder}: deletes the folder. - \endlist - */ diff --git a/doc/qtdesignstudio/src/views/qtquick-components-view.qdoc b/doc/qtdesignstudio/src/views/qtquick-components-view.qdoc index 1e2786bfbc1..e956ee86ad6 100644 --- a/doc/qtdesignstudio/src/views/qtquick-components-view.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-components-view.qdoc @@ -1,13 +1,18 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! \page quick-components-view.html \previouspage studio-material-editor.html \nextpage quick-assets.html + \sa {Using Components} + + \ingroup studio-views \title Components + \brief Select preset components and your own components to use in your application. + The \uicontrol Component view lists the available components. \image qtquick-components-tab.png "Components view" @@ -86,27 +91,4 @@ final application package, it is recommended that you select \uicontrol {Remove Module} to remove the ones you don't use in the project. - \section1 Context Menu Commands - - \image qtquick-components-context-menu.png "Context menu commands in Components" - \image qtquick-components-context-menu-hide.png "Context menu command Hide Category" - - To use the context menu commands in \uicontrol Components, right-click the - name of a module or category and select one of the following commands: - - \list - \li \uicontrol {Remove Module}: removes the module and all of its - components from \uicontrol Components. - \li \uicontrol {Expand All}: expands all the modules. - \li \uicontrol {Collapse All}: collapses all the modules. - \li \uicontrol {Hide Category}: hides the category from the module. - \li \uicontrol {Show Module Hidden Categories}: shows the hidden - categories of the module. - \li \uicontrol {Show All Hidden Categories}: shows the hidden - categories in all of the modules. - \endlist - - \note The context menu commands for the \uicontrol Components categories do - not function if you have entered something into the \uicontrol Search field. - Clear the \uicontrol Search field to resume using the context menu commands. */ diff --git a/doc/qtdesignstudio/src/views/qtquick-connection-view.qdoc b/doc/qtdesignstudio/src/views/qtquick-connection-view.qdoc index 5cc2c4a6670..066ff1c9dc3 100644 --- a/doc/qtdesignstudio/src/views/qtquick-connection-view.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-connection-view.qdoc @@ -1,13 +1,19 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! \page qtquick-connection-view.html \previouspage qtquick-properties-view.html \nextpage qtquick-states-view.html + \sa {Working with Connections} + + \ingroup studio-views \title Connections + \brief Add functionality to the UI by creating connections between components, + signals, and component properties. + The \uicontrol {Connections} view is a collection of views that enable you to create connections between components and the application, to bind component properties together, and to add custom properties for components. @@ -36,29 +42,4 @@ {preset properties} that you can specify values for. You can add custom properties that would not otherwise exist for a particular \l{Component Types}{component type}. - - \section1 Summary of the Connections View Tabs - - \table - \header - \li Tab - \li Purpose - \li Read More - \row - \li \uicontrol Connections - \li Create connections between components and the application logic - by accessing signals outside of the components that emit them. - \li \l{Connecting Components to Signals} - \row - \li \uicontrol Bindings - \li Dynamically change the behavior of a component by creating a - binding between the properties of two components. - \li \l{Adding Bindings Between Properties} - \row - \li \uicontrol Properties - \li Add custom properties that would not otherwise exist for a - particular preset component or your own custom component. - \li \l{Specifying Custom Properties} - - \endtable */ diff --git a/doc/qtdesignstudio/src/views/qtquick-curve-editor.qdoc b/doc/qtdesignstudio/src/views/qtquick-curve-editor.qdoc index 88ba762f53d..e066b56bf17 100644 --- a/doc/qtdesignstudio/src/views/qtquick-curve-editor.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-curve-editor.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -6,12 +6,16 @@ \previouspage qtquick-timeline-view.html \nextpage qtquick-text-editor.html + \ingroup studio-views + \title Curves + \brief View and modify the animation curve. + The \uicontrol {Curves} view shows the interpolated values of an animated property over the animation range. - \image studio-curve-editor.png "Curves" + \image studio-curve-editor.webp "Curves" When you edit an animation curve, you implicitly edit the \l{Editing Easing Curves}{easing curves} that the underlying system uses @@ -40,45 +44,6 @@ and then select \inlineimage icons/pin.png . - \section1 Curves Toolbar - - The \uicontrol {Curves} toolbar contains the following buttons and - fields. - - \table - \header - \li Button/Field - \li Action - \row - \li \inlineimage icons/easing-curve-linear-icon.png - \li \uicontrol Linear specifies that the interpolation between - keyframes is linear. - \row - \li \inlineimage icons/easing-curve-step-icon.png - \li \uicontrol Step uses steps for interpolation between keyframes. - \row - \li \inlineimage icons/easing-curve-spline-icon.png - \li \uicontrol Spline uses bezier spline curves for interpolation - between keyframes and displays handles for managing them. - \row - \li \uicontrol {Set Default} - \li Currently not used. - \row - \li \uicontrol Unify - \li For \uicontrol Spline curves, locks the handle on the left of a - keyframe to the one on the right. - \row - \li Start Frame - \li Specifies the first frame of the curve. - \row - \li End Frame - \li Specifies the last frame of the curve. - \row - \li Current Frame - \li Displays the frame that the playhead is currently on. Enter a - number in the field to move the playhead to the respective frame. - \endtable - \section1 Editing Animation Curves To edit animation curves: diff --git a/doc/qtdesignstudio/src/views/qtquick-designer.qdoc b/doc/qtdesignstudio/src/views/qtquick-designer.qdoc index 3ca01494374..d4fb09efc68 100644 --- a/doc/qtdesignstudio/src/views/qtquick-designer.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-designer.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only // ********************************************************************** @@ -27,218 +27,8 @@ \section1 Summary of Design Views - In addition to the summary of design views, the table below includes an MCU - column that indicates the views which are fully supported on MCU projects. - For more information, see \l {\QDS Features on MCU Projects}. + The following table provides a summary of the design views. For information on + MCU support, see \l {\QDS Features on MCU Projects}. - \table - \header - \li View - \li Purpose - \li MCU - \li Read More - \row - \li \l {2D} - \li Provides a working area for designing 2D UIs. - When you are editing 3D scenes, the \uicontrol {2D} view is - used as a canvas for the 3D scene projected by the camera. - \li \image ok.png - \li \l {2D} - \row - \li \l {3D} - \li Provides an editor for files you created using 3D graphics - applications and stored in one of the supported formats. - \li - \li \l {3D} - \row - \li \l {Material Editor and Browser} - \li In the \uicontrol {Material Editor} and - \uicontrol {Material Browser} views, you create and manage materials and - textures. - \li - \li \l {Material Editor and Browser} - \row - \li \l Components - \li Contains preset components and your own components, that you can use - to design you application. - \li \image ok.png - \li \l{Using Components} - \row - \li \l Assets - \li Contains assets such as images and fonts that you can use in your - application. - \li \image ok.png - \li \l Assets - \row - \li \l Navigator - \li Displays the composition of the current component file as - a tree structure. A component file can contain references - to other components and assets. - \li \image ok.png - \li \l Navigator - \row - \li \l Properties - \li Enables you to modify the properties of the selected component. - \li \image ok.png - \li \l {Specifying Component Properties} - \row - \li \l{Connections} - \li Enables you to add functionality to the UI by creating - connections between components, signals, and component properties. - \li \image ok.png - \li \l{Working with Connections} - \row - \li \l States - \li Displays the different states that can be applied to a component. - Typically, states describe UI configurations, such as the - visibility and behavior of components and the available user - actions. - \li \image ok.png - \li \l{Working with States} - \row - \li \l{Transitions} - \li Enables you to make movement between states smooth by animating - the changes between states. - \li \image ok.png - \li \l{Animating Transitions Between States} - \row - \li \l Translations - \li Provides functionality to add multi-language support to your - project. - \li - \li \l{Translations} - \row - \li \l Timeline - \li Provides a timeline and keyframe based editor for animating - the properties of components. - \li \image ok.png - \li \l{Creating Timeline Animations} - \row - \li \l{Curves} - \li Enables you to view and modify the whole animation curve by - inserting keyframes to the curve and dragging them and the point - handlers to modify the curve. - \li - \li \l {Editing Animation Curves} - \row - \li \l{Code} - \li Provides a code editor for viewing and modifying the code - generated by the visual editors. - \li \image ok.png - \li \l {Code} - \row - \li \l Projects - \li Shows a list of open projects and the files they contain. - \li \image ok.png - \li \l Projects - \row - \li \l{File System} - \li Shows all files in the currently selected directory. - \li \image ok.png - \li \l{File System} - \row - \li \l{Open Documents} - \li Shows currently open files. - \li \image ok.png - \li \l{Open Documents} - \row - \li \l{Content Library} - \li The \uicontrol {Content Library} view contains material, texture, - and environment bundles with assets that you can use in your project. - \li - \li \l{Content Library} - \row - \li \l{Texture Editor} - \li In the \uicontrol {Texture Editor} view, you create and manage - textures. - \li - \li \l{Texture Editor} - \row - \li \l{Effect Composer} - \li Use \uicontrol {Effect Composer} to compose custom effects. - \li - \li \l{Effect Composer} - \endtable - - \section1 Summary of Main Toolbar Actions - - The top level toolbar in the \uicontrol Design mode contains shortcuts to - widely used actions. - - \table - \header - \li Button/Field - \li Action - \li Keyboard Shortcut - \li Read More - - \row - \li \inlineimage icons/home.png - \li \uicontrol {Home}: opens the welcome page. - \li - \li - \row - \li \inlineimage icons/start_playback.png - \li \uicontrol {Play}: runs the application. - \li - \li - \row - \li \uicontrol {Live Preview} - \li Shows a preview of the current file or the entire UI. The changes you - 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} - - \row - \li Currently open file - \li Displays the location and filename of the currently open file. You - can select another file in the list of open files to view it in - the \uicontrol {2D} and \uicontrol Navigator views. - \li - \li \l{Open Documents} - \row - \li \inlineimage icons/prev.png - \li \uicontrol {Go Back}: moves a step backwards in your location history. - That is, returns the focus to the last location in the last file it - was on. - \li \key Alt+< (\key Opt+Cmd+< on \macos) - \li - \row - \li \inlineimage icons/next.png - \li \uicontrol {Go Forward}: moves a step forward in your location history. - \li \key Alt+> (\key Opt+Cmd+> on \macos) - \li - \row - \li \inlineimage icons/close.png - \li \uicontrol {Close Document}: closes the current file. - \li \key Ctrl+W (\key Cmd+W on \macos) - \li - \row - \li \inlineimage icons/create_component.png - \li Creates a custom component from the selected item. - \li - \li \l{Creating Custom Components} - \row - \li \inlineimage icons/edit_component.png - \li Edits the selected custom component. - \li - \li \l{Creating Custom Components} - \row - \li Workspace - \li Displays the currently selected workspace. To switch to another - workspace, select it in the list. - \li - \li \l{Managing Workspaces} - \row - \li \inlineimage icons/lockoff.png - / \inlineimage icons/lockon.png - \li Toggles the views to locked or movable in \QDS. - \li - \li \l{Managing Workspaces} - \row - \li \uicontrol Share - \li Shares the application online using Qt Design Viewer. - \li - \li \l{Sharing Applications Online} - \endtable + \annotatedlist studio-views */ diff --git a/doc/qtdesignstudio/src/views/qtquick-easing-curve-editor.qdoc b/doc/qtdesignstudio/src/views/qtquick-easing-curve-editor.qdoc index ef518989ce1..66b54c0d55c 100644 --- a/doc/qtdesignstudio/src/views/qtquick-easing-curve-editor.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-easing-curve-editor.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -79,7 +79,7 @@ \endlist When you attach easing curves to keyframes, the shape of the - \l{keyframe_marker}{keyframe marker} on a keyframe track in + keyframe marker on a keyframe track in \l Timeline changes from \inlineimage icons/keyframe_linear_active.png to a marker that describes the type of the selected easing curve. diff --git a/doc/qtdesignstudio/src/views/qtquick-effect-maker-view.qdoc b/doc/qtdesignstudio/src/views/qtquick-effect-maker-view.qdoc index 734602cf1e5..f0de1a35222 100644 --- a/doc/qtdesignstudio/src/views/qtquick-effect-maker-view.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-effect-maker-view.qdoc @@ -6,8 +6,12 @@ \previouspage studio-qt-insight.html \nextpage studio-model-editor.html + \ingroup studio-views + \title Effect Composer + \brief Compose custom effects. + Use \uicontrol {Effect Composer} to create post-processing effects that can be applied to 2D or 3D components. The effects created with \uicontrol {Effect Composer} are shader effects, which can be used in any \QDS projects. diff --git a/doc/qtdesignstudio/src/views/qtquick-form-editor.qdoc b/doc/qtdesignstudio/src/views/qtquick-form-editor.qdoc index 3ac30273920..7ecd0a77dbf 100644 --- a/doc/qtdesignstudio/src/views/qtquick-form-editor.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-form-editor.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -6,13 +6,17 @@ \previouspage creator-using-qt-quick-designer.html \nextpage studio-3d-editor.html + \ingroup studio-views + \title 2D + \brief Design 2D UIs. + You design applications in the \uicontrol {2D} view by opening component files and placing instances of \l{Component Types}{2D components} and \l{Assets}{assets} into them. - \image qmldesigner-form-editor.png "The 2D view" + \image qmldesigner-form-editor.webp "The 2D view" When you select component instances in the \uicontrol {2D} view, markers appear around their edges and in their corners. Depending on the shape of @@ -25,60 +29,12 @@ \li \l{Rotating 2D Components}{Rotate} \endlist - \section1 Summary of 2D View Buttons - - The \uicontrol {2D} view toolbar contains the following buttons and - fields. - - \table - \header - \li Button/Field - \li Tooltip - \li Read More - \row - \li \uicontrol {Override Width} - \li Shows a preview of the component using the specified width. - \li \l{Previewing Component Size} - \row - \li \uicontrol {Override Height} - \li Shows a preview of the component using the specified height. - \li \l{Previewing Component Size} - \row - \li \inlineimage icons/canvas-color.png - \li Sets the color of the \uicontrol {2D} view working area. - \li \l{Setting Canvas Color} - \row - \li \inlineimage icons/zoomIn.png - \li Zooms in. - \li \l{Zooming} - \row - \li \inlineimage icons/zoomOut.png - \li Zooms out. - \li \l{Zooming} - \row - \li Zoom level - \li Sets the zoom level that you select from the list. - \li \l{Zooming} - \row - \li \inlineimage icons/zoomAll.png - \li Zooms to fit all content. - \li \l{Zooming} - \row - \li \inlineimage icons/zoomSelection.png - \li Zooms to fit the current selection. - \li \l{Zooming} - \row - \li \inlineimage icons/reset.png - \li Refreshes the contents of the \uicontrol {2D} view. - \li \l{Refreshing 2D View Contents} - \endtable - \section1 Moving Components When the move cursor is displayed, you can move the selected component instance to any position in the \uicontrol {2D} view. - \image qmldesigner-form-editor-move-cursor.png "Move cursor in the 2D view" + \image qmldesigner-form-editor-move-cursor.webp "Move cursor in the 2D view" For more information about alternative ways of positioning component instances in UIs, see \l{Scalable Layouts}. @@ -88,7 +44,7 @@ When the resize cursor is displayed, you can drag the markers to resize component instances. - \image qtquick-designer-scaling-items.png "The 2D view" + \image qtquick-designer-scaling-items.webp "The 2D view" To have the resizing done from the center of the selected component instance rather than from its edges, press \key Alt (or \key Opt on \macos). @@ -110,7 +66,7 @@ clockwise or counter-clockwise to freely rotate the component instance around its origin. - \image qtquick-designer-rotating-items.png "2D rotation tool" + \image qtquick-designer-rotating-items.webp "2D rotation tool" Additionally, press \key Shift or \key Alt (or \key Opt on \macos) to rotate component instances in steps of 5 or 45 degrees, respectively. @@ -152,9 +108,9 @@ \image qtquick-designer-options.png "Qt Quick Designer preferences" The following image shows the snapping lines (1) when - \uicontrol {Parent component padding} is set to 5 pixels. + \uicontrol {Parent component padding} is set to 10 pixels. - \image qmldesigner-snap-margins.png "Snapping lines on canvas" + \image qmldesigner-snap-margins.webp "Snapping lines on canvas" For alternative ways of aligning and distributing component instances by using the \l Properties view, see \l{Aligning and Distributing Components}. @@ -181,7 +137,7 @@ values are not changed permanently in the UI file. You can permanently change the property values in the \uicontrol Properties view (4). - \image qmldesigner-preview-size.png "Component width and height" + \image qmldesigner-preview-size.webp "Component width and height" To set the initial size of the root component, select \uicontrol Edit > \uicontrol Preferences > \uicontrol {Qt Quick} > \uicontrol {Qt Quick Designer} @@ -204,7 +160,7 @@ not affect the background color of your root component or component instances in any way. - \image qmldesigner-canvas-color.png "Transparent canvas color for a transparent component instance" + \image qmldesigner-canvas-color.webp "Transparent canvas color for a transparent component instance" \section1 Refreshing 2D View Contents @@ -219,6 +175,4 @@ To refresh the contents of the \uicontrol {2D} view, press \key R or select the \inlineimage icons/reset.png (\uicontrol {Reset View}) button. - - \include qtquick-component-context-menu.qdocinc context-menu */ diff --git a/doc/qtdesignstudio/src/views/qtquick-navigator.qdoc b/doc/qtdesignstudio/src/views/qtquick-navigator.qdoc index f0cd1fe2351..cfba22dc60b 100644 --- a/doc/qtdesignstudio/src/views/qtquick-navigator.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-navigator.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -6,8 +6,12 @@ \previouspage quick-assets.html \nextpage qtquick-properties-view.html + \ingroup studio-views + \title Navigator + \brief View the composition of the current component file as a tree structure. + The \uicontrol Navigator view displays the components in the current component file and their relationships. \l{glossary-component}{Components} (1) are listed in a tree structure, below their parent (2). You can preview @@ -36,57 +40,11 @@ you can change the source of an Image component by selecting \uicontrol {Change Source URL} in the context menu. - \section1 Summary of Navigator Buttons - - The following table lists the \uicontrol Navigator buttons: - - \table - \header - \li Icon - \li Tooltip - \li Read More - \row - \li \inlineimage icons/arrowleft.png - \li Moves the component one level up in the component tree, so that - it becomes the last sibling of its current parent. - \li \l{Arranging Components} - \row - \li \inlineimage icons/arrowright.png - \li Moves the component one level down in the component tree, so that it - becomes the child of its last sibling. - \li \l{Arranging Components} - \row - \li \inlineimage icons/navigator-arrowdown.png - \li Moves the component down within its parent. - \li \l{Arranging Components} - \row - \li \inlineimage icons/navigator-arrowup.png - \li Moves the component up within its parent. - \li \l{Arranging Components} - \row - \li \inlineimage icons/filtericon.png - \li Shows and hides invisible components in \uicontrol Navigator. - \li \l{Showing and Hiding Components} - \row - \li \inlineimage icons/alias.png - \li Adds a property alias that you can use from outside of the - component. - \li \l{Adding Property Aliases} - \row - \li \inlineimage icons/visibilityon.png - \li Shows and hides components in the \uicontrol {2D} view. - \li \l{Showing and Hiding Components} - \row - \li \inlineimage icons/lockon.png - \li Locks components in all views. - \li \l{Locking Components} - \endtable - \section1 Showing and Hiding Components To show and hide components in the \uicontrol {2D} view when focusing on - specific parts of the application, click \inlineimage icons/visibilityon.png - in \uicontrol Navigator. + specific parts of the application, select \inlineimage icons/eye_open.png and + \inlineimage icons/visibility-off.png in \uicontrol Navigator. To change the visibility of a component in the application code, select the \uicontrol Visibility check box in the \uicontrol Properties view or select @@ -99,10 +57,8 @@ component. To hide or show child components, edit the properties of the parent component. - To hide invisible components in \uicontrol Navigator, click - \inlineimage icons/filtericon.png - (\uicontrol {Filter Tree}) and select - \uicontrol {Show Only Visible Components}. + To show and hide invisible components in \uicontrol Navigator, select + \inlineimage {icons/visibilityon.png}. \section1 Locking Components @@ -141,9 +97,8 @@ By default, components that are located at the top of the file are listed at the bottom of the \uicontrol Navigator tree and behind overlapping components in the \uicontrol {2D} view. To list the components in the order - in which they appear in the file, as some other tools do, click - \inlineimage icons/filtericon.png - (\uicontrol {Filter Tree}), and select \uicontrol {Reverse Component Order}. + in which they appear in the file, as some other tools do, select + \inlineimage {icons/reverse_order.png}. To move a component to the top or bottom of the tree within its parent, right-click it in the \uicontrol Navigator or \uicontrol {2D} view @@ -156,20 +111,18 @@ \image qtquick-designer-navigator-arrange.gif "Reversing component order" - You can also drag-and-drop the component to another position in the tree or - use the arrow buttons to move the component in the tree. You can use the - left and right arrow buttons to change the parent of the component. + You can also drag the component to another position in the tree or use the + \inlineimage {icons/navigator-arrowup.png} and \inlineimage {icons/navigator-arrowdown.png} + buttons to move the component in the tree. You can use the \inlineimage {icons/arrowleft.png} + and \inlineimage {icons/arrowright.png} buttons to change the parent of the component. \image qmldesigner-navigator-arrows.png "Navigator buttons" - When you drag-and-drop instances of components to the \uicontrol {2D} view, + When you drag instances of components to the \uicontrol {2D} view, the new component is added as a child of the component beneath it. When you move the components, it is not possible to determine whether you want to adjust their position or attach them to a new parent component. - Therefore, the parent component is not automatically changed. To change the - parent of the component, press down the \key Shift key before you drag-and-drop - the component into a new position. The topmost component under the cursor becomes the - new parent of the component. + Therefore, the parent component is not automatically changed. \section1 Adding Property Aliases @@ -236,5 +189,4 @@ \image qmldesigner-breadcrumbs.png "Component hierarchy" - \include qtquick-component-context-menu.qdocinc context-menu */ diff --git a/doc/qtdesignstudio/src/views/qtquick-properties-view.qdoc b/doc/qtdesignstudio/src/views/qtquick-properties-view.qdoc index acde01a951b..2d660c99511 100644 --- a/doc/qtdesignstudio/src/views/qtquick-properties-view.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-properties-view.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -6,21 +6,25 @@ \previouspage qtquick-navigator.html \nextpage qtquick-connection-view.html + \ingroup studio-views + \title Properties + \brief Modify the properties of the selected component. + The \uicontrol Properties view displays all the properties of the selected \l{glossary-component}{component}. The properties are grouped by type. The top part of the view displays properties that are common to all components, such as component type, ID, name, geometry, and visibility. - \image qtquick-item-properties-common.png "Basic component properties" + \image qtquick-item-properties-common.webp "Basic component properties" The bottom part of the view displays properties that have been defined for the component type. For example, the following image displays the predefined properties you can set for \l{basic-rectangle}{Rectangle} and \l Text components. - \image qmldesigner-element-properties.png "Rectangle and Text properties" + \image qmldesigner-element-properties.webp "Rectangle and Text properties" \section1 Custom Properties @@ -42,46 +46,6 @@ \image custom-properties.png - \section1 Summary of Properties View Buttons - - The following table lists the \uicontrol Properties view buttons: - - \table - \header - \li Icon - \li Tooltip - \li Read More - \row - \li \inlineimage icons/alias.png - \li Adds a property alias that you can use from outside of the - component for the root component. You can use a menu item - in the actions menu to add property aliases for property - values of child components. - \li \l{Adding Property Aliases} - \row - \li \inlineimage icons/action-icon.png - \li Opens a menu with actions for: - \list - \li Resetting property values to their default values - \li Setting property bindings - \li Creating property aliases - \li Inserting keyframes for timeline animations - \endlist - \li - \list - \li \l{Viewing Changes in Properties} - \li \l{Adding Bindings Between Properties} - \li \l{Adding Property Aliases} - \li \l{Setting Keyframe Values} - \endlist - \row - \li \inlineimage icons/action-icon-binding.png - \li Indicates that the value of the property is bound to the value - of another property. Opens the same menu as the action icon. - \li \l{Adding Bindings Between Properties} - - \endtable - \section1 Viewing Changes in Properties The default values of properties are displayed in white color, while the diff --git a/doc/qtdesignstudio/src/views/qtquick-properties.qdoc b/doc/qtdesignstudio/src/views/qtquick-properties.qdoc index fc8ba92e93f..87dfd7a3418 100644 --- a/doc/qtdesignstudio/src/views/qtquick-properties.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-properties.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -17,7 +17,7 @@ All components share a set of properties that you can specify in the \uicontrol Properties view. - \image qtquick-item-properties-common.png "Basic component properties" + \image qtquick-item-properties-common.webp "Basic component properties" \section2 Type diff --git a/doc/qtdesignstudio/src/views/qtquick-states-view.qdoc b/doc/qtdesignstudio/src/views/qtquick-states-view.qdoc index 407e2f57697..5270df5b062 100644 --- a/doc/qtdesignstudio/src/views/qtquick-states-view.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-states-view.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -7,12 +7,16 @@ \nextpage studio-translations.html \sa {Working with States} + \ingroup studio-views + \title States + \brief Apply states to a component. + The \uicontrol States view displays the different \l{Working with States}{states} of a UI. - \image qmldesigner-transitions.png "States view" + \image qmldesigner-transitions.webp "States view" To open the \uicontrol States view, select \uicontrol View > \uicontrol Views > \uicontrol States. diff --git a/doc/qtdesignstudio/src/views/qtquick-text-editor.qdoc b/doc/qtdesignstudio/src/views/qtquick-text-editor.qdoc index f202df3c372..6941a1c2ecf 100644 --- a/doc/qtdesignstudio/src/views/qtquick-text-editor.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-text-editor.qdoc @@ -6,8 +6,12 @@ \previouspage qtquick-curve-editor.html \nextpage creator-projects-view.html + \ingroup studio-views + \title Code + \brief View and modify the code generated by the visual editors. + To view and modify the code in a \l{UI Files}{UI file} (.ui.qml) or component file (.qml), go to \uicontrol View and select \uicontrol Views > \uicontrol Code. \QDS generates the code when you create components in the diff --git a/doc/qtdesignstudio/src/views/qtquick-timeline-view.qdoc b/doc/qtdesignstudio/src/views/qtquick-timeline-view.qdoc index 81947261841..2e34d70dc4e 100644 --- a/doc/qtdesignstudio/src/views/qtquick-timeline-view.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-timeline-view.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -6,14 +6,18 @@ \previouspage qtquick-transition-editor.html \nextpage qtquick-curve-editor.html + \ingroup studio-views + \title Timeline + \brief Animate the properties of components. + You can use the timeline and keyframe based editor in the \uicontrol Timeline view to animate the properties of \l{glossary_component}{components}. The view is empty until you create a timeline. - \image studio-timeline-empty.png "Empty Timeline view" + \image studio-timeline-empty.webp "Empty Timeline view" Select the \inlineimage icons/plus.png (\uicontrol {Add Timeline}) button to @@ -23,7 +27,7 @@ \image studio-timeline-settings.png "Timeline Settings dialog" When you select \uicontrol Close, the \uicontrol Timeline view appears. - It now displays a \l{Timeline Toolbar}{toolbar} and a ruler but no + It now displays a toolbar and a ruler but no keyframe tracks. \image studio-timeline-no-tracks.webp "Timeline view without keyframe tracks" @@ -86,171 +90,4 @@ \image studio-timeline-keyframe-track-colors.webp "Keyframe track colors in Timeline" - \section1 Timeline Toolbar - - The \uicontrol Timeline toolbar contains the following buttons and fields. - - \table - \header - \li Button/Field - \li Action - \li Read More - \row - \li \inlineimage icons/settings.png - \li Opens the \uicontrol {Timeline Settings} dialog for editing - timeline settings. - \li \l{Creating a Timeline} - \row - \li Timeline ID - \li Displays the ID of the current timeline. - \li \l{Creating a Timeline} - \row - \li \inlineimage icons/to_first_frame.png - \li \uicontrol {To Start (Home)} moves to the first frame on the - timeline. - \li \l{Navigating in Timeline} - \row - \li \inlineimage icons/back_one_frame.png - \li \uicontrol {Previous (,)} moves to the previous frame on the - timeline. - \li \l{Navigating in Timeline} - \row - \li \inlineimage icons/start_playback.png - \li \uicontrol {Play (Space)} previews the animation. - \li \l{Viewing the Animation} - \row - \li \inlineimage icons/forward_one_frame.png - \li \uicontrol {Next (.)} moves to the next frame on the timeline. - \li \l{Navigating in Timeline} - \row - \li \inlineimage icons/to_last_frame.png - \li \uicontrol {To End (End)} moves to the last frame on the timeline. - \li \l{Navigating in Timeline} - \row - \li Current Keyframe - \li Displays the frame that the playhead is currently on. Enter a - number in the field to move the playhead to the respective frame. - \li \l{Navigating in Timeline} - \row - \li \inlineimage icons/global_record_keyframes.png - \li Records changes in keyframe values. - \li \l {Setting Keyframe Values} - \row - \li \inlineimage icons/curve_editor.png - \li Opens \uicontrol {Easing Curve Editor} for attaching an easing - curve to the selected transition. - \li \l{Editing Easing Curves} - \row - \li Start Frame - \li Specifies the first frame of the timeline. Negative values are - allowed. The difference between the start frame and the end frame - determines the duration of the animation. - \li \l{Creating a Timeline} - \row - \li \inlineimage icons/zoomOut.png - \li \uicontrol {Zoom Out} (\key Ctrl+-) zooms out of the view. - \li \l{Zooming in Timeline} - \row - \li Slider - \li Sets the zooming level. - \li \l{Zooming in Timeline} - \row - \li \inlineimage icons/zoomIn.png - \li \uicontrol {Zoom In} (\key Ctrl++) zooms into the view. - \li \l{Zooming in Timeline} - \row - \li End Frame - \li Specifies the last frame of the timeline. The difference between - the start frame and the end frame determines the duration of the - animation, so if the start frame is 0, the end frame equals the - duration. - \li \l{Creating a Timeline} - \row - \li State Name - \li Displays the name of the current state. - \li \l{Binding Animations to States} - \endtable - - \section1 Keyframe Track Icons - - Each keyframe track can contain the following buttons and markers. - - \table - \header - \li Button/Icon - \li Action - \li Read More - \row - \li \inlineimage icons/previous_keyframe.png - \li Jumps to the previous frame on the timeline. - \li \l{Setting Keyframe Values} - \row - \li \inlineimage icons/next_keyframe.png - \li Jumps to the next frame on the timeline. - \li \l{Setting Keyframe Values} - \row - \li \inlineimage icons/local_record_keyframes.png - \li Records changes in keyframe values for a particular property. - \li \l {Setting Keyframe Values} - \target keyframe_marker - \row - \li \inlineimage icons/keyframe.png - \li Indicates the type of easing curve attached to the keyframe. - When a keyframe track is selected, the keyframe markers on it turn - gray, and when a keyframe itself is selected, its marker turns - brown: - \list - \li \inlineimage icons/keyframe_linear_active.png - - linear easing curve - \li \inlineimage icons/keyframe_manualbezier_active.png - - manually set Bezier curve - \li \inlineimage icons/keyframe_autobezier_active.png - - automatically set Bezier curve - \li \inlineimage icons/keyframe_lineartobezier_active.png - - linear-to-Bezier curve - \endlist - \li \l {Editing Easing Curves} - \endtable - - \section1 Timeline Context Menu - - The following table summarizes the context menu items available for each - keyframe track for a component, property, or keyframe marker and provides - links to more information about them. - - \table - \header - \li To Learn About - \li Go To - \row - \li Delete All Keyframes - \li \l{Deleting Keyframes} - \row - \li Add Keyframes at Current Frame - \li \l{Setting Keyframe Values} - \row - \li Copy All Keyframes - \li \l{Copying Keyframes} - \row - \li Paste Keyframes - \li \l{Copying Keyframes} - \row - \li Remove Property - \li \l{Setting Keyframe Values} - \row - \li Delete Keyframe - \li \l{Deleting Keyframes} - \row - \li Edit Easing Curve - \li \l{Editing Easing Curves} - \row - \li Edit Keyframe - \li \l{Editing Keyframe Values} - \row - \li Override Color - \li \l{Setting Keyframe Track Color} - \row - \li Reset Color - \li \l{Setting Keyframe Track Color} - \endtable */ diff --git a/doc/qtdesignstudio/src/views/qtquick-timeline.qdoc b/doc/qtdesignstudio/src/views/qtquick-timeline.qdoc index c6a33f62c70..b599932ed03 100644 --- a/doc/qtdesignstudio/src/views/qtquick-timeline.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-timeline.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -191,7 +191,7 @@ position of the playhead, right-click the component name on the timeline and select \uicontrol {Add Keyframes at Current Frame}. - Keyframes are marked on the timeline by using \l{keyframe_marker}{markers} + Keyframes are marked on the timeline by using markers of different colors and shapes, depending on whether they are active or inactive or whether you have applied \l{Editing Easing Curves} {easing curves} to them. diff --git a/doc/qtdesignstudio/src/views/qtquick-transition-editor.qdoc b/doc/qtdesignstudio/src/views/qtquick-transition-editor.qdoc index b21207259e2..7f73e0ceca1 100644 --- a/doc/qtdesignstudio/src/views/qtquick-transition-editor.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-transition-editor.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -6,8 +6,12 @@ \previouspage studio-translations.html \nextpage qtquick-timeline-view.html + \ingroup studio-views + \title Transitions + \brief Make movement between states smooth. + To make movement between states smooth, you can use \uicontrol {Transitions} to animate the changes between states. @@ -17,7 +21,7 @@ animated, such as colors or numbers, in the \l Properties view. For example, you can animate the changes in the position of a component. - \image qtquick-transition-editor-view.png "Transitions view" + \image qtquick-transition-editor-view.webp "Transitions view" In \uicontrol {Transitions}, you can set the start frame, end frame, and duration for the transition of each property. You can also @@ -31,46 +35,6 @@ and \inlineimage icons/zoom_big.png buttons to zoom out of or into the view. - \section1 Summary of Transitions Toolbar Actions - - \table - \header - \li Button/Field - \li Action - \li Read More - \row - \li \inlineimage icons/settings.png - \li Opens \uicontrol {Transition Settings} dialog for editing - transition settings. - \li \l{Specifying Transition Settings} - \row - \li Transition ID - \li Displays a list of transitions that you can open in - \uicontrol {Transitions}. - \li \l{Animating Transitions Between States} - \row - \li \inlineimage icons/curve_editor.png - \li Opens \uicontrol {Easing Curve Editor} for attaching an easing - curve to the selected transition. - \li \l{Editing Easing Curves} - \row - \li \inlineimage icons/zoomOut.png - \li \uicontrol {Zoom Out} (\key Ctrl+-): zooms out of the view. - \li \l{Zooming in Transitions} - \row - \li Slider - \li Sets the zooming level. - \li \l{Zooming in Transitions} - \row - \li \inlineimage icons/zoomIn.png - \li \uicontrol {Zoom In} (\key Ctrl++): zooms into the view. - \li \l{Zooming in Transitions} - \row - \li Maximum Duration - \li Specifies the maximum duration of the transition. - \li - \endtable - \section1 Animating Transitions Between States To animate transitions: @@ -78,12 +42,12 @@ \list 1 \li Select \uicontrol View > \uicontrol Views > \uicontrol {Transition Editor}. - \image qmldesigner-transition-editor-startup.png "Empty Transitions view" + \image qmldesigner-transition-editor-startup.webp "Empty Transitions view" \li Select the \inlineimage icons/plus.png (\uicontrol {Add Transition}) button to add a transition. This works only if you have added at least one state and modified at least one property in it. - \image qtquick-transition-editor-view.png "Transitions view" + \image qtquick-transition-editor-view.webp "Transitions view" \li Move the blue bar next to the component or property name to set the start and end frame of the animation of the property. Pull its left and right edges to set the duration of the animation. @@ -98,7 +62,7 @@ (\uicontrol {Transition Settings (S)}) button in \uicontrol {Transition Editor}. - \image qtquick-transition-editor-settings.png "Transitions settings" + \image qtquick-transition-editor-settings.webp "Transitions settings" To add transitions: diff --git a/doc/qtdesignstudio/src/views/studio-content-library.qdoc b/doc/qtdesignstudio/src/views/studio-content-library.qdoc index e2785149e19..b3dbb28065a 100644 --- a/doc/qtdesignstudio/src/views/studio-content-library.qdoc +++ b/doc/qtdesignstudio/src/views/studio-content-library.qdoc @@ -6,8 +6,13 @@ \previouspage creator-open-documents-view.html \nextpage studio-texture-editor.html + \ingroup studio-views + \title Content Library + \brief Select material, texture, and environment bundles with assets to + use in your application. + \note The \uicontrol {Content Library} view is included in the \l{https://www.qt.io/pricing}{Qt Design Studio Enterprise license}. diff --git a/doc/qtdesignstudio/src/views/studio-material-editor.qdoc b/doc/qtdesignstudio/src/views/studio-material-editor.qdoc index bb2923a7abd..44cea9504aa 100644 --- a/doc/qtdesignstudio/src/views/studio-material-editor.qdoc +++ b/doc/qtdesignstudio/src/views/studio-material-editor.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -6,9 +6,12 @@ \previouspage studio-3d-editor.html \nextpage quick-components-view.html + \ingroup studio-views \title Material Editor and Browser + \brief Create and manage materials and textures. + In the \uicontrol {Material Editor} and \uicontrol {Material Browser} views, you create and manage materials and textures. @@ -21,7 +24,8 @@ To create a new material, do one of the following: \list - \li In \uicontrol {Material Browser}, select \inlineimage icons/plus.png + \li In \uicontrol {Material Browser}, select \inlineimage icons/add_material.png + \inlineimage icons/plus.png . \li In \uicontrol {Material Editor}, select \inlineimage icons/plus.png . @@ -67,7 +71,7 @@ \li In \uicontrol{Properties}, select \inlineimage icons/close.png next to the material. - \image materials-remove-material.png + \image materials-remove-material.webp "Remove a material in model properties" \endlist \section2 Copying and Pasting Material Properties @@ -82,7 +86,7 @@ want to copy properties from. \li Select \uicontrol {Copy properties} and then \uicontrol All or a property group. - \image material-copy-properties.png + \image material-copy-properties.png "Copy material properties in Material Browser" \li Right-click the material that you want to copy the properties to. \li Select \uicontrol {Paste properties}. \endlist @@ -114,11 +118,10 @@ \li From \uicontrol{Assets}, drag an image to \uicontrol{Reflection Map}. \li In \uicontrol {Navigator}, select - \inlineimage icons/filtericon.png - and then clear \uicontrol {Show Only Visible Components}. Now the + \inlineimage {icons/visibilityon.png}. Now the texture you just added to the material is visible in \uicontrol {Navigator}. - \image navigator-material-texture.png + \image navigator-material-texture.webp "Materials visible in Navigator" \li In \uicontrol {Navigator}, select the texture. \li In \uicontrol {Properties}, set \uicontrol {Texture Mapping} to \uicontrol {Environment}. @@ -276,8 +279,8 @@ To create a new texture, do one of the following in \uicontrol {Material Browser}: \list - \li Select \inlineimage icons/plus.png - in the \uicontrol Textures section. + \li Select \inlineimage icons/add_texture.png \inlineimage icons/plus.png + . \li Right-click anywhere in the \uicontrol Textures section and select \uicontrol {Create new texture}. \endlist diff --git a/doc/qtdesignstudio/src/views/studio-model-editor.qdoc b/doc/qtdesignstudio/src/views/studio-model-editor.qdoc index e56055c7bd6..4d10f87552e 100644 --- a/doc/qtdesignstudio/src/views/studio-model-editor.qdoc +++ b/doc/qtdesignstudio/src/views/studio-model-editor.qdoc @@ -6,8 +6,12 @@ \previouspage qtquick-effect-composer-view.html \nextpage creator-project-managing-workspaces.html + \ingroup studio-views + \title Model Editor + \brief Create, manage, import, and export data models. + In the \uicontrol {Model Editor} view, you can create, manage, import, and export data models. With data models, you can, for example, populate views with data. diff --git a/doc/qtdesignstudio/src/views/studio-qtinsight.qdoc b/doc/qtdesignstudio/src/views/studio-qtinsight.qdoc index 6af9b071e17..6f420e1cb7e 100644 --- a/doc/qtdesignstudio/src/views/studio-qtinsight.qdoc +++ b/doc/qtdesignstudio/src/views/studio-qtinsight.qdoc @@ -6,8 +6,12 @@ \previouspage studio-texture-editor.html \nextpage qtquick-effect-composer-view.html + \ingroup studio-views + \title Qt Insight + \brief Manage your Qt Insight. + In the \uicontrol {Qt Insight} view, you manage your Qt Insight. Qt Insight is an analytics solution that provides real user insights on the usage of Qt diff --git a/doc/qtdesignstudio/src/views/studio-texture-editor.qdoc b/doc/qtdesignstudio/src/views/studio-texture-editor.qdoc index ef45d8efdf1..62b0eb147cc 100644 --- a/doc/qtdesignstudio/src/views/studio-texture-editor.qdoc +++ b/doc/qtdesignstudio/src/views/studio-texture-editor.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -6,11 +6,15 @@ \previouspage studio-content-library.html \nextpage studio-qt-insight.html + \ingroup studio-views + \title Texture Editor + \brief Create and manage textures. + In the \uicontrol {Texture Editor} view, you create and manage textures. - \image texture-editor.png + \image texture-editor.webp "Texture Editor" \section1 Creating a Texture @@ -38,7 +42,7 @@ \li Select \inlineimage icons/apply.png . \li Select the material and property that you want to add the texture to. - \image select-material-property.png + \image select-material-property.png "Select a material property" \endlist \note You can also apply textures to materials in the diff --git a/doc/qtdesignstudio/src/views/studio-translations.qdoc b/doc/qtdesignstudio/src/views/studio-translations.qdoc index a2a610b3138..ecf43369a42 100644 --- a/doc/qtdesignstudio/src/views/studio-translations.qdoc +++ b/doc/qtdesignstudio/src/views/studio-translations.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -6,8 +6,12 @@ \previouspage qtquick-states-view.html \nextpage qtquick-timeline-view.html + \ingroup studio-views + \title Translations + \brief Handle translations and multi-language support. + The \uicontrol Translations view is included in the \l{https://www.qt.io/pricing}{Qt Design Studio Enterprise license}. @@ -16,49 +20,6 @@ \image studio-translations-view.png "Translations view" - \section1 Summary of Translations View Buttons - - The \uicontrol {Translations} view contains the following buttons. - - \table - \header - \li Button - \li Function - \li Read More - \row - \li \inlineimage icons/select-languages.png - \li Select which languages you want your project to support. - \li - \row - \li \inlineimage icons/export-json-translations.png - \li Export all your translations to a JSON file. - \li \l{Importing and Exporting Translations} - \row - \li \inlineimage icons/import-json-translations.png - \li Import translations from a JSON file. - \li \l{Importing and Exporting Translations} - \row - \li \inlineimage icons/generate-translation-files.png - \li Generate Qt compiled translation source files (\e{.qm}) - and Qt translation source files (\e{.ts}). - \li \l{Generating Qt Translation Source Files} - \row - \li \inlineimage icons/project-translation-test.png - \li Run translation test for several documents and create a test report. - \li \l{Running Translation Test for Several Documents} - \row - \li \inlineimage icons/qml-translation-test.png - \li Run translation test for the currently open document. This test - shows translation warnings in the \uicontrol{2D} view and creates a - test report. - \li \l{Running Translation Test for a Single Document} - \row - \li \inlineimage icons/export-translations.png - \li Export all translations used in your project or all translations - currently visible in your UI. - \li \l{Exporting Translations in Other Ways} - \endtable - \section1 Importing and Exporting Translations You can import and export translations using JSON files. From ee99974c77db269eb4546dd3c7bd880f788e16dc Mon Sep 17 00:00:00 2001 From: Johanna Vanhatapio Date: Thu, 4 Apr 2024 12:15:32 +0300 Subject: [PATCH 109/202] Doc: Document Ext Scene Environment - Document the new extended scene environment - Update info on the basic scene environment Task-number: QDS-11987 Change-Id: I65679e9e6ff7082f91775d097ff2cd455b6271c5 Reviewed-by: Mahmoud Badri Reviewed-by: Miikka Heikkinen Reviewed-by: Mats Honkamaa Reviewed-by: Qt CI Patch Build Bot --- .../images/studio-3d-properties-type.png | Bin 6593 -> 0 bytes .../images/studio-3d-properties-type.webp | Bin 0 -> 6244 bytes ...d-scene-environment-ambient-occlusion.webp | Bin 0 -> 6750 bytes ...dio-3d-scene-environment-antialiasing.webp | Bin 0 -> 6410 bytes ...cene-environment-image-based-lighting.webp | Bin 0 -> 6382 bytes ...tudio-3d-scene-environment-light-probe.png | Bin 9907 -> 0 bytes ...studio-3d-scene-environment-properties.png | Bin 15463 -> 0 bytes ...tudio-3d-scene-environment-properties.webp | Bin 0 -> 6522 bytes .../images/studio-ext-scene-environment.webp | Bin 0 -> 14566 bytes .../images/studio-qtquick-3d-components.png | Bin 21566 -> 0 bytes .../images/studio-qtquick-3d-components.webp | Bin 0 -> 14204 bytes .../images/studio-qtquick-3d-view.png | Bin 6349 -> 0 bytes .../images/studio-qtquick-3d-view.webp | Bin 0 -> 6362 bytes .../studio-qtquick-camera-properties.webp | Bin 0 -> 11336 bytes .../components/qtquick-preset-components.qdoc | 4 +- .../src/qtdesignstudio-toc.qdoc | 4 +- .../qtdesignstudio-3d-camera.qdoc | 4 +- .../qtdesignstudio-3d-custom-shaders.qdoc | 2 +- .../qtdesignstudio-3d-effects.qdoc | 4 +- .../qtdesignstudio-3d-lights.qdoc | 2 +- .../qtdesignstudio-3d-model.qdoc | 2 +- .../qtdesignstudio-3d-scene-environment.qdoc | 163 ++++++++++++------ .../qtdesignstudio-3d-view.qdoc | 66 ++++--- .../src/views/qtquick-components-view.qdoc | 2 +- 24 files changed, 165 insertions(+), 88 deletions(-) delete mode 100644 doc/qtdesignstudio/images/studio-3d-properties-type.png create mode 100644 doc/qtdesignstudio/images/studio-3d-properties-type.webp create mode 100644 doc/qtdesignstudio/images/studio-3d-scene-environment-ambient-occlusion.webp create mode 100644 doc/qtdesignstudio/images/studio-3d-scene-environment-antialiasing.webp create mode 100644 doc/qtdesignstudio/images/studio-3d-scene-environment-image-based-lighting.webp delete mode 100644 doc/qtdesignstudio/images/studio-3d-scene-environment-light-probe.png delete mode 100644 doc/qtdesignstudio/images/studio-3d-scene-environment-properties.png create mode 100644 doc/qtdesignstudio/images/studio-3d-scene-environment-properties.webp create mode 100644 doc/qtdesignstudio/images/studio-ext-scene-environment.webp delete mode 100644 doc/qtdesignstudio/images/studio-qtquick-3d-components.png create mode 100644 doc/qtdesignstudio/images/studio-qtquick-3d-components.webp delete mode 100644 doc/qtdesignstudio/images/studio-qtquick-3d-view.png create mode 100644 doc/qtdesignstudio/images/studio-qtquick-3d-view.webp create mode 100644 doc/qtdesignstudio/images/studio-qtquick-camera-properties.webp diff --git a/doc/qtdesignstudio/images/studio-3d-properties-type.png b/doc/qtdesignstudio/images/studio-3d-properties-type.png deleted file mode 100644 index c0363d0233bbf359bd9530beb6b02beee3590e06..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6593 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 WVus6uE2*5{Q`b}!-Ai5V!3Y4xcm{a@ literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/studio-3d-scene-environment-ambient-occlusion.webp b/doc/qtdesignstudio/images/studio-3d-scene-environment-ambient-occlusion.webp new file mode 100644 index 0000000000000000000000000000000000000000..91e01bc809dc62bb336e24adbce79b06c05e8103 GIT binary patch literal 6750 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 Q&Q8k}KmE>Ld?h0T0F6I9MF0Q* literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/studio-3d-scene-environment-antialiasing.webp b/doc/qtdesignstudio/images/studio-3d-scene-environment-antialiasing.webp new file mode 100644 index 0000000000000000000000000000000000000000..a8cae264d397bcd73c6ab2ec570387101a2c42e0 GIT binary patch literal 6410 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 SIT?EWP2cYq(Ga{;LJ0tjtY0z! literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/studio-3d-scene-environment-image-based-lighting.webp b/doc/qtdesignstudio/images/studio-3d-scene-environment-image-based-lighting.webp new file mode 100644 index 0000000000000000000000000000000000000000..d5a9da81fb314229a060bd8eabebfa6663c4f0b1 GIT binary patch literal 6382 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 E0IDTD4*&oF literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/studio-3d-scene-environment-light-probe.png b/doc/qtdesignstudio/images/studio-3d-scene-environment-light-probe.png deleted file mode 100644 index 7a062a2a36015a7fdc017c420558df7eec3ab310..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9907 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 aO>%Am`Bcg5kNt@&$JT|4-p@xKVg~^7b*AwE diff --git a/doc/qtdesignstudio/images/studio-3d-scene-environment-properties.png b/doc/qtdesignstudio/images/studio-3d-scene-environment-properties.png deleted file mode 100644 index 353e0c35848f7bf0ff1bab20f76c658ec31071d4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15463 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 G2m=70o~PCT diff --git a/doc/qtdesignstudio/images/studio-3d-scene-environment-properties.webp b/doc/qtdesignstudio/images/studio-3d-scene-environment-properties.webp new file mode 100644 index 0000000000000000000000000000000000000000..bb3e62d716be73e468b3b7befcda76cc7472614d GIT binary patch literal 6522 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/studio-ext-scene-environment.webp b/doc/qtdesignstudio/images/studio-ext-scene-environment.webp new file mode 100644 index 0000000000000000000000000000000000000000..cc26498c95cbb2c9de21aa0a7edb0130ebf00a15 GIT binary patch literal 14566 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�`I#rw@ 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 diff --git a/doc/qtdesignstudio/images/studio-qtquick-3d-components.webp b/doc/qtdesignstudio/images/studio-qtquick-3d-components.webp new file mode 100644 index 0000000000000000000000000000000000000000..54be760fd2809097079af850b267a4fc751cb7a6 GIT binary patch literal 14204 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 F0Ra2MdEEd2 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/studio-qtquick-camera-properties.webp b/doc/qtdesignstudio/images/studio-qtquick-camera-properties.webp new file mode 100644 index 0000000000000000000000000000000000000000..e54afe807ed153d97af3a13759393ce566f9a008 GIT binary patch literal 11336 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc b/doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc index 19ee8e60ddc..4920cf582dd 100644 --- a/doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -58,7 +58,7 @@ \li \l {Custom Effects and Materials} \li \l {Lights} \li \l {Cameras} - \li \l {Scene Environment} + \li \l {Scene Environments} \li \l {Morph Target} \li \l {Repeater3D} \li \l {Loader3D} diff --git a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc index d98beda4f36..e86bd693299 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -90,7 +90,7 @@ \li \l{Custom Effects and Materials} \li \l{Lights} \li \l{Cameras} - \li \l{Scene Environment} + \li \l{Scene Environments} \li \l{Morph Target} \li \l{Repeater3D} \li \l{Loader3D} diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-camera.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-camera.qdoc index c245c490e67..89da3cbb346 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-camera.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-camera.qdoc @@ -14,7 +14,7 @@ scene, it can be used to direct what is displayed in a \l {3D Views} {3D view}. - \image studio-qtquick-3d-components.png "Qt Quick 3D components in Components" + \image studio-qtquick-3d-components.webp "Qt Quick 3D components in Components" To add a camera component to your UI, do one of the following: \list @@ -65,7 +65,7 @@ You can edit the camera properties in the \uicontrol Properties view. - \image studio-qtquick-camera-properties.png "Properties view for Perspective Camera" + \image studio-qtquick-camera-properties.webp "Properties view for Perspective Camera" \section1 Setting Camera Field of View diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-custom-shaders.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-custom-shaders.qdoc index 5675ac92863..f053834f0dc 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-custom-shaders.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-custom-shaders.qdoc @@ -29,7 +29,7 @@ \uicontrol {Custom Material} components, in \uicontrol Components > \uicontrol {Qt Quick3D} > \uicontrol {Qt Quick 3D}. - \image studio-qtquick-3d-components.png "Effect and Custom Material Components in Components" + \image studio-qtquick-3d-components.webp "Effect and Custom Material Components in Components" \note In Qt 5 the \uicontrol Effect component is located in \uicontrol {Qt Quick 3D Effects} > diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-effects.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-effects.qdoc index 158932dff8a..95eb3232d7b 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-effects.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-effects.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -263,7 +263,7 @@ be transparent in the \uicontrol {Background Mode} field of the \uicontrol {Scene Environment} component. Otherwise, the clear color of the background hides the blur. For more information, see - \l {Scene Environment}. + \l {Scene Environments}. The \uicontrol {Fade Amount} property defines the fade speed of the trail. diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-lights.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-lights.qdoc index af994d58774..c7ae1e955ef 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-lights.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-lights.qdoc @@ -9,7 +9,7 @@ \title Lights Light components are the primary source of lighting in a \QDS scene. - As a secondary light source, you can use \l{Setting the Light Probe} + As a secondary light source, you can use \l{Setting Image Based Lighting} {image-based lighting}. To add a light component to your UI, do one of the following: diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-model.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-model.qdoc index 4159269bc75..1b515a6e6de 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-model.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-model.qdoc @@ -31,7 +31,7 @@ \uicontrol QtQuick3D module to your project, as described in \l {Adding and Removing Modules}. - \image studio-qtquick-3d-components.png "The Qt Quick 3D section in Components" + \image studio-qtquick-3d-components.webp "The Qt Quick 3D section in Components" \section1 Model Properties diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-scene-environment.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-scene-environment.qdoc index 6e9db7012c0..5d18f39a836 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-scene-environment.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-scene-environment.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2020 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -6,31 +6,43 @@ \previouspage studio-3d-camera.html \nextpage studio-3d-morph-target.html - \title Scene Environment + \title Scene Environments - You can use the \uicontrol {Scene Environment} component to specify - how a scene is rendered globally. You can specify settings for antialiasing, - scene background, ambient occlusion, and image-based lighting in the - \l Properties view. The \uicontrol {Scene Environment} component is available - in \uicontrol Components > \uicontrol {Qt Quick 3D}. It is - automatically included under the 3D view component in \l{Creating Projects} - {projects created} using the \uicontrol {Qt Quick 3D Application} wizard - template. + The \uicontrol {Scene Environment} and the \uicontrol {Extended Scene Environment} + components define how a scene is rendered globally. - \note If you select \uicontrol {Qt 5} as the \uicontrol {Target Qt Version} - when \l {Creating Projects}{creating your project}, the available properties - for this component will be slightly different. The properties may also be - situated differently in the \uicontrol Properties view. + \note The available properties for the scene environments and their location in the + \uicontrol Properties view vary according to \uicontrol {Target Qt Version} that + you select when \l {Creating Projects}{creating your project}. + + \note \uicontrol {Extended Scene Environment} is available in projects created with + Qt 6.5 or higher as the \uicontrol {Target Qt Version}. + + \section1 Adding Scene Environments to Projects + + Add the scene environment components to projects by selecting a suitable preset when + \l{Creating Projects}{creating your project}. + + Use the \uicontrol {3D} preset to create a project with a \uicontrol View3D + component that includes a scene environment. If you need to add it manually, it is + available in \uicontrol Components > \uicontrol {Qt Quick 3D}. + + Use the \uicontrol {Extended 3D} preset to create a project with a 3D view + component that includes an extended scene environment. It is also + available in \uicontrol Components > \uicontrol {Qt Quick 3D Helpers}. \section1 Setting the Scene Environment - In the \uicontrol {Scene Environment} section of the \uicontrol Properties - view, you can specify whether and how the background of the scene should be - cleared, specify whether you wish to perform depth-tests on the scene, - apply post-processing effects to the scene, and define how colors are - tonemapped before the scene is rendered. + To define properties for the scene environment, select \uicontrol {Scene Environment} + in \uicontrol Navigator and specify its properties in the \uicontrol Properties view. - \image studio-3d-scene-environment-properties.png "The Scene Environment properties" + Set the properties in the \uicontrol {Scene Environment} section of the + \uicontrol Properties view to specify whether and how the background + of the scene should be cleared, to apply post-processing effects to the scene, + to define how colors are tonemapped before the scene is rendered, and to + specify fog settings. + + \image studio-3d-scene-environment-properties.webp "The Scene Environment properties" The \uicontrol {Clear color} property specifies which color will be used to clear the background of the scene if \uicontrol {Background mode} is defined @@ -64,23 +76,22 @@ image as a \uicontrol SkyBox using the \uicontrol Image property in the \uicontrol {Light probe} section (In Qt 5, the \uicontrol {Light Probe} property in - the \uicontrol {Image-Based Lighting} group. + the \uicontrol {Image-Based Lighting} group). + \row + \li SkyBoxCubeMap + \li The scene will not be cleared, but instead a + \uicontrol SkyBox or Skydome that uses a \uicontrol CubeMapTexture + will be rendered. A cube map texture has six faces + (X+, X-, Y+, Y-, Z+, and Z-), where each face is an individual + 2D image. Selecting this mode allows custom materials and post-processing + effects to work with cube map textures in their shaders. \endtable To leave the scene uncleared, select \uicontrol {Unspecified} as the \uicontrol {Background mode}. - You can perform depth tests to optimize the scene environment. To skip the - depth tests, deselect the \uicontrol {Enable depth test} checkbox. Note that - skipping the tests can cause rendering errors. - - To have the renderer write to the depth buffer as part of the color pass, - deselect the \uicontrol {Enable depth prepass} checkbox. Deselecting the - checkbox disables the depth prepass on any GPU that uses tiled rendering - architecture. - - The \uicontrol Effect property defines a post-processing effect to the - scene. Use the dropdown menu to select one of the effects that will be + The \uicontrol Effects property defines post-processing effects applied + to the scene. Use the dropdown menu to select one of the effects that will be applied to the entire scene. The order of the effects is significant since the result of each effect is fed to the next. @@ -92,14 +103,21 @@ tonemapping, or \uicontrol ToneMapModeFilmic to apply filmic tonemapping. \note The \uicontrol {Tonemap mode} property is not available in Qt 5. + The \uicontrol Fog property defines settings for fog applied to the + scene. When the \uicontrol Fog property of a scene environment is set to a + valid \uicontrol Fog object, the properties are used to configure the + rendering of fog. The simple fog provided by this type is implemented by + the materials. Use the dropdown menu to select a \uicontrol Fog object for + your scene. + \section1 Applying Antialiasing - Antialiasing is used to make curved lines smoother on the screen. In the + Use antialiasing to make curved lines smoother on the screen. In the \uicontrol Antialiasing section of the \uicontrol Properties view, you can specify the mode and quality of antialiasing and also enable temporal antialiasing and define its strength. - \image studio-3d-scene-environment-antialiasing.png "The Antialiasing properties" + \image studio-3d-scene-environment-antialiasing.webp "The antialiasing properties" The \uicontrol {Antialiasing mode} property specifies the mode of antialiasing applied when the scene is rendered. Select one of the following @@ -174,11 +192,12 @@ animations stop. The \uicontrol {Temporal AA strength} property modifies the amount of - temporal movement in antialiasing. This property only has an effect when + temporal movement in antialiasing. This property is only available when the \uicontrol {Temporal AA} property is set to true. - \note In Qt 5, the antialiasing properties are located in - \uicontrol Properties > \uicontrol {Scene Environment}. + The \uicontrol {Specular AA} property enables specular antialiasing. Specular + aliasing is often visible in form of bright dots and flickering when moving the + camera around. \section1 Applying Ambient Occlusion @@ -188,14 +207,17 @@ \uicontrol {Sample rate}, and \uicontrol Bias properties in the \uicontrol {Ambient Occlusion} section of the \uicontrol Properties view. - \image studio-3d-scene-environment-ambient-occlusion.png "The Ambient Occlusion properties" + \image studio-3d-scene-environment-ambient-occlusion.webp "The ambient occlusion properties" - You can set the strength of the shadows using the \uicontrol Strength - property, which defines the amount of ambient occlusion applied. A value of - 100 causes full darkness shadows, while lower values cause the shadowing to - appear lighter. A value of 0 disables ambient occlusion entirely, thus - improving performance at a cost to the visual realism of 3D objects rendered - in the scene. All values other than 0 have the same impact on performance. + In \uicontrol Properties > \uicontrol {Ambient Occlusion}, select the + \uicontrol Enabled checkbox to define settings for ambient occlusion. + + Set the strength of the shadows using the \uicontrol Strength property, + which defines the amount of ambient occlusion applied. A value of 100 causes + full darkness shadows, while lower values cause the shadowing to appear lighter. + A value of 0 disables ambient occlusion entirely, thus improving performance at + a cost to the visual realism of 3D objects rendered in the scene. All values other + than 0 have the same impact on performance. The \uicontrol Distance property defines roughly how far the ambient occlusion shadows spread away from objects. Greater distances cause increasing impact @@ -213,7 +235,7 @@ occlusion, try adjusting the value in the \uicontrol {Clip far} field in the \l{Cameras}{scene camera} properties. - The \uicontrol {Sample rate} property specifies the number of shades of gray, + The \uicontrol {Sample Rate} property specifies the number of shades of gray, thus defining the quality of ambient occlusion at the expense of performance. The \uicontrol Bias property defines a cutoff distance preventing objects @@ -227,15 +249,15 @@ differently: \uicontrol {AO strength}, \uicontrol {AO distance}, \uicontrol {AO softness}, \uicontrol {AO dither}, and \uicontrol {AO bias}. - \section1 Setting the Light Probe + \section1 Setting Image Based Lighting - In the \uicontrol {Light Probe} section of the \uicontrol Properties view, - you can set the \uicontrol Image, \uicontrol Exposure, \uicontrol Horizon, + In the \uicontrol {Image Based Lighting} section of the \uicontrol Properties view, + you can set the \uicontrol {HDR Image}, \uicontrol Exposure, \uicontrol Horizon, and \uicontrol Orientation properties for image-based lighting. - \image studio-3d-scene-environment-light-probe.png "The Light Probe properties" + \image studio-3d-scene-environment-image-based-lighting.webp "Image based lighting properties" - The \uicontrol Image property defines an image used to light the scene + The \uicontrol {HDR Image} property defines an image used to light the scene instead of or in addition to standard lights. The image is preferably a high-dynamic range image or a pre-generated cubemap. Pre-baking provides significant performance improvements at run time because no time is spent on @@ -286,4 +308,45 @@ The value of the \uicontrol {Probe FOV} property sets the angle of the image source field of view when using a camera source as the IBL probe. + + \section2 Advanced Scene Environment settings + + You can perform depth tests to optimize the scene environment. To skip the + depth tests, clear the \uicontrol {Enable depth test} checkbox. Note that + skipping the tests can cause rendering errors. + + To have the renderer write to the depth buffer as part of the color pass, + clear the \uicontrol {Enable depth prepass} checkbox. Clearing the + checkbox disables the depth prepass on any GPU that uses tiled rendering + architecture. + + To specify additional render settings for debugging scenes, define + \uicontrol {Debug Settings}. + + To define lightmap baking settings for direct and indirect lighting, use + the \uicontrol {Light Mapper} property to specify a lightmapper object. + These settings are not relevant at other times, such as when using already + generated lightmaps to render a scene. + + \section1 Setting the Extended Scene Environment + + In addition to properties described above, in the extended scene environment + you can apply effects to your scene by defining them as properties. When enabling + one or more of these effects, the result is similar to manually adding + \l {3D Effects}{effects} to \uicontrol {Scene Environment}. + + Use \uicontrol {Extended Scene Environment} instead of \uicontrol {Scene Environment} + to add multiple and complex effects to your scene. Because the \uicontrol + {Extended Scene Environment} combines the effects that are enabled, the number of + render passes is reduced, which results in significantly better performance + than applying individual post-processing effects to the scene. + + For the extended scene environment, you can also define \uicontrol {Local Custom Properties}. + + \image studio-ext-scene-environment.webp "Properties of Extended Scene Environment" + + \note If additional post-processing effects are manually added to + \uicontrol {Scene Environment}, those effects will be applied before the effects + defined in the properties of \uicontrol {Extended Scene Environment}. + */ diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-view.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-view.qdoc index 5f2f53ca96e..6635de2ad34 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-view.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-view.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -8,42 +8,53 @@ \title 3D Views - To create a Qt Quick 3D UI project, we recommend using a \uicontrol - {Qt Quick 3D Application} wizard template that adds the + To create a Qt Quick 3D UI project, use the \uicontrol {3D} preset that adds the \l {3D Components}{Qt Quick 3D} components to \uicontrol Components - and contains a 3D view. A 3D view component includes a - \l {Scene Environment}{scene environment} as well as a scene - \l {Lights}{light}, \l {Cameras}{camera}, and - \l {3D Models}{model}. A default \l {Materials and Shaders}{material} - is attached to the model. You can attach \l {Textures}{textures} - to materials. For more information about creating projects, see \l{Creating Projects}. + and contains a 3D view component. The \uicontrol {View3D} component + includes a \l {Scene Environments}{scene environment} as well as a scene \l {Lights}{light}, + \l {Cameras}{camera}, and \l {3D Models}{model}. A default \l {Materials and Shaders}{material} + is attached to the model. You can attach \l {Textures}{textures} to materials. - To add a 3D view to some other kind of a project, you first need to add the + To create a project with many complex effects, use the \uicontrol {Extended 3D} preset + that creates a project with an \uicontrol {Extended View3D} component. + The extended 3D view includes an {Extended Scene Environment} + component that enables using various effects by defining them as properties. + + \note The extended 3D view is available in projects created with Qt 6.5 + or higher set as the target version. + + For more information about creating projects, see \l{Creating Projects}. + + To manually add a 3D view to your project, you first need to add the \uicontrol {Qt Quick 3D} module to \uicontrol {Components}, as described in \l {Adding and Removing Modules}. - \image studio-qtquick-3d-components.png "Qt Quick 3D components in Components" + \note \uicontrol {The Qt Quick 3D} module is not available in projects created + with Qt 5 set as the target version. - You can now drag-and-drop a \uicontrol View3D component to the \l Navigator - or \l {2D} view. + \image studio-qtquick-3d-components.webp "QtQuick3D components" - \image studio-navigator-view3d.png "A View 3D component in the Navigator" + You can now drag a \uicontrol View3D or an \uicontrol {Extended View3D} component from + \l Components > \uicontrol QtQuick3D \uicontrol + > Items to \l Navigator or to the \l {2D} view. - By default, a directional light and a perspective camera are used in a 3D - scene created by using the wizard template mentioned above. To use other - light and camera types, select the component in the \uicontrol{3D} or - \uicontrol Navigator view and change the type of the component in the \uicontrol - Type field in \l Properties. For example, to use a point light, enter - \e {PointLight}. + \image studio-navigator-view3d.png "A View 3D component in Navigator" - \image studio-3d-properties-type.png "Type field in Properties view" + By default, a directional light and a perspective camera are used in 3D + scenes created by using the \uicontrol 3D and \uicontrol {Extended 3D} + presets. To use other light and camera types, select the component in + the \uicontrol {3D} or \uicontrol Navigator view and change the type of + the component in the \uicontrol Type field in \l Properties. For example, + to use a point light, enter \c {PointLight}. - Similarly to other components, you can select a 3D view in \uicontrol - Navigator or the \uicontrol{3D} view and modify its property values in the - \uicontrol Properties view. Use the properties in the \uicontrol View3D - tab to set properties specific to a 3D view component. + \image studio-3d-properties-type.webp "Type field in the Properties view" - \image studio-qtquick-3d-view.png "View 3D component properties" + Select a 3D view in \uicontrol Navigator or in \uicontrol{3D} to modify + its property values in the \uicontrol Properties view. Use the properties + in the \uicontrol View3D tab to set properties specific to a 3D view + component. + + \image studio-qtquick-3d-view.webp "View 3D component properties" The \uicontrol Camera property defines which camera is used to render the scene to the \uicontrol {2D} view. If this property is not defined, the @@ -63,4 +74,7 @@ \note The \uicontrol {Import Scene} property can only be set once. Subsequent changes will have no effect. + + The \uicontrol {Render Format} property defines the format of the backing + texture. */ diff --git a/doc/qtdesignstudio/src/views/qtquick-components-view.qdoc b/doc/qtdesignstudio/src/views/qtquick-components-view.qdoc index e956ee86ad6..5933e4be703 100644 --- a/doc/qtdesignstudio/src/views/qtquick-components-view.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-components-view.qdoc @@ -57,7 +57,7 @@ \li \l{Custom Shaders} \li \l{Lights} \li \l{Cameras} - \li \l{Scene Environment} + \li \l{Scene Environments} \li \l{Morph Target} \li \l{Repeater3D} \li \l{Particles} From 2637c957f418ab134664fe15880b73f44ad2d3b5 Mon Sep 17 00:00:00 2001 From: Johanna Vanhatapio Date: Mon, 8 Apr 2024 11:20:02 +0300 Subject: [PATCH 110/202] Doc: Update info on QDS presets Task-number: QDS-11987 Change-Id: I7ba02b3823d2bfd127ec2ad7e86f4d3281d2801b Reviewed-by: Mahmoud Badri Reviewed-by: Mats Honkamaa --- .../images/studio-project-wizards.png | Bin 104223 -> 0 bytes .../images/studio-project-wizards.webp | Bin 0 -> 75114 bytes .../src/qtdesignstudio-projects.qdoc | 9 +++++++-- 3 files changed, 7 insertions(+), 2 deletions(-) delete mode 100644 doc/qtdesignstudio/images/studio-project-wizards.png create mode 100644 doc/qtdesignstudio/images/studio-project-wizards.webp diff --git a/doc/qtdesignstudio/images/studio-project-wizards.png b/doc/qtdesignstudio/images/studio-project-wizards.png deleted file mode 100644 index 3a329bd16e4742e60e56b170e2b163df74eecf6f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 104223 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

}>{3F>wVv%8w zfYB_j12d$j?yfc2{Pq!-ywz3pltV3tFH2Ub`W(3%x_*PM@8*)={^sI_85Vp!cQ&1i z7y6eRJKvdwmF?>CNq?N)ovnMg(`;$GJ^PKTX`a=tkK*4S2+norYd^!KRAE#)g_*lc z+IRVjWCxxsX9Mk9m8z-d4#-!@yPx^vq^ZCE|J}lmOT`vnpUmfCyU5J{x9S6Hn+kt+ ziLx(TGZ=((zfB2$@$*wm&7zbVb#?DNFDE^jNALbNec#GpYcqM$>z$7irksBLm3PUr zF7E&F+XP;m9HgVzfW#-YlY?+uFS7$vhskm`y zS?ad^Paj;%c*k3|8HXPY_SjligFd8tq;8xc|?~{#`9`{+ps^2$;1# zRAO+}DK@-$Qm(>mPW5`{lMkgC8onNSlv!>8nhxIP#a_!6|JKcQcS!ciR_QJC5Bhv~ z-K+QW5zj(<#;FMtia+w(I<$Cb7#w)A`z-I1W4Da!KRlewqu6fGvOQQ%kF#s# zmfa6y5=rNH79>(Ec6^75e8Fy!W7Sq)MUHwjCFN-H8Y!)-Q*Eq4|_TLT$-94+d&f;5BAgnTLU zO8U1X;LM8NJ+Ym(F+cVsw5czvzFEJ&n1ijbHKcEK<+9}leMgOXGT-HIKGTsN;Kt^R;?g>lYUPhb3EI^ZgVN5k77|c}>B6qvJD~CeLFE ze!>QA|VNE~}C(kFD9XdGGA4 zlJ(y|E-PcO6nu2JW#99rgg&XGsz!P9AF}^_$$u&1%Bi#3*0-BJ?-71~&MA%a!quXM zH@Z9Ps|@Z&?VcU=(&X18tD-CFN3Kn6(yiT@RVmFqZEoeW-+mGfB_7H}?>6wAvHqNN zu(;akOv{XuoWB>DZisndw5319wv4`1EF>WOE30?zc=EnHsQpirb0Rrxe#`NP~F7q)98r@vD=zWMXt zNV&@0JG697Kj*wX`6edmsH(y7=AyrfWz%+S5xSUDTD>A|_D}nzi#8{AR(Eo(^*%d` z^D~=Sf5;Kv;N_v!@BcV z&!ov`<)^FPV%S#BxzB*pQ6M;bi`m)P*}uL?Z}-mjUAJSg;WMF=fi<7~3S%Sdb6%TQ zY-~wzUb3dDG~n=!gsinzb$fnoNIo->uZTTTO3#-`&w{r*YaSYqGrem|UP3cRyrUu4F$)&1G6pkJ^DTswOeG?WPIf=R0!C;!R6Gc?{!Cg>t+SNIpT1+_|Vr|T9T^|Ux`Z2QJ(~5!15Rdddr_^-aCPl8->Z8TESh?6kw}Qwzw*gvJQ8L-SFc$A zX42%I6`@@1PSV$KQk?c(#i9AtL3cxmv(l_z6P6?VAYZ)%vmddFg;*48`cd47i^ zU%kD|cUMx(iPxb#b7$3MHFdrGYm>3Zly{%$lRG7gro?YRzni85o7=4`rH;e7Ud$C<-=r+=lkUJo;gZp%F-dQ2=aywcKq=8oO1HW|CjHu^Jn z{p3)pjNRtflcW4t{^L`xa34N5x#tJB++Uly@Al=I?aO}V`AyF8`+L{t|BD^9)vL}s z_x|2@a$|nUE9p6`Y}fZ)?w9?0ccxnS?Dau$tKwpo#m!iq!nXAGruccUCI?rUdj7t5 zt>(?Dy=7PC?`~V(7^T^s9Jfz(X_%Zyv&IufUdn^jrM4?ChNjMYsf{ z85Gz%8mE+e*gMOw?odk%OP=KA)pwO9izu3PO!{{C{IALJ1`8KOikwb~c9sxvkYI7u zb8zLUs_a`C93ArDO7i*1S6+mOX?_d0+aE-OdUdeZzmsNjH z-nRFUfbjNt^$KkI2Fzc>mijJ?b5fAsnrl?b}H_<>DlWRYr6HuyQ^2b7@sK3 z^Z%G|;@an0E5Eu!48lGK`s|wDe1BQLdEQ-v*v=`Fc-am)FFxun#O(EQlJ$hSdz|!r zx>iTH_+GfR_mcb|MC{l>%J{o0xB zm!g<`D>WY6SDx)?=2F?9Z?V^nU9$O6!qL-PC*623>(G)bFXOb%itcc>xf#c>pka0C zYVYi?ie`ETb;OJ(@YCO`PiW1Lon=C@JJJoJie>M1-Oc+N!};L5 z<$P;ZwlJR6+xDd7+HAZgadYAe2jA0`l_l@@esE$vw&GRj{r14Dq&b3}EgKV~YNiOz zHMu8O^=Dealuky8v#OQrpBK8NDE08SZY(@e8aFM)!bN3e-chexbC;;KgcfU#8dd^)dhN&fm>X?mql`u7mH79Jg1PS(|U^ z{ok%%R<6uYS9*5SK=)msN16GBIraa=^WTR4>zX;SFv{jjwa!Y3936?X3)_xoPZ5;d zfBCm;S>%-RAeQ9EdnWHNKg-qs|IXsLSRc6shz9Bwx0EKA#p%4_v|?LTbX*E3^s z-~HzKd!%PQ-C44y^Ok@2?_*I_mqJ-4L9 z#1!HFH;060-?*VBc6XkBy0HArcl{>s)L-!ne{J7*>v6=_%$k$0_$Pk*dg8^3{4xQl z`7FndAAHE;&wMX-#h**lf4?Z+|7+9y+MDMm{FT-E!}lY6-MyuYi(kLEbK{To@5~SR zxo@q#7fxBt@>FnhXG)?*9#4#9$D!UtYrAPrEzUg%%lOtK;Cxm=L$=G}$X|oste<4m zHJBt=`W9D|^zyKCD}MeuNA#$V$+ETuF1M#i-zu8od|A`4S*OFlbdR{iqP+~J0va6d zg)d%q@|{hY&@n@Q`U{Sa#p)d?*MjF3EK*3}=j{;dTe|C2RF}6gql&X!*G%K-A6t0Z ze{^n?*!tqhq}*9;5e*YwbQp3s&RP1i?5kU?dfx;=7G;5z5>*}RQU#~<7^T1OJY2}b z;M2Jv-DOshnyZsWlH%uwGbHC1Svt;Sx0#uK+Haf8Lywm%oQnKUgP6}tx)lUH5xB~^ z%hvWzh@LHPq5gT-K%a!JY!!>8ZOiTnEY)(_CC*SDcHAU}@p-4=F#`q(g{ro1*S6Rg z`UxIT^bz<{G;vzZ%W`|RLisaicDRUrv}NFOxOeSOiS6VCAxG})Ro`~tD(~T`O!0f( z@Eu^DS}x+=9mOHebokw@W$)Ztr%5F9{4G; z&0PHa(7~7s#xV)2RF+gT@)We32-`I=;qqqxRvRJ1@-tt4v^Z?oy=>)9p@ZU&n>#Os zZxT_DHWEG0!qSv@(cq!@%eAlLwFH-MyZ6~*@w|5@REwCZjV-30sd|+k{NRT3;#E(q zHuWy82-xscIC9>@9tIvI)8Nv=71qLSNj=Id6w@c|2)rQn_-v}+O>3{<%f+j(``)@cK+Pe_RP(=)KJxXfgX#3)Z0BPqc%=Em6ml*=epmW+3qn@ zMCPREFg%!C)R`5qCc-=PbdKkCwb*1sh6O0362p;GZH05E_$=s) zdmgYTL(}|9fbrR4QSF^M%buA=r&d~GSpti?FU>l-0Mpu z-Y&nix2$CS+xaVt-!41%yuMJd=Zx6F#!a3l1=Lqhy3wShsycPj(kVCoD0QfMa7t-J zW~tOmx-ptf;966p)TR}qemZFhw^5VSgisz0ovAw|wOJM1753gcTBmcqVa2D}ZC=W% z%MKX?a$I5fb2j(sst;2;bu?~EHgX+a(A2zsQ>^#eqb{}I--lRE3{mMm6gW|;Qb+pE zzU5Ye&QHI4^H^pbO!4LMGH7CC$baJMAo5sbzu-lQgQ8w*_BJ|&|M<$IoPMOUtw~G$ zP}t`4GZI>hC;vEU%w*x0b$99eGPz?dUkc`1GbgkNiHZE$nz!F+Vd;l9vl9_|4=2ei z;>%vEXYLj~=XL#~y@jtNirt*9{;inGIpw50d$YtWhq&sY|qI&`NXr`=T!OX zCEF`)UUDu{S@&`KpB7<_pMjUB&iQ9(C*PYiWdgqgi}zRQ`|tK&>`%xli$7*@TtVi3 z`%%Lg>SgweHMbpmCm&LxIMp)v{#uJT?<|KEq5F^Zi%$O=Y1np#^Ptl&PsSCKx{Ma| zeB0Ua@zF%7w|-GM@4hug)_n7{f4L%j<(+Jmw{fOlcAR$?GX2UtX}eY952a%Ya}<`( z?Gb7=+U(Y6InPS?=>_4{ON3WGl?k?*_-S@ii09GN*V6tcKbyuavwCdbqCDZc=G_It8D zzqkI+)T-(8?k!zh{&1h`)S8boSs4~@Yno`V>Kj|eubPAQUw{9sWJ_}K%9hnLHP2jn z=l>J-$NMhMH-2(GdrGla;`7rjhuhrDCAm4WmZWXg?(vKHcc6K8L?kQs%&6saC+(;* zv%8cXx=(fUpI({m>gQeuA79&f_}i1d9q-@jY+H7F-_o1s?T$At{a(FC`E*&z%d}0h zGyhLp<9sp5JYadYUrxVoh`P zZ)WG^%P;KDyd|By(O}7q6M@!`)s6|6X+?1UzWn8d!b61)Tdv1^>?=DxPxU?jpv?W? z$AV={?oJC=@Awwj(SqIKHd*QdJ1evkQdv+kwE`)k$5S=Y}Mw=IwTa-;TQa{2Y- z^5eDs$8+ezSOnx@n`kYvfiwP=M1v+&4iQlX9X_IQN1cNiFbF% z(U7y5B^PGICz^;(jcI?S^15mLu6N~cm&W-npRV;fX8GgOQ#qd-1pl;apSh`Go6Q!kbAXE^br?T8X z{YA1wV2_9MgtbO$i`gdkR4%(@m@ME|vn$qk|J_Y2q86$K_r2PF`jtuO7xZ;A9cPjW zxnui7{D!cjqp*ufF%k`VLe`}@sv{3lK^5Kf)5XS1q;z{B4hp-S&22^Hz>EZ$>e@1MAC z#u;~26;me@NwLWi%PqwVPHC)U+Zp+@4GHWBBG zOSL@~IHtDUuet5oCcGy7ROLIX>!F(>8y}zhW5fTo=tL+#d#=8#{ED-p+y>niYm-i$ zW?xV#9i1K3+xVsI$lZ(aX>H$Kw?sGWi`&mF(7;e;xq3hAR!32t(xmi?ey$Ue1uhkA zG8^YP8}vjhYIkPY@&2@bvuVM&~n33;=tuNuhrQ%R0I+v%d)eGt>#G_5|lK>CV&?ey!PUo%GN+rRz0*7{Maetlzt^tbh`Z{Fx# zo$0o7?Nif=&fm4WPTN_ zxvn5l^W6Ix_ilV<)jKYDn9IQGUHd8aJ$b)1e{Bw67cx? zo9WEQ`y-6p!qc98V%a4;uVDZ7zXc9gB#vFacVN~wvH9$76E4=il)SriNu7npt(%k1 zMz6Uu-{A+7fA+}-n_ufLdGj*&$OXe`uWq1CES!f z-ZY$3-~UHb*wm5ZckQlA4(WF{Y`xyTr|j-L{Q~EOJ$Czew;5I3)KJ|w^XA_x6^p)R zzcY2nIlJ?;{HK!z()&a&njHRiOiCp7!;{~ORF2wOeSz#JhaEeCGhF5k3qt5jUGZA)9g>&4STogFw>rGo9>$}J?%fo{)@8O z*^cyMZ+KJH_TJmJL23`{s-5x+E?mk@@IG|v%4%7ASH3XkqBdcvbEg+BULALVX=`DXrEY|Kd$*vkMY7}8r7K4^vCrUOm2B;OewEX_`oXTh%nP=!^UmFu6L;a!?G2xv zSlY!`DT_~^=YM|go4#i~3qPFEOJ}c{9@Bf5l>LOiDcSGLy^zEUZ{IMi z-Lgl@g@vJF>-(!*>8pd;r7T@)uTl;H{JR(~IqffEHl0zytK+y) zG^v=OF=Ow_g{DVC{`h@(k|OYao1SK2<>ZZ3ZtriHFSFOk-9F81>A}KaeXiVo_AM#T zt}j?w&^-0)%rE!nRz-htt=Q7mesl9)xhp)ir2*0*vd+KX+u4aRhuH1+UE-EE`LwB% zZ#17x(4KkGzvAXT%qsQSUVkcAx9D$MTY=Z+{!|%kI^Bs=OmbIkUL=vFfAicFb9S3aHBWz*ojIA;zh%eXoa?7f z9F5K4tzO8@q4S07zP!5jQ`68DKWh#P1X>?oxvl2t0siPL*(tK-$%keaMoePOy0-4V zgI}0;xtmAv&BlpyEGT z)Ux|Ib>+?R|1U(JJ*_|Q^Sg%2HG(&+Q-43*u!3jf0+U|0cFok8t7XcCv*Pmj=OreF zsVYZzd~E+?BlT$6o78iA*SAmg`t!@c>OK1Khc7MQQI|{E;`uSH z`dzS^zvX_m4#yRmsW%FGUpE_tBqhI^e%?RjvgC)CyX!2!h(*2rW5km6ZWW6tI}69> z(AtBK_vo>CB+pX1y5@yo8f$8hHq2WTVG4N*(M)W+sUJT%B!+ERb{m!|N4Xww{P#t zX3qTbr*R{rq7#FotKav^Rae{0A9Cb`OqX>0>?c!j{q(Yf8BR05f7d>8@J@f^iC>$y zJ8tSfdtux2oL4{cXYaUrRqb|uc<|E~D`mxw^{2MIX=cw~BDHm%->Tz()p*k?W0x42 zYo4sHe$$?1WS*Bv1e*xG2kDn`Ma5x=3ovzs{ znX&qnjYm|S(2145WB4TOn=eRAwfwmId!18D?K`)q&>5V(nR`0Rlh>cV|Ak+H*X=>+ z_QMOfj`NGI_rIOrUYT0&**tke%Gb`1TOMcL*14tN%(L#qtDhNF6*q%U?pSdC%;S&W zg7ZGIh$tH|IcXmM?8E!7iC@om|JmhRz8UqsZmbhzt4R^!nI~h|eeL;c?|)xZuII9S z7tN3R!l?Z&dG3xsB4M|maV>gd=;o2S{-f3YHy2hv`WS4j!+F4Zzv1%Bbv)lDuG7(b zlWbK9>6 ze4lTniLB$!P-tP4m~(4;=%wl3dEat|>$1nkw!hzZTK>M-Yqh1xs^OjUP zO0WnDIQgzy^zitF_rLp3dwTkoEiS%NW4U{Ot){Ge^-r5`wUam4B=EjD$bDhQzPy`d zR=ZEP`TUKrkdJepIrG(yz!m9>g)XPgeLC0a^K_f4{p;oCx2`j(%sCeHI&qfs?rHlg zzbN@7`09n1TCTL2TeUUrTiv_gPZtzcO8c9pPtUv1GF5)!@z+N`e_5LMMN^b_ul4p9 zudPbIH*Wn>Rl<0tXYEcu(cXHF{^!jmpIk2`sMqf?@wY#?;q7t`_q=n4d@-_B(mrbU zc(SU_?dtozt@CExm*Cg4A~I~{ms)R}w{%9-CVq6^TVlFxc8yH(9p6Bz3=wQ$!1&ooVN$gPSxz$alZELpSyaIy(Zk3)ZRy{o@GD( z_Gt&Bo64Ix{+DCQ47&Y3KYH5k_17p(W9RHQeU3ZotEJ!E-MISL>)YP%zUbV}o%L&$ zwf^r-l6&J_*Z1w}&z~Kvxcy|6pmk?do|KpB{@qE|%||}`4}A6W!tB)le+rK?xn94! z!|7ygz4`5WS>@v1NZs2m%JO}W%jfgGk2hW}|2Ky<%S}yBL9i;azbx;H;MCf-u>14v z3hw<_c5z?M#jC4!-)=jjc=XzvP0TElCci(H?)Ckr%2F48o{ou8ueZp0h?WKmY<~M9 z+dx6%v#IuV9n~`?Cv`Q~$n4hF+1vP!*S`70_t;CHezP6&`cZNFP~9^nG2_rve&xRo z2-p9bqLsBn^4R(7aZJAU+o$?vEik(sw&}!NuFh2)chk%JKU>+JV2V0_KIA0Jzvj6Q z(-zn(N(r*9C_df4@A2oA@pTwD8O0p`?V+5u-#4~y z`SjJ#sxI`b2^8ONxMJT9qeT(x&w5Up@pgSs1BY^0!?UMbJ)iYda%wcLTQaTiar5ha zckWvzc1WM=`+C~?@yA;izOPaYwQafft?jx@EW@OSWwZM;D(`71y9a;xy?t%3QMi*~ z;?fsA%?lqqie7vB&*Qu`)1M1Y6*yhEYDH0;oz=r8#!Ea5p896KteLiRf0g-Nt9$pm z>cXCVS5C{!Sxd2>+NxD!be{>`u=B0zHfH%w*Q|Ec^)nQE(fmK zZu{&&$H^Jg(a5U zx~x9$$Gn8s;Wg*H)A-XDUcD=ptr2CHT~~1Ty7zj!6Sv=+ef$)&V0%E?)vm|K{%y-j z-stru>hq=dpG}khbsAr-5^)GP^E`Fx&sg!0e+>VZ zU}RB~X(0b2_3_u-`Np=VOJ_gZmvVl!U5?AU>nA_^XC|`8JeTI3Vs&zb<5`b#X04UG zc0XcgW^ENYrhoW%U|#WIjU`J@AM4THvt(YreZKao80LNNvt+(J-}(EY_q#XFr{<dSv%j(JbMo&)fzWVd|!*iT-uI=5@U~+J7d})!+y!o8JYyW*Z-ZpQN)Q`nicJK4O z(3w?PK+$ols_a0YiSKI&gWAcxGrT#uBu|IRs zvHZ_do}ZVsR`9y`xO8&blzQ7QXTzQO*M0x|u=e|ejt-~zxJBB?X=t;(z8xEJxmu5ID+{o}nDX)&>@ATVk zImh?E*G_+5+q821BbT#!+1Y}DD!M%#{nOt({cb0wFiS8p`Gikh^T zc>nUsZI;&8X$ki=T&{gso%#7wVfuj&U5}TP{kakPQ_^5&!!cdkmU-uozn5X_ec$}* zyz7zI_H}0t_59kUKUdad_I2MclQ*Ay=QI0Ze7t*$_?t~TKhL=td1I}6_~H*ww>e5r zdpG^%?D9FDe~-AFxh_3F_q_gorK7Je1ZmpN-MH41rN!~msjDw`C;tES@sNq1e)P5G zH{Z=qf1mF*Q~c4QTLIiIifljEM{W2~r_SeE_cv_skFR}7nZN#}ZaMQJS?71pn*G+d z-o>A8mKIrA=fw21=E}j>|9|zdHlN#Y`do^FR+?yd{Nuo~xk*m0%1z?oy*infc#E$l zaGpKC`{Q1p&uwoSbC&OP-6XmAmqqlow1X2q^u+gEYoFeE_=Wj?&Z_+{r9xOkCkb_y zc}joXux$B%omcOdz5M)jxw-Y5S8I4=oWC3|n-Y0cYKr^fTYoz9549cMmh-utx8~EI zU%QqDYUR9ZX#XJ4=x@7cGd$h%oO z^HRb4)s9QW_S>B0|NSw`tMK?8zPD+bJNsYjCC-gsI^n~dJ2P{Q-aHfY4f?qtPx4O9 zJbvyjmjw@}3huWNl<8%?Vx7Bg(&D%L>^z(ztPEEsr+!?J#Ijy~$;Dr7S2o`&j(9)) zW6F{U)yt_*=kNPEeX_#MzB6Z>_WQkd&G^8eJMYgnk zed+tt%b$H+8YdQiq%%_9^T1{G#c{nAJ5m!x*Pq$AbLNJhM{aD8V{^@XG%GtmFobjO z=E%h{^UuAnzPS9={~1puW2>HvRGz<{UQsps#%Ys!jkA9*zH8pP;{Wy;XBTe&^4V^B z^WQTULT_HL+#7vtg6Tqq{nN`^kDnG()&DrV=bqg1>d9x<>fgL6@A=?#;~gEgJAY0s z_{AdpW|9=|?!CMIFM1`gYky5r<3jD1u6KS2u$|vtt>!20EWF%g#iXf%&z?(ZX=Q~< zcdtzIomIN*`1H=XZ|qNJeB%&))bQrq?v1;u`?t=t*RTq-S^Vlngnj(`=7WW|HRhl2 zIledd_>Iy<3s0X;P*#7uu3NUzQ8_lCbdIUZ%3ane@o=Y$h>a=qnRUp=T#Ms? zfSZlM`@?liWt!inT>mlKVeOxq*6tmD=9v6Um63kw<~w(L)${3`y?nFMw;fFiu|K*y zIQ1F-_w`oE*Sq(AJ^W;o{-$?F3Vzv?pKNqtSm0K5 zdcnpR&b3cfwy>`fDq7>orlV@08MbbY`Poq`ljvC00G0oe6F*C zEGcEngt8`u*uGF&QS99}@BgR7OEbmN+&8IsxC9?l|MgXvlcUFI>T4SV@72q1B+Fm< z^E)lW^i;0gr6`T->sC8)FMd8lnX&QOt+2HZHQXe3vok!^zRst%?(TXg2G3dDrYEy6 zuS`Bx_PHjpHz;V;+_<*Dqmk1tI80vm`otE)xsF=9WE)tTmX&TRO}2QvBtTiILuN8<)Zn9Niu+?z#DSN4QG;UmJmk#e+ER({k z>0EG&OQbdObJTUe#-7WZRR>MgI3`$eR9;F6u3IWK_3^TUSKhq5cH|AOWJpIt>`~_Z z7acX2X06~^d~2gw+xx%{sVTGW+zy+XnO&yDs1&d_=R^H%ai4QRj2EO&uQStmT2c}Yw`&=xmCL1(}3vu;IB>wT&7WPhIr zZ}z(2%CxXW!YnrXh1ek4RbJMxMbG$J<3^jW{o`!Wm1L&q+OC||r_yZg-* zwpqQ*M~sqYIwWy0AGpz6Cd6>=kf7rcv6wDp77j)wrHf254D%xRJ3bt`|84p0=0$TE z7<^ok=KTGa$DzEZRwwy%+kWZy?n-xr&u@`)6?a@4{$H|UPhFmZh2P~oc9}wP+YEJv zUu!lCq^|m=^l#sqF1;fK4ISU?xsPAjG*$5Uhi#5ZU+!$*+r`mQc5lXkABW}}`FLDf z67oBgfBpYw4Wc)i7ik>&^>%+QQ{eeoVv#IKw&wS8&+fH*{lUG`q5J7=;hB6^+?Pr| z6&~NSf8JE}{k2Q)l#6;@3^?~OX-l4|nWp2%F!4!WdS%OGy4KnJY?c4V#`ofg@}9T5 zE9!08ZBLc5&t(;|e%`wOz~R2=0*S_lI!uAx7tA*-^DVj{E#JNy-S$XN&@eW>=k(Z@ekVw$)x?vrU=1%;w^_AAc1M2jaHMfc46 zysWCMakchtk+5aySucCK<2NkdtIAne;FKS@FI2nFo5{H3)FXa{@~|hiUA=NC^$nuF zn@^t-6n9K|DzfpMaPvit;H4U8cCSc$4XoaG$eMHMgd{ zUaEazrnk=D8#3QF|FK}$ko5B0=YZ-f=RBveO6aQiYCc<{HcxZq@~bk!mwGnoFS#hr zemP;L(6%MAN9SERv02TvC2H|@l{Y6+K4qLW{LUsMo8zpep7BgIdSX^DtD~O1_epUH zALbM{HT8^FH-nTizAkVekK>bMjc! zcYQOhSX$38uh6!oYGu0ygXheK#^W3K=Wmd0;nBImlf<;B?ByhniiVhFS0}AoH&gg- zOq=r#1|EiKYK#v>4zVW~e(EUGGp>Fc_uD?#=Hf52EqB|i1s9u}a%x;Vk~?RY$h6px z*^>At?w@9;OnNu%W1m>&!sQOv zLXP`S+W2|;{VR{QNZyuuET|^HQ=t9Wv+iA0i6%>nz@pDHn?G(9FE9lP^9yg%<&ca!HI`!wORf(!=s;X`@}#ysy;whP33{X+Gz!mmv3tP zewljblMYX`nPT(IfXkDU1a~_qSvp@il6)@l&FS4aTlbvO)TptH2tRo>*!th@_>``z zVjBg2)GRQbuetH2rRVQ|^7`qG509TOU;a{N-<_QI-7zaaNi8?=+WGFu2M@DVYkJR~ zOaGNJBWhvU<;fYB8_j!+maa(S^NX0{^WulWc9U7+pGqwr?Am8-;8&pa{AcjC^39Jk zHaCX$8@DcaoONp_x7gPE{`+s&l_cuOO;?_j_xG6m{=DsfSZ8kEVsvK5abZ)DKBHSp zuI^dCt>VnPSvRk-x1QM(()Zl7)p}ydM~&qE>{gYN5^C>0Wq$d$v}R7)oLdI#&WmWC z<&&w3P5t)f67v<#m5-X2O*VhA{O{D|?guwjG#)wFWtacnOL<*dxMra2tsj18bI*{;Lys_yw#Mz_@euKI3$)k5>` zqDy9T<2N4J@YVLizI{g9+h*H*{QErnrg?kDrwfyN)DM6AE!%Ud@X`~R^4ad`Qww@5 z_ut#P@bo;psj_E&*X&(YzL9qdyRX-|iWBGJ{?z??D|%>k#y^z|z1AlmKYosV)SYmB z`o9|UoVsg9KQBDq^yI1dva;2EyR&c2{Jvy~_2Q?>Mch`+X8SJP^!#yi{jTlPUvHWG zss5M2k6&ABYzpVyn0?{Qp%b0D^L;Kg6n~3-+^lw8S;yt`@lRfb1-|Qf=BMTa&$7Q3 z?lZS=@$Ski6?g8vo2Atg_wG*8#I! zi~Ckbe&0U-^ViY`ua11HnkBaI`SfhwPh7M5`_~m+o_6X%!H->212fOc=NwIU>Ns6+ zr?A@m%{wWV+ty2;|6z>Q`SyBx_HF*NUwQ2-B<17ZtvYkLNpUmh`zqVkeRKBa&{>va{8)jJcnsrK;&Ft`9 zJ6rGXeVw{myFBP)I(u_|{ORMJr;miKTe^91ncCXi8{1z>7tTpM5o*pHxc7_q9lqz_a>M-M-W*N2LPh z>%Qx%ynO$O&e>g(IoIl9r>*r*@n4ntnX`O%!M6`@_xN%P?zgi|`+NI#S#9p7Q!}E@ zOtA{xT`t=A$;n7^#R;K(`|?C>Mbi9>-amPFe_d*NVPBVW2V16;Nyx#slWxv1xp8j) z>voe*#XE1lkjo6vNwDDaO3beLUG{dJQQawxBN0dDKE8Qu=Ki(i*CR!izy0&}rlQqm zv3}l}3#=r+=bFDb{a?8JcJ>{Yqi-b7+?c<0^-TerH~+tV-u90_;VOgcr4k>uzV*M> z-FQ(O_vP2R>1(5Zn3(RD`TecUaT1UBBH24W$#dtL7C${);5a#;_MDyk{++w$MCx5V zad=I}Z`o~~UT&w3=cWA6IGw)rv+|qGkLK_1jgn7k`7UzY=aNRFVaLKn;UC%cg3X(I zU(F8_oh|&x`~3O)G2t;sb{}WEb^7_SQ;nv@kk!?!&jDyDgWtiFcdbdid;m z-o4I)PdjGZdCo8est=>j}an` zAG2o5sh=>+`y+5uxIZKPy>R55@Wa;0%2$||_awxY z7ni#}d~xu5RIAadp2crD&8NqncyrM3%DYLnXYzl`?usg(9>{*#cwTRq$|6%vnIKpba;{}#PIIT+PV#WXFL=e0zDapOq7i#N^mSt zRNc`cvXWsLv!9yQxg?7z4o5EZsC->Dzv6Cf-1c{NlNkgSxgQYWYUjw@mZoMs1_NyY;KcErD%>Mer~=wg~|3)jWyh{ueQ7w6I>d4 zOZte)k&det%}EFPHVE8st2=k%bl%#z4sU##p9{eR~gF+kN`^f=8<+HENk#bAw1}SXu|;3j-e}n_CRD(fe=2c@z5A!B8{Y8hZnu5= zBKmKi__qHCjV?UU~U$FqIUlY{YmU94;2dpcdXd&VYlFk zTD*!ogS^Kf=d-RAa;MKd;3#Fg%l3c&|Lb4(?|Cd;8(zIZ;LE0)D~{YfGa*{TOy$g# zW2SRwM4#EIoBEyz+?nKiLV1$To*$2#ReTtyZGS2nY@J@5 z5?v&p_kN1}wrH1?-~KpWYpdOJYmJGTRF=2yU-Om6Ywa#*9JxI0+?jX#)J z>*?(5^0T+jrp)`Xb8pv+IoGbc9}x4>j?DSUp>4)K&-jzczB%*fI)Cx7)SC0**>ZdN zPPI*@g_1v<1By#)%QJ5j?w?vJs>8nSQ1spTk5%7#r5JVpjmbY;W%B5m)N1#YMK?Be z%2%KMUA!>mq3%tVlr7%(u1WXyugQq|w|TYcyxxnSTOSGQ?s@vYqN9cX!?bUSjQ74< zZkAcYEXm1W|D$PUW=vR)s;!@Sve5&Ez6Qp$3mv__VSG6YO(*@Fniuw%TtnHNR^41LHT^FYf$0TzBPB zdyjQ(!NG(X-|j^8bz2-sG1W9$p}9`$$;J{+zvXXZefPXwzVg?qiaT!K&xWr5XsT8| z_iW_%J^QP&MAxs2T4t2n+Gg!qm$z+W(8o;+v!2z}?Y#9Npi0YPZmJbWa40+LFlzi{_^s~#=*RFWD;F(t94NhZ4 z*JUZf!r9;6OqpVEJ?f;)%`<6^GklpBFrD#PTqMJwettux2$SO}C)>IC<>qti$|iJ9 zkG_9*g}%K+Z9qkD`>e++8jr78C&E2v-RoTKf7+t#=4A(j`?@C<$z1H-bF=l;$2WhI zf^S~S`kj@<6`O8&{cY^)pVu7U*HdG^cJ`XfUZ(wdw|~W-t@}^^|GW36so#&w`~Ch-zJLAi|N7KPeDD5$ zJHM(f_p+YP6~E={uP<7D&TOg1zvVaO;@7X(&B0&3k)r_p6(Y+G5xDMZF7|(&OBpg(SE$+ zhRZ{i<=3O0S;p`8k=cLwxN>oV&V`N#g7Ul1o(P(+C0tB>v;FJk#qT!EiFhqiKBpl5 zUg%=om^)8=b?nw{GidAFD zRuidkW<#Y3+myB_tK08{av%F%f3q>Aa#7{v+YeuFUGZs7xaRb~Q|Byh%g-(2;WC%6 z5q=sm^T_Y)DOy=m+0=Gtzjpe)@?X5V&$&Gtb$%T^?Y4Td)(_z%{U=YY_s8VbcD*)# zUo=g~VC#2TE&izv$5Z!zy083ysp`&Y|Ly<9UjF}gdS$)Ab?4*Pc$KRaS`>+1@d$#}gt0e_g_kXeP zuCot&x5Q(1Rbf))ovZ)j|C_#=zw`J0|G7`s=ihE^6!`S-^S6thbwR}@$()%Laq$bE zmFaO^q$8klpa>;3kF4|J&x)ht0VD>-*oU+;2ZUeNy+MYI^A7 zP3KY=e>RkGzH5K0vDxdz?Qi%a zdd{CU%l&tWNXo}=>$GE;DDkoM<`%n6wX&Q47r8!Hur@ok@2BAU=MAUpwiFvSUo|a~ z+~?!`=x%9A-j>=8CfV~FPaCUCMCe?Vk1xyEm2-Gsko^zct{u1kt-5yez}D^iSyXTH zM{2G-bm~`3pYd`YQmd|JX5-+~9zM90;yV+xoJ6n$Xfitr|pNo#v*eSq#fN$H*=!vuUyxDUnqa>~O zS4X~m{AHbP-_V7I_s{9H9~I5c{rrhji`lK`FoH4KRSQOpO@WL%!2hF*BAfOP-t8FdjH?mkM39U{`_5U z_2vJM?j8I8P5Y9*zyI%R7e8y&{eRv!)vcS5V6fwl-v90YmHxA&mwQ}y=;EqRn}HOnpS;PL3g-^7)qLi6Ki zmH(IDqVBc#-kTKnJu-Ln*GG3**I(Vlu6A?cN4IrvcP1x?Dfi5}cyUooj_2ZUR_A~J zD*b$t|Jjb^nu{2fP8_-yzispUL%xmb4-5ZPrS3Sjj{W=Vn$MSHWKyLTw>e+Y6TKf6 z>7PEm#9HhZ<0+nK*+=_prMJ{x&v_QU;=;d|i{+)a9iP9>P5h{(^hdE{cJjX>GfIC| zZT+`wQ;PK7pNaa@^J}IQf6Ow!9^%|;d}qDa#TUL(X2H9s{MnHoHTOg!zrg4E-`{4Z ze7hLBLECh>QBvxrkXby}`fu*IBl|k_)n~hp8+Q4fUH9gy)%o%b=0f}a+SGjQt4RLi zzkl|QAAa3ZPV(mUI;XDhe=+^{+t)!4e>T2ed~Cbx;YV|yJ<{P+31LuZoxsJw&1zU{ zxt5!Ms;SbIIN9f=69lbqv5A@}h_Epnzj0YiNvoqUPx=IhZZ|tuRueuzIl?{4TQc%XvA8OYWhJbb!gEqoMDX9j>oSPg%XTzwqB_{@Eq7TRb z?EW2~oX4`j)Z+F&^`-asU-rFwj^BTGWShzFKd0*zf63d-J;V8bb-n46|9@7^UpMQ2 zf4%UZ_c43F_^#7Ce&J*N7x}I_eKq#2-|he0K6SrhWBvcT6aN3rf8)1c?m3>MV@DUf z`u~^z?|<2jeLX)u{}24W+W6_YwLkv<3;%PU`+fbt_FwV0KK8D=>m0~^J7IDE(`S=+ zcbiv*AG|hm^6px7uEj6j-ac~E(fUQp0&$}$k>5U+p7T0(C-d&D={_-VBZ-c&HUiRkY z+_N9U?r*z3L;i_)>aqGiHd(*VnZMciJM!|UCAzMc6HSwTo-12hXusA?|NJg9Q`xe| zV)i@M89zMRUuDZWKVID`-Pv+~QJ3KC-Rq3jwu;QY{O+xzbKtMXr^Oz(&(?l?`oSsF zqwn~Zz5Ms;>x2F0c&aYnk(p{@?zHrJTlmi}$^SlE7VnpnORGGZ6s$jWzG>k7y3Ni7 z7w3kYyMA};taIg`H=Gp<*O~c#I{)9_|Ey;6-M*Xi`DrM(+{Zs3#m~-UZETr!|7M!O zrL~M~LD@OE%T<-m*uLM>C+u(QvH!!{J3@vcos3MEO^bcqC7CWPTGXs!vPkB>(+Xd$ z#drACKW&d{yszXlx$8>hbKMIknA2A)MGMb-xF_s&meC9TD~^o42IlK`Zn`hPqBXUy zhqcJ^-%O&Z>LO1$SWaK>0F*VT+JYyIQRGRLfvW?jt96w-GSeO6(Z>RP_6 zz47_eilyzF{iN+@*d3F9d__aG`quXidR%fb;zH+6cW(On_4gkWiLPDuvU=2>n%{bt zqSF~BzhD3V)%viH+4Zr1wd;j{oi_{j`)2<)_2>G(z6am`i+p0w5|jMp*Yy2;KiH?g zD>1wO&*;L+5*F8UPdru;I*2em8;d=58o0q=(`~1=UAHToY*Lweb zy#L#uV>$QgXa5i{Zj`t+x2JbOP}|nR9V-K7f4z3M!LF=&VPr;ibY9$tFHheJKNZ|G zwPW8pqh;ySG+F0PuAQBGrb_43g_m=a8b9a8=@xygc#t`1LqPQXXH#!{67PMPdiBWO zLrT~8u6rHv>YP!R*_+2lk4-fVT;*HI6?1Q86MO9R+26NVJ`ni7&`R4>zI)j%8K#Ig z)3p*$U%euyBgT0=@{Wn>O_Az|G~fJ?=vJ9+KObA4mgRYT^ur;UZi5q=rRuD)zb(Vp z$E|YQYkO|RnP>jT9!#I3`~B)+%eX1h{ADbP%by;L;$`-bv}ZkcLOF|HZNtCkS6LPn zGAQ@nOaGKIv+Hj3#_LBb0uD&`dOt{Gt(X? z?@lQcw+uc~xz+HX#kG}_r@ZrjcKz+t;>f3$e)Ng@zEjG-;cvu!Xz}G9bJKuLX{{z* z3ocvL)hS72{g;z4Wtj?KHccl*O-vdQbu3N|+g9CDi9%fZ{E z_QK$O(u_5)|2)vOOc9%{G1D5`Z+tai={nu`%<#-Zrh*wQEinh)FP?o? zr453YV`>F>f%8Hu=0=t-&x$XPjER$W1Fac8^2!-bEO^qwuOldHLKymN~{ z_7g^r9V;ptx_3H#Z0P2w``h~dZKcPpe)E$FWz2FDUMGk7pDK20r^bshOo_rfG!Zl0q3 zpS5xdpOre+95wC`kKFoaN$;)Nhny7v0cnxd&;!K68PzeuV~U-P4zGd?V|J%8Yw|Bm*<1siVFyqdQ!q~f}DOu1VA zde(=sTp}|I)0k6yr20b_UOU>e?6t9*>&^1Zbt<>lUam}tzsY%2(tT%N@z;Hu;(d*j zuXHXmHXF3G-RS*3E<=Ge98uV#jrFHg0xdywT>K0W2;#MQg| z-pu+_^q5y?vWJTM{{5+w-d(G8m)D)RWzwdcNqr}~Y(7o2B$Xl)k&?#&sRQw_OWX7u4{Lu>6{JoPV>FcaeZlk(ekIpA8ptF((gO< zvM%QS3fi4p|sw-datZ2LcoV9Ll zRb?Rh8I}hu)YiDOMUf}9+UA$s)%;NQL#P97Ybk=@rbn!-U z&g~92mMqStNn7=P8aKa~bkV7MOT??IOvfTct~@Gb4cYQ@v$zD~@zVcK15IPaPsrG> zers?3akBr>s;P<3gOpzuMzs6;{SW-{Wy`{np>mj09Sf zu6$hJ9U>n(b*lZX{e@{pZ(p6^$oQ%Dv@P(r_^MPB-K^tri=%ZU1Xrp~+x70o^o_ey z#8`#2(qyk5>HGg_)iu_wmd16vGVYX3Rbm(KyhFYHv(hV_(|X?*idx%7sjn8Y zU&>$FPQUh zeZ8&n#g}DUe{nWDEo~*{)aqH`jwO`)-DR`o#{r}PLwX4^(Ked@Q_u7oI zjDC5^wV&)cZyD!K+5D9^XOp5*&})6=i5_X83mV#3veE4%EKqNYvzwc?6T`cB!@&cOU%6Z8+gX;Y5~mMAMa zm=ZtpRA8sTL z*Kd5kFtoAexkZ4VmDLH3=cQXZD^m`58d=@Su-y1}mxIa`m7{Yn8Jy9Lh^s%aC+xO$ z^p>p`EAQzzJhgds(u9ZQYNE@s#mg3~d!u7_dPdu#XyLn)g2mm9e15X&-Pn25 z%rw)KIv{0hXtv~(*yIH&lMa^2NzQVdUw&VA_fs>!)NS`9Km9uXRjf;FpYJt+pxfSQ zH?^;?S*5D3(V4T{ z{(s{t^HO$lZ|2>2&LA<;u}lB(0jWx}j%xy!<}O^8_RnPTOt-^PRYKZ68_#n+?R_RN z)noO`WR=4oI$oZZKl7fk!rb`N*N`pzzROx(*!Oey?$+6)wW2MqzINj$JMF^Gsb4CN zT1?v6S>qDtq^iyEOl6Hnmxog2+V@&4T0b5!Pjy`Xe%6L-JSmz9DhU-1s<%}Zh-Mjt zrW}jfq~&+0=jpkrtO~Z4A^~g~oGVhM-?b5Rblur<=EFuqC)Xv7dAq+qV1M;uhPZ0L zJJaX`Yyn;?)`;FN2(q`9Nm$LiXHvlZ7n@ENX9U}enQ;i6eXF3jMd%CjLxFj+jW6u{ zIYX{-tddS<-BV!m_-61~&Gp@{)W5FYC}8o_+rhwg(t)Z;awU=O#}`|#e|-99sNU@M zh84SCT7T)?6q+8*wKR2d#qz#@?hDS*GYwZ?JY+$m{l`uY zt(`_+j!%17&R#O-Mqx(bCbRP{uA8oZ)@@ssntgV$jL8E#`|ich%FZwghOScBW^5$! zr+L!LFYI&5(Bk9nFxg|ZdVav;V7N`l&`DHn= zXYT2(B9RL2k9dq#GYzsrfY4gnclN@6; z?c@1Q?fk#wOZJ|FygPK)H?+JjlXd4bosd%eW@kFL-a&=mN(Oxso1#kfuU2-s@~UamGE8Bt=Bk3dUBQIfma*L3)^1Yl4t3?_Q{dK>ZPIZ(x@uQLq}LtxVM(P z6Lx0M|CyKBqR*)2=FY1;A=0^{WKn&rgUp4OGdi7*&NN+|79Dr&>+hI?NcX>?A$EIT zUug6%-t~6c{f(ZsW^dA0PQA@{GwzLOmH)34p8qk|B4Rz&CmqwgcwMi5gU3dh-C^HT zj@E7686Kf)l*xSe+-J2(g}1Nftb4dMWI>~A?anawy|>NcwoUa~R;nDUTzG$ncE
z-IGG`m}_;O-cxzO#D^fAhi0`1B>8=1r8dp6QpHyr}=tM2)Lzl9${BO;5P~wea37 zJ6Tutx7o^zcHJzmyRNo!9sRgy#oumY*0$?GoV;sh$$dY1@9-A>?S5-M{q!|imBMDM{wEBg*Ky%cr28U_yZFQ19bO&9v(^?h5y7|S&-uAo->rH7(FD1*Rh$?-#R6J2{ zW!u)rHb-Ygeck9|uH`Md%-UZe& z4_!5F1^e4?`MZAw?U?mCV$v_;_8H3;dMPY1QLt^8AmAlBbK*{kCmpY|rxndl^;^ex zb=S>(8*OyVAuDvDedQad`_}Hovm;>4~~?Tln=|&-r$SC$IWf`TFj*(oKtWUo|{F zR9kzg?oII>_M`cnD|+>R+q`~x{ix)_sV@?5WKNU_Jvmd_X#a&Jmrk2q4ViGQ_33jv zHX3zKn-p9*JeIHrc*S?7TzST`_%h^Rf z8TspuEx(?q6h3!be&#*D1Z~N8Xt@ihkey(yR=a&1jxGmb+MW@q?w{(j?++3W`&679VM8DN{`=>_{ z_VT|NznUI+v_bnz{wJB~UvyKaO3Hj+c};mw1aD!t!NJL{`Ta`ap68U_p3Nw_H~nTg zZ%=nguMXGL-)7;@X1!T9*+*u_+edFc{j@r@>X5HbdZuX=lbX<01MR&Mm)7Mu7K(6+ zTyjq}KmYLQ1^x~5L)wnV&YXKmcizm|uU>0A78-dKNr&3zoPOTCZ_^q-bpi^IP$w|;emYa$$#xs_q;S& z9rN>uyYr38hueO#ypD~#`+Kr|)c1*fepfGDZJgt#@wRWz>wO{9n;cR^of2!At1%V7jE%P4mwoSExM)7}=5z19`pqxGOc!1FtX8`+#5z6UlGgfzwYqKBzdk;H(*}vDp6#xJ-#m;K?RJ(uQkl8G zz{vJJn{)2lUcsgDxgkxJPYdo`o8fOOzFFvc`ZV_=o@G-cIWuiocttJGcAfdWs^H_o z;QZ4^nI-rOk1u`yjeX;-@UXor^RG=kl@%o^pvkq`=-DfM_KQ!KhKb+l5WVMg|Eg=I zK`D>d&NH9W(@)5s?8!A$e0Ek%f1A-_rG0vvYj+w`@(lyXeOBZ0v$TlqjGoID&WT&@YW=vhDzRxp z($#&oUsL>NsufNNea+avx;JBWAdeKw#>Kj?Pi>3JxyH9PNcnWxzMs2N?o}M=H+sC* zwbHoS=5)Y@Wy)GVJI`ycQJQpeZtv;_MnzSP#?apNpRaS!VsweqSb{ za%JiCd*6%%UKG}UO|SoIAS^1;2Euits`K0$7FicfnEV7*EXobO?xiX*DOP+A6JITutC~h%Nc*BJc zm&>j0*YBQQo%eOmU%&IW^B=FAUj43Q`)jv@>a)row!B@e6;QKc;g6eASM#i6i;dVdV7`0*}dcWg&Q0W6D&j$IApd@xb%{*YlF_$Sp}ifG}Lm+GoDP4lUNmg zdVf*n^G?~f?yKMacex_);Gpbe%LCcf>_u~fG(SI6T%mX@td#H8^y;|P4^^)0Iahhj zBwfXDZqd2gd2=3oR#V?|w^(KV4W)&$VQHej^)@VL|=)p_=ts={BbnN|dZEJ&~ zPDF!((hV88$VHDTrzcHXwPI$+B4OEgf7ji47QHt=KI?DD^4`g@FUBF_`IUS~D?5G7NP2qI`r-!@Qf>bKin$q_CU0Y_ z{amv9_udC@53~IG{)6Y%{DR8oGZ%4m&H1zL%r@`dJ#SjJ9PjUZ^h&(aATr#FIp+8N zecQLME$QfDXz57XcjZn`(#jXh`t@EiYCX|XUH)X+jwO}quM!@fx#sS<@TI;^d^qce z>6J%9(x(4e_(!qw(M5}gJDxm$`0AIz*FAdX=7#aIi4Ru$*7NuBe6=o}``CAmzAjJQ z)6>u7f}7_(S#+pUVcntirm5eLB!4@o{w(^{Mq9>q{!K4Se>{4Ww*1a= zACZ`OMMH=8-=&qC4!_q8`g@m+{n#0o*H1Ej-kkP-nhSEe*fd!{fOb){LG_EkH7q?@p1Df_G0g>`${(&cb&g0`)pP9z3Kcn!zAt$?me>U z;=AX+-p{H%BdHe1Enz3SXzIbZ96po4sd z@mQGX{LKDall)$)szHX+e%;1xM^AZl|I|Bv|JuZ_S8b*%{9}wW=I@THyu7%CqbYRn z)#ttUw|)_DaN1&cru*u@WA`|BxhsgwnR#C(vV7*N<|oTrf9`Frev!ER!!l+4XV&v8 zqTP@2e~@R?Qr^_1mB_><8n~%_$^uo}7p~D2yU&)ynN8?OKP0BLccEDa#{oT#O_e*2 zWn{i&VBhRrq$wT2yg|+Qr&Gd5jr4|f`~e;5N!=d}y?Rt)Rk~RcMKpzvJbQcNGqXTC z-(QtkDicM7td(4TsH80s3-ecgaqAFgrP5rHB@)ZSHLguG=JMFckaJHZsl9#fI|p7j znD(0~C^u_xx|9(2GT&6GIln|OP=Xsi+=|x*?cD?zu_#c}m zdw72}7k{mHxpu;ztsmTWwaS%E>WrQHVv$6FVPiq?+C^#W-rkuztDZmb zOcF=D566s^re|;5p0#M}OaG4PeVpfZ7Fg|Es#;!f-~GfJ<|Lg(P4DLvaBRD@o4qnF0%huwlN{xg73-_vyNiRkf*mgOCOh%m#>?)?^BvM`?F}v%|-8aWc|!pe)sV7 z_8q@mFTNJH)jysypT{ATM{W74cLI}3zSnJK`^d!e-)5c|%LawU(%G-RiNzNsrq1!Y za3cAD<^S31uI8-?&zMrt*Ie~CZ>4?z>b#a(Gu7n_d8H~o$K1Fix%Q@L-{xi7%@^e! zDF;h4CV1?h%k*GR{M}6x*RPCTn&xjW@nPBW=DQy%E=bRR@M;(P-j9yU-EZIQE?bz8 z|E={kyW!3Ck#XBYU-`S*v(4sv@JhtG`n;~Y`KNg-f$y0mCOC4ejQnZ2`l|0M7UBIF zHMzT%X!q4v{oMbeLqMcC_P)ugx{Q@~vhT9D-)CwrQd+^ntsgF;+S2m;v1fHU%L>n( z$7Yn*9QV4m(e7!)>_Zcib8oGD7QJ0Yo^RtRhIL(@g6$r+*o+iTv>uskcbKUx(BY4# za!U=vgSmop>>dT34v4u`!dN(0IDA>zoCgsOQ?4|`KHPCFBXeie#1|@&OW#Z`@YFmh z9QsLYTDh0d&wzgsiba#7+0+(!R)^b8l@Q?&6D|)?;0S0bXgJ^}*ZpH}^r1?l#+TdD z7azDS&0>?IHJ8Chec_99g;V6+%U^fANGaR6bVIuMM4`EpJ?a$07R?svagjK|`pR>* zi$}-)diOHx)7EbnSo7+rOngzq)j5e{`i;)GNpp_4zjv87MNn60@;sg`%zVr)P8O%4 zWmwLxvnWX7pEg~{Kkrh6*F?ul$BsVh5AmF)p>tG2O}>UZ&q1jc6J* zV+)F=KVsn6BCb4-SF0>_Q81Lq1{&Z(H_O(+h0ca_O*Plx&S_v*_XZu*LMYwq6f z!gJv5bBHWTLvZ zxVBiGt#pIT)IV(fS1xlJnVe?fslHmTkDUrP=h8=FX(M>(IDmNDg&!4coZq29T{MQezI`OvWqbTRHyUX}f-bQRsS@ieVoGRg9ZAKWSA_+7h;{#i{AAlwiT_d73|z*69iy))I+5cwtUw zmXYtpthZcW8YDA}q^*Kx3E!@m&2FkF6eFVH)yw^O&FfV*CvNntERzmA-@N2^;;UxS zAE~LaXPow_MlNFgD|gJ6JvgT4j%e0u9r6Fa)9+NDeBYlP=&k7Y=?C9C)uxN*_TQOs z>de259iM|V<5lJ)?LJ$`tdz8IMSa2K<=lNgORA1eo>6FJuPbWxdiRTsx=*IroHb9# z`}{NL>AeRZIkHtIOt0g96ZZUW##;G6!;4#tYF$?L-q=#mHN8B{;KrTjN4bLMPI{mG z;jX{yig#NNKk7@o{CJ{Bv(D=`iykojR(c{hO{k?+GRJs*bWqExMO(R>v<>9aYa0VF6MQyt&3hL34GWn%xQhy{@Xmyqw!JJj@K`gl^s@I zxBqQcndI}dZU0SLdB6S>D?jHxeUHr66w&-CA@vEu2Rdvsp8x5-rsOjBb9wB>jJL_^ z2HQK7k3Zn_xbbzH_Jy0-ib~tkOcsWfCLR2+>)ajn@;wJ)uj{!@ci<6tc&R(D+HB&D zqZ2~7GKDmvyKdhv(>>Cn*Ccs;-K~@P@7J8o(e4k9FX?=E`_Vy8R*mk2wyqTwqNURn zJ3jNfb}lfQXSwmjE-98}DtlL1Uq087-}z(p|Lh{|kGsxl`m?+7*1o#?zff~-rSA5l zfeCS|%`9JjemmLwll!A?rRP_q=JWo%CfHh$&c5*36vrT`{^bYLYG3x|9a1fen=iLX zq%yPpL~`{ZM&J2IIXqHi)Xy#r7QU-7IqKOy-DQ2Ix`k_w_Sc>bFg6oi!r||CtnT8I zGS@5FXR?mFPvW=o{U)XMM0MN6XN!K^F#XD$aeaPW)^lAC&it1PHOUiDC zbj-3lyYSwW@FSCj8yuv3Sb0N&>t?+W>Uy@R>vPXbg$Xkjn5VR;C@#=oI3dBp{$@(| z`)yWVMd#d?PO?nm$t}MzwOUhIBC=VLf0mA_&zl6L1C0OopR1jnZO0QjL0vlUiXzVo zZB?d1cD-&PF{z6^J~0a#RaA{6xVm;6`w?1beQ>(4T<2|RqogL5JFKM!PbF_eEjiNo z`qQ_^`cs9%jwA|hviLUj$Xg!P5Etjh76#+Q{1aT2O7#Nu;t3vsHu~?j{(9pn*)l^^ z!!*@EgmdPzFE1x`WXw99?Q8gC)!dq=dv0u;k&>@}JK|94PEU6Z!xMj|mFM+L7OwuO zGig#!kWr8Fd*K&<-b-XYJ{E4Os%4?`^g7eS*yg96d+gRR2g@9*`cs<{d2acg7Ogj% z_XUW}^Uu*vyR~#Vi~Y_p^`FYG1j*L81|U(K~bW3%G788>qKqzx30 zF47WoyZyRkSAP%3>&XdbkI!}f|E1t_jrGpN^rLMq)7qy8edlw3>^VQ?muW#v?24{~ z$67*8?(nz&@w{=Y4 zJ@-LzLd7fJml+h$LWC1@+W<>YCkIEJkm&iaMecgT=DATdqQ4|Y!>vHWnQo@Ytx!F zk;@JqzjCRl{A#~=D_3{KZkOC^nR}!Ax_BgOAJ(XYk_`EDD<&?V6ytJ(|XUf@y`p%PKU+Q%Iz>?jka`QW1 zTU^zfbV$zIbjP{(ht#yr^*Sx~mnaAkH1Q2O8pxP6#m)GO#rL=vp2w9}7o6TQQ84X@ z!_N3;SC?E`bL>ol=hDDs5f^kedtbY4s*_lIUG@&g8kZL$OTsvlrsg~q>phc@Sz$Ttf8 zv*%sEI_J=o_V9B{ds+|v{r&xp;Q0l!6*wB_-e!7~Ahl&#pXrXH-9f)O{X$Yhe6gd}c$@Nj2de^ZL}ifh*<-&)J_y8UFq#BDr!7Uvd; zo9CWQ@!{F~yK~tY`7L1aMeLA22G(@6f!%A76 zWx0O}gN0*r+m-}9c{T5nz%8BGrYrr*jh4t<zIj{fPx7pL z^;YX}MaEahU4eqliJ~lB)7Q6)1-*Rf)Nh_T_mfDNt>}k6rT5><<*xUbDOYwgPV~YK z!LayAw<4j|EOz}(j9BgjrlwGC3uwB?Vk`V^U6n+^;M2uQ0$cA;zO~a z*S_CL^^~xPp7~3b`$Sr=n_5xNnpqaB+kd6bXO8uG-FDW0dhw3q&&}#)so!0E;;Qw+ zxGP6iZ1v_mQufHYtkg+gY<5nv^!_bZme`8Ts22CpcJ0uznjWf>sl0PZe>~r;BT5?! znl{_d_F6Ca@lt>Nr$hE#O<@P)&I)!&ir;<~+Q@uUE&k#5Ghez>{}h}5J86`cI>A9^ zN#K?DOiFUSpRWhczhd#Dg?Z0s=BW_M{bs)7X`?1Aexe-~aRSGhajYoNr6r`h2o(+MGJGcf|`yF@5$OJ7+sK z+N}Kfb+VdiUD2bj{I|cT_{^D?c=$tqy6B?NIZrFpcJz6@Uv}^F!{ortJ0{D&IPvUG z&)krEvu;=J4Bgbh{qEM)MH8yh53kIx_P(#NPWnu6-iL3q1n*th^;P)d+oq7+N7$9u zNV0fMSbJ*syiGeFPYSI&`B%kXbZ3Z3>$~fMtH1Z3bl&+{@3u^{l`>a|bE29r7k}Ei zSyQLCUA+AA+>Ask^`Gyg^UwdC;+gdKPm^BZnwCuq)AQxa*WBswp3*-f-*(BeziiDf zi{jFvr=AxG>y9}mc9ZwQw3(+;=Px~eCd?wIU%vS$ul;vTN8#C)eXAFjY(CN3dQa_c z=i)W~$sf(>-*$>r*mVgafkCCH|x(ek7CXw@EtfO@8V!{DEJry z)2y|J7f(z2^Jl}6W!Jx*3+GyV%;=GX;TgGef|odgeEJ10wD{e+_w%Qx@uSXyh7HU9 zGA6YrbSk!3u*fKi38)-WJTyyjMWIi+eWlLj%f|$rB{nc@ufMJSkRggipz}hcoYhB% zcgJ%VNA8XanK%31YU911=Zdg~C+_@fptgRKyzJeJa;E3AIe4xpt@QM{pBY@#S9~no z*+Aw-md?eB@}t>@HVRh6gj=>6?@BfaL&5w^juwEa7@$9+tAVLdmId&CRDpNA7ma5&alBMeeTetIEcj zM`~+-m0rH!sb7CeQ>Neb*|!<&uBj0+OIdgFa^}{T>^+;E@;KLnxwxlC{O85FuglXt z7K&DSFfBj-p?cEe=lAx;cWvEtzDqAJCd4x9l;)wHz*q4)?K3sa7hKAIuzR26lg}|a zI-vp1duMm5ikiny-MD<_@n6E1b-d+b>Z|W}Ht$T^lH`+>Adt+ z63mgaE3*@o9UmnMPgW>g`BYsZn>lWlp5XGn!#W;6j$H}vmh#|ZnYT7AdVR>#C-Ob} zEQ*?@g<8+D*qhH(xK@cn`ocYZ^A9Kf*WRwLY&h{zx=mmVK9Q`b!`FvQ05b;MLR9jAs>Cm^LSWo4z*Xa?i=u-Cd&poG++yisUp- zoK<`5!JDkBlh!R?JFj*MgX!(hzxO=k?{n8>ZOGSsUJ-bH@jIoX3W*Zag-xpW#b3R= zMbjJSr!@jW_jPvKKF8_>ssyT zNKS{R#rK&#%Bo*`2Ylh5(aW&)x4w)LgUar=YzTlU1AQKYeH?aGK3y&d;=buHA~@n^~E0*5PAt32K^H~-|GZ~s7NQCdpFIpfIY z2OgXX^L#JGESadMJ|RQUC1ycK(h2vQd@NVm<$YM@@Mi0UcuW&gvT9$`GT|!!mRBji z6e^sWI5;kDd8pL%jD5Wgd!Unt$D~gdGdh>HFkF4Ub>GxG-(nZ-*DMx@QS`TyR(ZtR zqp9AI`Cc|QXbDT_S)=ziOtVkrS@njU`l|HCJE?2snq)US-`O!e!tp+ORv%yQoicmH z$)7jgiZ>;EUG(MYZU29V?Kj@bJ`kY6#l_1k`%!a|djR`!RpY$6q>pF0C#N^9np|g^ zS!GsmO+rOsdC8R@N~d;gfM|P?h%eUr}bYvo;CUC%bl?Yt#ZCF{0X%4{G_#c*3{cNsvZhLe%uL8PmO+5 zA3AQH8Gl*JUa?Gz^YUNz!aLe(o_@Mo9ZqVR=O%x8S>wJl?{anb9pSLEcEWRgU+X@v ziF4liXHN}NR%TE{w^qQn&AUywj^AG6lRWWarqBF{w?FPLuD3j|`EzmA{m6ND=b1_- z$pkF-sqfk>&zWD;+_>c7_x>&JvY!^rxIKSe)Up&Y)1Huv8;t9oe~+>K@%QQd+4H00 zz6HN}U;lKal#9xy2`6K;_*0&qc)})VqOrsvx@}+V`3>)1O1g812kVKOea){cHF@e9 zee|rk^_=Vt{L}6Kcm7;pRhr+`t#(lTam5e6M#*#$1{Q~mn|J!xOj6txcJJ=Oq{z7j zz0YS~$_&@hE>j6*Z{4c-CRu#%zW}dksS}hh8>a2&ij_Bays>if<2J@!@5*;xj67vJsn7WN*Qu9pDs4^_z1>#!M1S`Y=QkUy zJKxWax@-4ne&>qTM-Cy?3g5RWc`UlZkhSwn|EfodG1~dYKH|^gWIx~ae7R}82(b`#;8C_xxSNpm^v1zxQ+Z z7u~M^t=&Jr_&|V0k5KB>EvFaOL+@;Uw*S$91_hR;z^L{H` z&seXt7ubC^Ht)~JOh2VqF=mAbjSYUEl7epEeNYoRz2Nck)rV@Wm%LBe<{Ho7Bc2lD zbo}hecUwB=$C!OM@T+*XXiOm^!$o3Hn~B~-ud!>?ZO2= zUw)eNY}5TC=g!Tki;L*8N$^ZOm9k#Lz4fhZxK3Bny3M}%{eIm)R_XZPp7P+5HcS8a z*uav;nrXY@Rdo7mbKmf;sJ$&J&3bhVr>_z;R>6(FZ)X)%|Be{w}_oOB`PoW_nc>~PSZ4(ZuXMxoBiYD%TIE*YTg+g z(mu!Ta#`ox$|@6ShG#pStA5rhJ93()&NBXYB9OxNy+n#dq%sZpVH)oWG&@<;@!J_^Iy< zZh7tJ=!vd6^Y@5;Y+vP!Und?+o8$gxA@8rtGThT=RpfB>1b1HDW8m8BtLA#>mD&1h zs~jww-}LO(cwp7BzqN2Nk35f@{OlPwmrn1U)}Fhq{AjB~NC)dC>-EQY7ytbBum1Z2 z3y0s2txRp|?;re~`u}Jp^JHr|vyF>qCvHA*@Z97zF>ki;1O_zTu{s)hx@pGi+;^AT zw&&O1S<)iD(!uzYf%3QRvzY;hox|m&XS5x()qKD7+9yBH=}V_hie3JrC~$_s-miZP zI7F9ruJ%uS_{NXZ%ES1}mHf39SN*4!-k-wDTt4;i&s7^H+!CHEW8GLcf6DHAn|J@N zm9(|Z&zO8Qak}80eGg+_2{caQ)#7svbMvubA-u z-aUpQq2KKFYvXJZz6$+!x99#X<t|kA&^qD5{PwqIoBY_>e#xcw zYWk_|y4GHEUijX&HQet^++%0`58Z5)Ul+GMyzS-oomKnZv(4Xm^M$udje*02Rh5NZ zQLja=vaBbord8UiZFV?=Dy8dw;!j=hF`DlJ(PPYs~M@J@Tk$>X&PUXXc*! zT=+L-_s+zLUl;Hnc(}!n|NQ@XkDuFF+uX}^P@J#5{-yETEnlkseqDSy@SNSZm@6|k zF8*HPAa{FH*eRXjfX$hQmMa3v)oOn`%KvvZYq`XhZ}sy&vG4Ewx!Jz>*D1$jCk6$Bz5l+g*X2L+ zF#g}I&-M}tD@2!X^0a+xWOBj3M9^}1T5#MKl`g;CM^y-@@*GxQ97~r*U z*Ds4!`Hb7`AA1`eS53OpbLIL>lSnhq3050~rtLbdt$nlKuhP%piN+U+yPYCw_xCzE zi%QMCvxPzQZ?4uyBXKF+MDgohbsz8W*D`Ed9-34!nNMW#9ML6yjSD}oon^X~_x$Xt zwM8?-qg1N{XV2SyRj0dy)hDQFso#X1Y@8h4=INZKC{X*(To*m~7r-e6hzV`py_$fFb+WyzZhb57r zjXHrZ?DjjII=_eg=(T{$pQd-NFtGl{?Gu}^Wz%J$5T3iOe7%<@@Ul33>RXhP$kdUZ z9#?xO)hF3Uc6T#;uJeB$WBCRA9LDpaVH*&WZV#L%@# zGSpBtk}K3=0o&D8g>!`_Bu_ZKYgTKRVa}!BpZ|-#*)uh4Q*QK?35<&-d(2rrQA^~A zN}jH9Z&6vj-qPn>%?sT#4lKy%YhX#so~rHr3}@ywI#<2)kQdbdM|04RyBolE#q2A6rv-w*z8aymcK2`GIK6~uS#wr)B z35~aAy3M^{WF3(;q2cDuWn3+O^=e)3oUSXo=qexPUvF5<(oRv2@U8w4sJi8(3 z4xiBdBn`(W)-5e3s`OazF>Tl(dGS(^e4pXxS78B?8VeLd^6URDRJkJlu+gKXeNv!c zi<-ei&fAYW=YAFr7MOR#l|galL@p^oDSoLGs~;=e+nr-fKeyKF#VQ&2e^FVKq8!$; zuy9do(jJH34!)UB7k+=}+c$M4%N0BI?=}*v|?-F|&SfIhBy1;c2caXc{_m+t$QqVJgAro|X5V zc95!TUNCQMfc3g1Pxd}rAlZLvzxbM~S^RIKzPdEdy4sQW;zYa4qy5e^XZU>cd8K@d zEydE_+(_3_Y{56bz9aKEc_fW8x`g6hCQT0ewbs8@H?3s;^w);Ivwz;3Y$Lf=zEEhz zqFa4)V+-_t#-{Cj7H~_d?lgA>Lu(@wmqRT>PnNz`c0|Ti8y&qg=egR&HyKWG?RAU1 z7ICp?`q!D4Tz9UVnzFh`b?f!=Iy3!M8lsguBfHa77v1b>+jwBv`A6?mBu$gb;@6+F{F9e=Dy7^{|<`ILk^%a$C&E%9$uhURF_u2R3k0{mRXG>xa zypWw9XS_H}o8MRMLw)W4iPHEES1tE8ebQ$u6qtV{$ zA`TZMetcZEXZ?%WPQQ2PmdyLGWa{qRQ!AI8S9mys$8k+D_rE3Gw#VMRPS#Ismz&&i zQ!rn#HPNlQVD0IMys0Zrvb5RfF41S5c02XX#@jE>`K|uo>wjqFxs~kkS6Bj@C5=As zJJa!5C!_FCl*9Ws3+{Bguae*L^BKq2z@#^BZjusPPc6@#x#Gm>po04$E2iyV;VC6( zcyaa39gDC3-_0EM;0~XQ-=>qhYF50s?euzw_QrV+_DtOWmP>K_k_{Z*2R+c?#vt@4v692HF8B*{0C!O+ZkqvelTUv#WYlDALPHS=xJx-X>T#n(NjL(-VV zEVr4-1n>DSKe%rY6^Scu%Ja^XzKNtU9 zGgmlfX4~BPk!rKGJ=P?*b$>V5q<8x1u>-oVO*frf=HhYJz<8C7Wxpl!#)973_Q0-< z9=iQpT8ocJ_@-&b{p{Sn*Ii9X%A^^c1B*0>y*E1UE-Nw-G&t!#dBWwoQI&8dz) zn`OzzxF=><_)Ca*{o6jdPwh#_si$*YT1sV89K@7ES5@17XSbihR`BBWy~*n)YCXBs z*LU34Y z85UMd-~8o-KvY#!QRL#c%Oy)CkDZW(O_MDPH1ZVk12m& z9NpX{b#o@Kp~i+OjW*Vsge`qo{ElpTyx_7?o89lJBBqOSdZ%t>U%>dH*FcPgA++{Y zRc?gC4!QXG`5(;$1^a(=RV3Ul*ccsf(BgjOrAcaOcdNM4-ikzsOi<@l&$5_!^_JkH zwttVEr-?g0>1jTiwP&6~^aXd;jrSOe{x4cCqnj0>v*1&*^}e5-{#Kob+m}Dy?C*ch zaY@qZ?U*>hzEGV4%;!jGDD;@*W6I95N3?5A-9CNKf}N$i{5EQTS3HsPIceg@qD?WGR$Xlw zy%!!>g*qwf6uMaL%tqjqRbz-%RTNacwDH znHT9*TbX|Jb8)!Fre8nrd0*J@T;A=x8OyfQEsQNw_OE68#+&{-;g0!i+dmVo+sDj1 z{3iL${#_j=iz9^R{kSJKy>waewCjO$))Y?sa%;V?oA`+!e|e=@MFQ83=xO=t&V8Hd z?9_UCn~>DAEr)~_Yq^PZIkY96w|G`izhc(J08hqOG22y@miO)cDZ#S!x6!$*6aOl{ zZFyq4d!AXZTkg}Z=R)tSVVj%!jfvC8_RYNjCQfOgD|5~I@K}G z{u#O}R`cM57S3!tCW%9bRww_cxn`T!78>^I(PW;@mx5ONFRfo{+4!!0u4wnlZHAW* z>nO$Moo{}vR$O;x)|VO1i!SI^EdI6B$MT5YiGJDHM|)ipYT`v^i&ol1+?}N4TIsgf zVBVgf;KU&7nCVk>KF>T_<7`F@zvfH&5Wmc&IL7pCLtx-)*I1`0 z|Lyx`eHA+MI(fR8HD{ZKx@qv%cpB=h;G~WdfT?yp+#G#&y0NP6B*~%zvR5>Ik&5+r%g8I zv3u_b)%=>X;Kh%OpNeO`A35ifa4b(qXVU*fQL~dZT^c9m2nAYaXdgadsidQ~bovIb z2cB~Njq{fMTYDZ&e!j;#al&^W!w(O{fBMgLVv~~Trsqq4W=_rv-MCcR&{mj7`{NWA zo$fuwGTVMC#;yH+LC%`z%Y}%L{31DbzP;A|KiACI87;uL^;m7}-N1#hpN|J$vCK?+ zX>L35S7qKxA$H@MqF*y?(|+vq|9W2Qt<71zUoMw&_jk(8uajVA`%Q87yx&n2ii$m&neY=?BoFX@W_mZ1|Ya^a_s+V2Lh%1-8l78JW zH=xa^-z@6Mo85LE z(tFZRu}@c>_U_x2?tqV*bSE!elNrABOy;!LyfMdweu^J9yzvY|2d7CzT0fO4L-X+H_8f_{7LQVo;_1etJmFo zF)#j8&!OGCU2m38>x^pK()Il4F=k`QJ>PfV__;f0O=32TXR%ZEvFB31_Sf!Bo>&U+BkQLvS?Yk*7L2}Bp zS>eqN!Er5}r%dv9UdU%~%6l4hh{bxco0a#w$StNCY|VwLKk8P`a!rhCElq5l*vXu> zPVL0ZDO~q2taB)+nRLMTO!U8XTMv}7tl;@=;&c7S!7Xm8ZVNVi(k=KgyLD;4TGS+i znjrh$sl@>&CuMReXLw#cbh%io<4(hsQVDUdx#HJ9g(u}+*>G@<+{E|*t;|G~D5V+q zX3qCJWqE>e@=TFsbK*bV-dW`wp_)9et0>W}eck$l3y)>)<8x9yTk?6nG&_s%K1&|q zHbMV2!p#l=CCN+|cdg7~-Z9tWfah)NYP~}Oi42au79mIOmFMi-y5!0k{`vO`w^f}o zX9;}VX@9@J;rg2NTXv#iCT#&y9~|X2eEN5%;3@Z>is|9N2Tn&~r zTjeV{xqSPS1NOV>9X$9nmHR%)8wBzHWO?V!eZb+t_L#1a`$h&Av=W!{wAApNta+lJ zYq>$Sp=TSec-)C7`=;fx? zP?3?}p!Dr_H0Oyj58Wm$?_#sujNV7Nb_Es=bBaGRoIb+!-B|L|nY6_&w9IgC`JpoYy^FukX)|kk`A&5r0|yWLkDdDp0v_9x94rj>6Y+nsGe)cEzms?R z^k|#iOETu)KJ@eHpYz+#I_r15*-@?_|IsBfa@7g>ePuh<|CG&4lzBZz`|{trzZTq? z7M;57p61@S>lUp|U{zgLHR1nHvD@31lo$Ak9GRAyXFZv`R&?T)m^V4^cxHaqd;P-V{gu;J z&up!P5>~$!np1UdvB4Ie`qJXtF^3*3w$5MmF=FMEJ)iywT(>{`_(zn*cAvUCvp=`r z&iypw$-cXulkd#jsQmd~eMB<5s7cc-pPp7Ou@8-YsX`Z9TNthXnYF3Sn&7=$$S8~9 z>rIgZ%j+atC4`^eHxqZOh&R#XpT(}c(ZsGtG`Rb?SEbkwllr=uf97=lUbs8I+^qcj z%`|Iaw=ZXRC+IGIb7_^(rNn8ERAwD3j6B_SCMaD+@Zx(DWxx95~U zz1Ny}dnspTUhwtvvG-m$l`Q8sF;#FnDH0cBdb=a)@LAWW-6x-4s4fliy=EHmNqNd<(4UNU?KldHhD<1sPqA;g_{~R0 zTvb)$eV&7*{<#^?%)ak9yLpXae!{iW-A7X=3&#F-n|v}e{K35k6IF!c{yVy?e>kD3E8$Nby?*t(OZ@_xv6rq zy+RA0f4$;(jPvE9KQpA?o!gQVcq^mTm+Q*sLoB_|e2*yml>9z(>eK9anfIrkTnxQ6 z+3k7m!czBRm%l1*SWy@xp~rCZa3KA?Hyjjaa++62QinU+6zFWmwy>~V*;}iMY zQL%^5`}dxouJqiolg;R0h@+e5n?+4Cq~Gc}OrCOV*}2n@EXK}1IfA@U4 z+ljXdT1J=RKHvM8Jm1KUnJ<8&?9_zIWz&47|0sU3*T!pR%;FD=t4_Ukeei~>Afmiy zm7D9HXNmWkAANd}9rWUhs_iY^pnwN=R;t*Gsm)$J<-xh>sZ(V>u?d>(opj;cKGvUg z(YJ0CUKWn5*vW5xd*-PxD>hfmxnii@-gnLCvS6jr%)m7>9QZakB&J(^J9H(}^P5YM z(^~U2l5Zt^&Q446-*RtT_RfX31$ay6TQBRX6iF(QvOMPE`tthy=Ud+R z#=Ct}^eof4hx2onH=6wU6+PQpyIpf7|23b>`fVp(vU4z1y%1&=xF+Z9*wAA4=G^sj zstb2r&wl^;N|^nO@*`GShcy+l7z*w#bh%PqcgRXB!{vo_`;2F0KC1Ihr_Q;|A7!(B z&AyC({Z$I-*#-BqpPmu#j{jTmB76QLgD0i_r}K8k{%CYvCBGqCV}nuVf@6`A{Tj2= zmUY)&zrXX!)bIBqFA52>?|9mHJxKDS%+`KxhUza9Srl7TLKw2<6dvzqZtlEgcG&WY zv3>aWLvt4Su3dN3@_Nw`^&bjL{n_7b+-dap{A2-+f;o*A2LJ0X=-8*Su3R)XUExut z$YcHk%s+1C8|Ox^U(?Wd*r0Oi-5dwIizW^H7gjkg@Z7=hHu4FB&wMpw>(IXCy$lIMUhoMZ`PNIT|RpM#T?D;Cz2l+_@?>!rkX8FWwPM0NwVJR=E3r*t%SkO z?`f<^%NjWzjoCa~oLIE^6&a@e45%WL&IImKo{S^V_=neILe7Nx5G3wSR#YTitq z9d0WBCHY>gl4UdNfr}bJ)9yB$Dm#DqN|*7Bju>C3HqF%drN=fhYi?V1H`*>F+dQOk z-u*3l@^@{FbgZ3^=e=ld)==ZejV}#rPxdg-ea^>3h7BCFVEwRs1Rz`?f9qa7ojwgC$mT zZ@y!{D9I;kwoci5p6N#uVTOH{r;lCwz~#4h&B?&x$wH#fGlUra7%eyMyfG_!`?6Wr z4YpeRuQ5CDHt!tMlkAuZtH~LB+z(6G3>gn*FEiX^syl7Y{|EDCxb0aPIQ7h))C`-s zo*v<0k7}n`Rh_U@mhAA`asTu~0qg(lOI|Mc@XS*8{jsfUADa0bJwILMEW-lBjh#Cd zWHv0n|LleTv~xe^MDi@(VDh@|EYGpaH)qZLf6%|4<#%DAvBV3W^N|Z)s;&RM_5Yjy zAODwfHQ(62B7Wzxvcn1A)XKLgUa;ttpKxr)oYNns*3F22kiP$C^&5fE)3>}8Q}jqiH+eCtmdlz9<=Q zJ^7{WzHfgkIO^ZL%t?CwzhU|Q*_U_K&%7LYq;Hw>p0ahzehQsT(mSNN{;%0@Kd)~W zORgwXuUxK?QJAPOSMmA|K8qrsuV42*yS#tx^v^R`*Q{J&>V5uFo`i$AN1vF6wbn=W zl4BK9SpDRe-(mXgC|w$IbpOTK+fVFuP-6J=V0ryQT`~Vef!znU|FFC$vC@a_wbKNI7MMzj++milsCzMhcLlIuX(<1ED|*^A-Qp%chgAL7uYUILesshj`tD8U_!Z$hm-&5uyMIow9Hal` zy;XO4lCtkVR$gFS*txgS{@d35?q6?hHj8Fk)Zh`Tw3>a=S>=dHC%qo4#XOxJ`}f4r zsd)_L2}urT@0Gmdjlap^TcoweV$PSpQCDvNn<}cS{&s`uZhf=0E`R>$ZE$~U)FV4% zcKAkL6Wu)(tYvkF4HtB;-x>SzSZ2YutbGg1pEIp2U-tLFVJpEG*O;PTUfK3Lp&~*w zUSeMB1btoavoSuRy~;>Qoaf8U*u9bx1@b??J6_u>-; zT68#>#0^6{eqD^5v1ptzW&n%y^$3a5T!{)!m&u>1j;GM*}O4&3hgt{u6Kv zI-I}LWs2i0L#qyrNz1l<(V1JYdh++G)?=J`XFM#k zs&(hKR=zx*`8L5>cSL4a@F>k&QWuf2{PlY`V~Ns=B#ej%2Qji zTGtyba{2FGWLjE0bLRBCHx6-mN4K2)(*550Ww+4blBq_b!n|oe3k_|mUi4Vaz18`u zV!`E8J0`m&@BG%*AK1dE7>J<(BpS#}!MORIuf zCTzb`bozns#2bg#xMvmwys|MnyKCCq>vJ0K_B1Iy+90Q|H9hcg^ck-d>3i0qZrM+~ z?oU$L9x6qnt-#_}j?%bQKu)syW9Htv(#jkEVw&Ff}C@;(HfQybZp7_q? z`{J&h{6MnnbkxFGSD(50TzR76Il0_;Qe3EVv+t@`r4v@Rr~4gxl<9xQzjN0au1Jx> zevOj4WxS4&IT7n_EpfkbKVSNN;z5((!YVyQ=GO1If$FyfEUlUYW1=+crbe`h=A|9` zsJ4@fclkm7?P^*{(e(`vyY-%*!eRuf-+w zN4iD`#eFR`SrBya-jy2u&(pbj#TAUObGcT z!4tMH<1f3p|FxNsHdi!#7pDoW|Jga^G<)qO$@||Q-!`-eU@p3xUcJ3HTyzmR5HCYW0?>W$WbrRO#Q< zwR!a-^tOFAp z#iJ#DpPN*4=EU7+n{VR2bLMspeGU1X#VdMjioaa-nlnwcaDwq(qg;z=r`x#di93I0X&)~vuo3tE>8S6dCrF>UiZz9_3ppD#BH8Y zsW83!mk95+a!rFn{ilx^D4Xwu6U`f|S_Wqd;dZ=GDMwXJ|DEj;V}bM>~Li@f*$`t~{fjqaA{ zIL%WJnxAb;JbHN3-8ciLo^!vWK0D6*dPPgi^Q=L)SE=(%(~0u0XM9z^vwpQurs&66 zG1_b2FHzDw^Cqk5$H&l9>{oB^5?Mauo*(b}io+gu?27*aX1A&a&b(Gp>NnSuP|;)3>gVcdT!Xv5>5swL?XQ%bY8sHq*uT^8%$+MpCCYJX7NG zxP0NECdqz?q|b$kA`wPDVRv-&qHE7f?{Wj}Odln*G_?>hG% z$6fRJPijKLJnb_>o&BOs79HQV>Gc}f-3w%mqw70@T^lWz%ukYuyw&w##gxtNV#?D! z4tR*4dA_NKfAtO>o+#NFKlkioTAG_ydguA>m75Y>&1@DIujV}{l#UF%)2c- z?|z!c!8zN%H{8BcUf2{eX?llB!qR7xEhHt5uGu1xsHVMimPzj3Z@1T6ocivObCJ#a zQ+uaaOmaDEs%^h3IdX1rs=chGj;N(fujB91S3jIKcOH(ij=svTTmLg*>FG^P;XKC@ zcK5jySFGMUEnLb#?dMVR`>DkW*G_+$!BbY=`g>K()?4dhCpTYux2Uda<g zovn8Dkk<( z<9=Hm=jNktAT_P^|K!kVWslxS2AvT)?(XqBHT-s6^(%MBN2(nqPY&EU*Zu0vsr8(B z=WQ;YU!nGCrs@jS7$e@B{2Mb5q%q9q^Z2UJZmclT@1){YdAY1Q`D=Iothm~#xn)H< z(@AB$?v|4?dBPrM&NJVg$-RtyN?G@2_3+bGhCb$2ic`{@vP-wy@98xtdR$;|(I=Wu z$t-EzX*Ye*o+`EM&#%)I!hX%m*k@6<+n8BGcINxk=$EmY2Sq9(miL~1a)v)*-izFj z;_Y|L#ar&KDZWsDEA^=j)7gc)wo8<hd=g87^V@ z9C}RgvT>a0X)ULp%)N&WOjkRy<%mL(_Rmdg_};tz`SMy6dV7GZU}qdP>E6yhw?y z{r>gn_f39t?#<{(O5(d&wB!}Vm z2m9iXZ++qb=al;`vAPnP}FjTd?xL;E(}(zQE2^Ydz>Nvq~m zH1r>TzsHN~bjua~sClkG*Ca7)eR}!fmXlLcvt*B*&(nLk>Y|47)Od*)!G5K0ujeTE zpH6puJiAZ9IKV3@NT%u5YWdB(Rtf~0sp&B+FZN#N_~X^H-#4!8n0Z@3YzBYVa%Dfy zl7OcvjsKsy1Qq_h$bD;`ghNh9+xFDf9Jzo)&$m^TR4-Or5VCcqEX{8Y}@%V?)&@y zH;X(pMYrm{;Lc$=JBnPnzxL-B@Gt`09f?les50B}?q9Qad{D zeu_pf@8++6C5;2y=Y(;7HeNo@eCeUe$YpcHUUr+9^gnh>=h~?{U7?^_>E(t;Z(7`( zYxBO(UtXnBvgX|1Z6~;mwk`eP^NO>1(y}GSFP%=EkTg-Y?+^5fVNgq`*43DNe%4u& zbT{`yZfj?seWkrP>{OMl<+-f$CVafctCsdouFUmz*<2f+$lv9eXMj( z9r8K5sK-4Z(I@lT1CA*-XFO%OcyMyIHedGDqOi~o8SdHDZ!ON(9k)OGd6(Ssl!*)2 za<-<&{;z&wx-*z1QuT8iqxOuaRsNF4b^3U_Gbb1HuI@2(_xZK>*!yi6X^|P%)XbjR zFIrQm_4rFnl1^IP^`{JrP53ly(`TLbF^x+t%51BPZH;8hzqI$;o=?{6XRq}tk0~_y z{ovZiq5yu^Gq2AaI&-OWX*k!6M^C#BovPpQw8(2?wfnQ1a|8V(#Oy4WJu~E}GXE`i zZFcQSBknssC%1GZE%)(TS++iL)sI6aoWEPDW77Jr&K0!W{d()khFPMD*@vR?tz3@p zd-~;NX(YdaRCD3Mi$|PH{3f@xg`T>2`Mp!u4!Km(%?~_R1)n$C`P41;K+iya$qz5jcBro07@m7VYQnS4hn^bte+ph@u;|^q%Wo2=>b;t(mGVKx z+%EG&YQV*=&e-;!vG%e2QcK$Hk4($sOxP8?<4fSGuMc*-ayqMX=4Fr3p^g^|ud$cz z49!}RW_wdVXl~BU8v(6vnsvOcN2PwdvTyySjY4k1(;cp`{p^g+Sh=guX|L9nk6jbF z&P1tZ?Ua&CaJ|pm|8j2AnU99`%UXNt7RN0AnC-PWs;{o@VceP7uDMSW60@G)+_mzu z-e)zfk1}cId8VgVZMl}xvFF_D)cNc8I3Bxv#WeWJ0^PHl?r2TaI+r_V>Qws=Mq%&P z`KGhpD*K%*hss+nZT$z;q9NSyUWjgiXgBJ-^-folsgl^m_ST+A@*TNJ7Gv!bmh!gFm}^>J*BF|5 z=iW4FIR8lR{;|~7P_5LiyDNW~hAYmVGo?M(`n-n9pS;tK24b177p{)-3+vSCUVbfk zF7wm*XRe<}diOKJWYQXyc@k+}51wD~N}TrkuKCPG585YrM{VQl3f_Gns8_e@%&WNw;`&^-B|}Ns|^_nUxhKXLfe)=?(Q0f_x16pZm(DN#=y`%B|j= zY_xv)N`@23pDt)TZe-=3vovPmeGl72JATE~Zh&{R`po@ir)0IbvZ*fiji;2^wcQG%yRV=#F?#x9e-7B0dK_91H z2=wvwVN&?onEB|-y&o<@dJAIoO&q%e>m?YOeLo$ztabi$h>xziXnTN*2Fsf?W-XpJ zK8=M_ULD=Ea;8bRWa$5aZK3n*~S2w_{t>!lzvJ8(tIIwes>)&nMN%k)&m z6U#aI8`igOQ>%2=t&?*~eUTM1&5J=J$tG&S&S{7CzpBw&{3s!VL)FE?*`QAyxu6@GhJbmk$IX8}&vaM58>tkqK#(e(9=e=AVEDNTruB=_^yKE}g;l+D?q`uGy zGFleR*=eWU+2O%)fGy}D>OQHTgAG^Y6D?)_-hF_xL8NxzJTX`KxlM%nrWh69F+vLZ045 zQCm(e+%xq|!i!1M#3#A?_)hGZ^2jk`qZoHY&F1e_2OFKzUUj`>{*n4w>$9d*uam2c z%SNLwCK{ZYDeIQbSY)X+&9UiH*Wy{LXQrG#pLJf$G5yWYzzUcBEt@6?NuDibTjsB% z&G&ej_$Jp)dw)Lv7q9dvw&>Z4iRT_2QTVpI`)9-Vzf(25R1J}*ty{wt{E-3%7OOf1X%4Znq%hWL6*E6HiN%G2Mvuv4)&qvw4zWvb< zKmD~$E|YcPkA_LLscavMyIYes#24@~J)618t z117y{;=df#_9S9!qX z+s)J-;@rT;Beqg!mRG*`-^&GGjK0#Oqp4J6*6WC35jZT zyermJ-s<(d6V%2fw^mJc_Qs>Xjvg<6TO7PGjf-cuXH?Esk0*?0Bd;mFKNG$3YGRzG z*My?CQI@ZE%#wRqJAa9nn{j2sVs+KeXFTh6M=f0uT6Qi+b=T_O9*mCOY-*a3WqVR? zwoY2d9sMjk`?bwApE4!k<7smYO+I>bw;Ako>GeD)b>r1LvEa#*L4SKbtRgj;V@x$C~oBkNhQH@wm6xwsUFU<;?ePSlbpR`Npd3oQkQq z+-)xw?ir5iG*yy~aW;Tc4+^W=bphepsbfwlLosw#G+P7+(*0L3i8}vjn*PWTA{N=66+Q%=9RWIwiv^X_f>ztkV z$iwoeXuw8>+uLTninMr0P<$8U0)4%#HekklGw7;M) zh>@R+vj{&zmXUTNBW_@3N5@r}vvE^Hsc;LN6LTD{K4z z$w#1P;migPBj$%+^!L51ExSK6tn{vZxyoy!&$03nPZwz}l#rG#T`m5pt^0a?anj-f z->~M5MP_Rb)%m=yQY=O>POyC&6M1C+DzyqYu6Ex z*18}Wrx-{K}cX_)=vF*J}mnl6*1(rL>Ei^IAV8dZA({YfbqX*xDfPONMF9dqNZYUWHoBjv;F4>HYvJ{0qaz24pOF}8Td z)USKj@k_3ZJ$j@5Q^}c0vRr{1d}^iYZy(Z$xRbl}>+h8Fn`>pR=C%@ro$tIq_V7UUI(6X?-?n#iW?(4B5s1)SlmMxqkexp5KLM zDwoe?ald=-9B93=uX=Oa&*>)v)|4=5o%H#9G&aU1)%0e|Dw}OK)65g)Lyot7cj4PN zOYZgN9g%$=Gq1iA+i{=&`@5obk(I*t7Uub#o$nwP*kF+UT+3#u>O;3D3@1&?FEAeQ zF!i|LE;{o8jJ zrX^TNn%9-}a>UX@HKCD>m%iow-g&kwXwl1*xp%7b0$iT<&J?lx`R+ha&bf0T8G@PK zF$z1ylG!e0352^c>&3m&`Kt4nZAGrm?mZJt9zFuV*cYW66m0zUV^zt{&jSe)J z967siZfcKFyWNV{?ynnO30=8)$Mxr~l?+?&p3U5^-}Nr#@>%=c3l}ffG>%P74*jBwn(zN4#duX`&X?de5+a#twx>tHBXmW4MrJjvT`d_&8ZSj2U zvN&|B+HOCM%C$~UkB5hu`E3h4czIfOdsm$L8`O3RALYBzThq|}y<^qHc?-OrIj5=QdZ_N&Gnv=o>umor&(yUq zPcCw(REScQ7R{I#uI_(Zz>h^iUW(P;Kdw=L z+tK{vX2FKW4_t=k9MMmYIDB0w!@#(;TR?%sDMB={;`kyF7SsByfBUrfJM;K2ESH$- zI90>b?#PWJ>fA>KH*!3jcjCvfh7T$aPkia{@!%0~E@(DXQWWg)lkgJozbPajGhz9n z?|=8FY=~93AaUTbL(YGv(U($k@@#p|;KJU(!IG3alh??X^Zl9N zKuyK(vwLU!`NHLS;MVcb0~&gjy$651oVv;8Q_#d!TRH4%pL+yVbGjLJ*QBqwV9~O2 z^IKjuMTQMg$Co`fD@w8c`?iyz!eNGlpa9#GFUy)F1Y4G>c6;bJ3M_a#nFZ}%= zOl#_wqfzY7ZYW)zo>nDQp`^sf=QvByyJv+UYryH{-Lrz0>~xNL9Wrz2nKcKtud6*j zGtoKV(azO(%u07XJG1&}{)!tlt7fI>SnW|=%K2MCD{bP=@I*m5h7~vY`pECHvj--P-#r z-n+|O$zfzmuxz`r-A&*_u03zW#e0_;EY2pdWnPh98Myi9O(te(H>Fiy)g+=0IXgA5 zY6Z#83vX26ntuDjwdUpe*Q^bk1HVO>cTQq2;+tnNdFIlzDK~Sv-vc?ZhQ(VpX)JvH zckZm0{8~GoU!Hld%ujro|1-19z3Z;sxUg)kc=%g>mxo#FMV`srsryi!ar~Q%>h(E> z+ap(MOsz9qe_`#~ldH3h?!_!^oN;s0vPng=*&Pe_$b_EX9b_CXe6ZophnOWP!HQBF zneOo2?)j*`MqHHj!~CM({keBE`YTkJ@3e2b-#S11>cQss-Df60-(cHtbdi$t)!c=l zH?Eyp`!;>A zZPYodA$RlJw8wTY<&OIsf3FG4c;40)Ix*7k^=z-?$jrr`9#*`2=eYcA!uOSOeFwk0 z?weg{@M`rFQAgc&i=-Vdpo$cng!iH7W?h4#ra*g1WzKs`Cw~7SeZbgD`bohJ)xLMnUM`9h z*`zpiPVg-TH4fv%c21fD=#UqnnM{dBPur=$3}?TbsT(VDGp2 z--SI9)=vIOFuq-ydmhst#dc8WPCB#bh~+QdZpup z&x@pjS{|{?m@{qaro^+_JR2e%;DU-jO}=n%eFqmeSKfwwkGD8TFevRxQ(5V-XmaA&NFMF$a_;Iaf%4|n zqMZ-pCX1L0?_GL5MS-{N+C`qt8Fl=-QvTgsn_qjix1gaTP1gI__brw8-&lv2d|U8h z(b5<9x~DJley0C6^3r9mfagIa@5OyX{EpaukYB!FZKu@5rISVU=k8CMv?fgJ`#k1Y z?sEZK&Sge)#A|Yz$Yq?Gy}7^2{b8=B(aI>EJ%(jTYu-#rv-ZwtPGoxet(xb_V*bgq zqnPG!UPxfI?r>>|a9JVpT%z42kjdCUt!0~(!>U8C8dEzR&CDhu+ULjA`-`1sYy9iEYEKfUYfD=-o zGuBLGiou_9wg_7l@xsRYPRCN+Qx84t_c~RantJLAi_E`S0<+l#Pr{P&FrN5d?3 zMn+C189#NyC7Enay3|V1b8SC#QW9-@B@nm7hT+sz2SyM3C{il8eT!h66k5?!GBIFIvPf zv+$`Ow^0Vi7f$cnmDlwhuj~$zu<~mRm|yeh&ra*P6*2_N7NA(XHq1mws&8+Fxt8>-(*}*NQs>TVq<~7&A_* zc6yuCso5=NPto)!o~PCou=<}$s}AS!wnzL7%-d(V@+fCtd0@Ib_x!!xhg!O?*)iO# z5$ln?``*D<-NEI>qS)*6TH>}O?Q&*R;cCrTcvU)0qqXe+qLphDLtd|3V7*U9N$XXD z)K{O|uU58MO{q|S(IMrvk9}TGk5cOR+l|*l7da@-q&AU zj>ue{VVLiz(8R&Xv&6NewdmBj-EJo@@7@!Vq@c>MN-;dJgQaU@59gJ(r1LFpON`d2 z@Gk3e+GQGWdWFH}HPba)D$K$Rra#_x@x;}`VGDV+gN_$hFS`;Lm^n-Ty}+d`H@kc< zkM#1lho4kbX!4x5;C~|JoWsK)G_zx|MZ8bPvo2;~N#)RDLro8@to#*Ok7g+!@K8(2 zShuDz&u_!Cn%Eq&mrBTmSm&L79 zZ2sH!c$;{~DTR$2FY{iFxaGQ8_~QH%EkWkrT2oVGR9$U5L~c1dD?ajAvnPZ@pxNKq zYE1*X(X$V_4W|`N>$frSU2iyfU$Ut%xXAF>=M!$d!C|e9HbpM-82zTJ=ecy?xmlx5?5K6mUL?8 zE&F(R<@{$sPFeEbkDj}{rhl8q*+WNr#m)3tn{ATz_e9lwX)^fzC_2w*`D-Ql($0Bb zp2a>fTxVMCzny<=(|?^4x>GcJp1S2k{OpWX*eK-1V+~8#Z?xwmvWRP_=%J zeyWwTqXNe#i@eo;q)YF7Va}_+6;?5G&Pn#Tw5Oj}`&`&p+N3i5vy$!(XUBfkMHw$< zv@c*{)BpMI)|z$tyWZX|cp0;jH)d+h;dtI&!N5qduOd;+J0JTTu9o0+de8vSrfC?k!hBV(%87a^Q5zjN7Jq%$c)^>FNs4d=Eyw&sIMtJ@N0pbLRWK zJ&#wVem%?^b63Gz>-g=MSBtCFtBQ67_rLkRdrF|->?0ey`B&Pl+`+l{E$_8AAthGD zTubK6DSq&$<#jk@asTm+i;To-%g&Mf=6`FI8R6F8v{qd&%<6ue+=BI&Wsqy4r?=v%>t!i~J_u{`}F} z{m79s&#rEr6e;>w-}v=a&c(c38UZ_hw!8lM#_ro{%73`3`k3Nw76$E?GL?CL6)A7` zp6$q4JWt%8N7(PyHjP^@&W~T~ES%tUV|I?FSo!ysl`FUen5TH9K0R*w@U^9_a6w>Q z&*C7?Gag<4=TxuffA&1K_iM?cjF9V+z6(uQ55DvB4!gQi)^gpX$vZCY%#?^rU3>S> zK|#~=Q&zUS{`zw43tI<=zQgN%ayMRa?B`wGe6i}ny?aY$8+;d1_g`O<-_ju{`7iJN z_P?PE!s=}qpYD0^(8Z;7@iQ;Gl)1L2?@pZaTW{}&w|}#Ae3wpMoGttz+xq2OheB1I zaMvG}AEV`#?27#78*xr*y4wji)g_r#-x-+Zp4+Wv{n%ftq2+t(OyN6<;c9cz-^~`5 z`(dlxAXw*fNQ_;E-L?F7TJ1u^T!Pl&BBQ#d<|K`rs| z&Fr_)=e2sQ4Q5}Ntl+V5@7=&Tp38XdR6eS_o2R{B>*uNOuTRdeh|G2CyF6Vxo-NOy z@J^n&2;14b9Onrs(M?M#x4yqC6?aUb$bIRw;F>Q z&*8IIFl69R*vJ~eprhFLa>?NYrY^1~E@g?0E1tdUw!U}3d%FFTuMDp)mqm9jVrfxV zVD-q%)$NTbKC<|jLGMl4fFf1uEfb}_|a6HbHkbDkT+A@MO%8-cWR1CZg?zvdFljaPa~1e zj$I(7HU5h_2>QR*d(SK{2ERM%qFK;FaL{rd3eo({{L-2W{#*7G`F}}&*Pov{ujP6C&y&*sHts%sbehd+gEVgmo_2+UTxFZ3&+CWZ`tyeK z&Bv=Ef9|_Isn7nO{MGGWNoI@NFFw}BvlZueWqsLsFtY#6`}l{M{{=R&c^H0gfegE%Es41o<_sS$YXUBs zYu-8)?zgYiO&}JP0AtyGQsf;`VpALF{6n!jST4$O)iIKmQf$<2x@4=bXKQdw zaPHyTYoEz%vyJ-7pKylRSd7QQ(k|*#Vq)+_9=|ljH&N#k9zUIHb7tY=D_<|}ocU== zq96-r0|&FfBoU`2iP0}N=tl2*+m^k|Wi>D7fo@qt-kuq5t62TI`lp*NleU``cuIIf zZc^Qq-yu^Tp8LJ-_WSbR&mJW$5UBHB9QTm(y}j1^(twE-w+y+2<}#WteH;0Ha^S!H zy3hYdv!}5ByOsHd|MZK=t2KXQbNy4Qcw+C$tSN4#Q6jLxi!Zl(e&wb00!=HcE&UF# z|HzRHyLt8Ysb}f)XU?rotox>VKFuch>*je%nq!K=<*9Dc|25{mzrJc-kW~yZc_Ipa1v9{#rLPe{Q3>yG(AA zR@0tJk@(Mnk5l=Ui!g4}ZH=cDSCa1hJDxQ!dY#yxe>)ei3^Xg*WF>V-{(|I&19#7sU(!y#8uLke z&ewMPRded6Ra(zqVg7xq@Y(vWD(9kp7Onm$Kj;2}tM~J*SpVldV*lKnzNhZkvXm>| zf0Wca-xOk7&e?q6_07Lq*6g|d(C)^vb?QOi+|DfJm|iU>apuqTuZOhGO-TQ@ed5mf zK_B`>e!Umol~;e|=$FF0%`N|6=jgx4KIk99@3sx*T2Iaq41Ko97J0$^QcG z+MVl?mdtS0u>0{Q{-%D-^~0~Z%MAi0MYqq<*kZ}F!f2x$?>oQR6LR|(^Iv0oah`Ac zw0nEcEaU!t?S}b0C;vt8Ph#`YRLrcq7ziRIM{6GfActKv)iKb`2x7_@A z_rr?2{x1c8%*oKGV0d)oO03>|g`DcQjUR;0ybnA$qezn}kMYN+OA`Fa5OFIQ_+P`-<&N2*Jb5Za3>?zjoUO0#TKI~r< zSDbyI_u=PhuOp5|Nv*lL!Fypghhw#Hnfs}2>9s!3ivIt0TK7NZ)Zg{qT~ofl7nlBZ ze=6_(57}J*$}Z2$+x>s{-aTc@o?kz!5#+!A{P%CGUMRkoOIqu+;ncc&QQNFOp7``6 zc(t&<=*ed-m~IU0^Xbt zr+nWR%DQLA!tOL4~_Tr!W6~Xx-4KeS`If{(*YNf4AM5J4EFe`=07DX&rhcxaP2y%*pLF zr;l=ZiS?f_=bK$%;@iAt`qj%CQf#8~QM>#5?{c|uq$d3Sai^^L$3?Y^PyW29l5Y3E zYY{6Zu=VcWwqri$q;B6_^8CvCJvWXWkvL5Gja=Ix04qal4F{Y`y) zUu<34vXN#8HJ?nQmL+*9#`>Oh* z%UuiXws=PD|K}%PwLduleCejI%N;<$f+Mu$Yq>?3NL+ zcj4>Vny1t59eOyWnkD1LUhXp9gLjXyt$F-Zhxw2BM1`KIRa13(EF@3-2y9FaK4~VJ zF_mN4sTY9)jLX+Iy}fDux--eyYU2GX7M6-ms@b#F%-*7+Y0M!Yx4fzKx`L0>nLoqBdF6SoD!g1DBY2?S(YH;)*Y99VL#f%wpm2Dy@I_IYpqcK1F5E zjqeKlLe1M**D40_e^j(qa?FwtS?Mj1`(?kLy4D?$lygt~q_#@5RkH~>t#C+dKNQ65 zqjDi);WXjSzC{g63dQ^Mj&IFLZaC<-a)(c0mpm(n@0AV?Rb2Y+$cD0D{m+I@f4dKMTfgb~&B(A@DA1F;SJjG5%uC9& z?}c6RvAgop8=|yLA0`E+HMrgD@jP_;w)Nz;4eUZfIh`gG-!BuWmU?`A+A+_gj8c;) zBrsdG?~}fIFF2aZl*`2J@cH%E1J)iq?Ys5EpB>`6bqux(9a$o7c5Cm_w^JGzex5Qj zv$ndSI8*r9)i(>GKX$*0k6Rz-eSZ1uIop(stuJIxy*th0%#vq|Has0Jd;WI4>6OfG6?3jguP&YLm;dS3k`o8| zypv>H#n{V43}1dsP5iV0ilf(doOU$N&B%lbZ9;y z`~KvNOZ(;gXTD}Wn!@NAu;xwn_uwx(?ys1#OZcwLXM^sy0UbS&kwWU>oAR1s|3zQ@ zmo)Y7FXvNfDc2+ly7sS|9RFWqZBwJVb-}g%-Fi}D;a@jgsMyoHl>N(;qknF1Z#@0o zdZynz`~PP*d^Ft>bKSRY>bB;M?$;mL|2_ENb^dW{&D4v6o{fiduKcb#9cZ^|Q}%Oq z)2+Amylb_e^QP|CPpJz>+iIO?S-Rs6F)q;3e>wq5SaEB@T} zvWq9$o%5aCQko`hd(yV%E1!LQsQJ|+ZaY`D<@4>YUbnmW*X_JH7g8rrF5G*gOd@t! zujs-~8HsZ(%szjP`X^7nzj1O{mt$2&*1c(*_y6ucy|UoMz4CuImoM=7R8;>v?@W15 z;xXaxUp#(nUw?IWy!|wf$p2q%3;wfR6B*XO|8Z{pi=(E$|AimUx6gl)GR4(jg_ZZ@ ziStV`N-s=QezWb<^!2m9n10^fef@m>txL5LKPQ_uFEf3y#WC_#?&9OJR`=Brg-ZtBm7kF8dhEPj^J;Ly4GYMJ=@w94wKrn2%2?)uKHUc&W0?XJ7d ze8IJePcN;QJ=J)cu(xqlVUg$b=&zISm&lZUPo8JzY*#O3@n88K=drmqtLOi{_~rP0 zzmj)TLcjMPyj$})_{sOb|6i}R&`_KDdVB8fs4ss#YijQ*J>J-TX18fKw`jh$^aeA} z6?c-Djq^;bwQd}!P?gyuwryA9?WNgggd3eYR)u!*mX7R zvww9;4}~9Z+o$b2XMt_7bvJiZm_oIGF~|D9LKpTuW>0Z$UYgY2_x8r)HMPyVRy{47 zk#bfp(I9|hYGaE~v()hh#|G20hZohAMXaj3t^Jk%s&#Tl!b*lk%l#bwDhn(<#4LN~<1YQC zPFYvRKi5At9G79`>SU}b%a`x|@a)}r_O7JK|9dre9*aA?Ds9cZnG3hbiYUX8P#1Id9eu~=$}E3xU!b#0 z{e8s$UAGq|TulrPu0DL2yFBV)XK4e6&QZ%Es}8Rt$|+u&svCZ`R=LlW7`IsW^YU^xF;t(RK1hABzee z<8kx$vOY;loE!=m0deVP6;Z1y(X zIy*w&|Nis*f4A$KAODYTeeE-)Ha_olywI{idxneA(%zS^BT9zbb4^)Q&qGEN{87QDH;E^ga8h zzW+CW3ZqKcC5@VMuP5FK=X)nsHqEW>x zeEz1a;sp{?vl4tNGFQ&c-~9iv+XBNAk8t)~kXZiGv-Tb$9q?&IFf0i4n+5Fzlb^ChjDU(0Ge#t4B zs(<}-b@Tr}cmKT3SC7~pcKhf$^BS`^vtH(IymRaY`<2Nhi4)W$B!wF+1g7jeHRJTQ zfEA+2cb5M;ym!~C=d%m!L!u8iN3Zy1)D#_mzr%W+QVkG}=O?ojoC;Upd=PDLPWsr_JqI&ieOKlKpDi)T4gCcUn-%09bh_9V^_?>e4PN<;h6%*);nxO1HJdp!-$&vbQkJ9N)GY+0+~7N>*ypAMK#S74Z{ z(%|jUAUl16q*Vt8cii31_ZEMQAGIhP+}x<==%D|@LHCmXpPhnj;pHcrIjaPv8EO!Ung#~UXEIc{8BleqYmNr;Oz!!-v3Ug7!8xs6jJ zCz|l`Xz95spxUz98+vQ!BdyKixII0V}O{}`HMSE7?vQpv6g1R@4q%56$bBW(^ zbJu_RX0va+_wBjH`p75iL2*ayRhg0{7i4xU>ImE~wEAVp!p_h~8kSl%OZyu1vZhKl zUHF_nr(ou}neA;%jLLp_GavV_T^4*bL-FXWJ8#Wry^aY_V+~uMtF0HTeI}6mZI4w$ z-PvCcHVMq^T;TI0>e{4)Z3YbS1vdjcP8=bzgl!;mgT?4_Y zYG$(Wn!)l!-;ImjeDa)gBue+7^@MYiwxm7~`FU<)`tb`+PhVV~n0)tAMBUB_>o=t* zFI7J_>9+1t#UlsS{M}@%SFJpIqwI~Yf1C2{6)!TMjVfLJG`NdPXX@v5Qw|-Dw~Mvj zAo!PakL?BjD{6Mt2Q Date: Tue, 9 Apr 2024 15:20:24 +0200 Subject: [PATCH 111/202] QmlDesigner: Remove ScopeChain dependency for QDS_USE_PROJECTSTORAGE * possibleImports() and usedImports() has to be implemented for the project storage * getQMLSingletons() is not implemented for project storage and also does not belong into the rewriter. * semantic errors are fully disabled for QDS_USE_PROJECTSTORAGE, since they require the scope chain. Change-Id: I200ccbc1faf2631c4764a4676553a69cc0f5cf1f Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Marco Bubke --- .../qmldesigner/designercore/include/model.h | 9 +- .../designercore/include/rewriterview.h | 5 +- .../designercore/metainfo/nodemetainfo.cpp | 3 + .../qmldesigner/designercore/model/model.cpp | 24 ++- .../designercore/model/rewriterview.cpp | 21 +- .../designercore/model/texttomodelmerger.cpp | 182 +++++------------- .../designercore/model/texttomodelmerger.h | 6 + .../designercore/include/rewriterview.h | 2 - 8 files changed, 86 insertions(+), 166 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/include/model.h b/src/plugins/qmldesigner/designercore/include/model.h index b907e6c5d8c..8dd87baa5bf 100644 --- a/src/plugins/qmldesigner/designercore/include/model.h +++ b/src/plugins/qmldesigner/designercore/include/model.h @@ -218,14 +218,17 @@ public: // Imports: const Imports &imports() const; - const Imports &possibleImports() const; - const Imports &usedImports() const; + Imports possibleImports() const; + Imports usedImports() const; void changeImports(Imports importsToBeAdded, Imports importsToBeRemoved); +#ifndef QDS_USE_PROJECTSTORAGE void setPossibleImports(Imports possibleImports); +#endif +#ifndef QDS_USE_PROJECTSTORAGE void setUsedImports(Imports usedImports); +#endif 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); QStringList importPaths() const; Import highestPossibleImport(const QString &importPath); diff --git a/src/plugins/qmldesigner/designercore/include/rewriterview.h b/src/plugins/qmldesigner/designercore/include/rewriterview.h index 23841accda9..0134349682e 100644 --- a/src/plugins/qmldesigner/designercore/include/rewriterview.h +++ b/src/plugins/qmldesigner/designercore/include/rewriterview.h @@ -123,7 +123,10 @@ public: bool renameId(const QString& oldId, const QString& newId); const QmlJS::Document *document() const; + +#ifndef QDS_USE_PROJECTSTORAGE const QmlJS::ScopeChain *scopeChain() const; +#endif QString convertTypeToImportAlias(const QString &type) const; @@ -135,8 +138,6 @@ public: void setCheckLinkErrors(bool b) { m_checkLinkErrors = b; } - QString pathForImport(const Import &import); - QStringList importDirectories() const; QSet > qrcMapping() const; diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index 742a093400d..45da52254d0 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -919,8 +919,11 @@ const ObjectValue *NodeMetaInfoPrivate::getObjectValue() const ContextPtr NodeMetaInfoPrivate::context() const { +#ifndef QDS_USE_PROJECTSTORAGE if (m_model && m_model->rewriterView() && m_model->rewriterView()->scopeChain()) return m_model->rewriterView()->scopeChain()->context(); +#endif + return ContextPtr(nullptr); } diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index 03824a7b188..714480991d0 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -1758,14 +1758,22 @@ Storage::Info::ExportedTypeName Model::exportedTypeNameForMetaInfo(const NodeMet return {}; } -const Imports &Model::possibleImports() const +Imports Model::possibleImports() const { +#ifdef QDS_USE_PROJECTSTORAGE + return {}; +#else return d->m_possibleImportList; +#endif } -const Imports &Model::usedImports() const +Imports Model::usedImports() const { +#ifdef QDS_USE_PROJECTSTORAGE + return {}; +#else return d->m_usedImportList; +#endif } void Model::changeImports(Imports importsToBeAdded, Imports importsToBeRemoved) @@ -1773,6 +1781,7 @@ void Model::changeImports(Imports importsToBeAdded, Imports importsToBeRemoved) d->changeImports(std::move(importsToBeAdded), std::move(importsToBeRemoved)); } +#ifndef QDS_USE_PROJECTSTORAGE void Model::setPossibleImports(Imports possibleImports) { auto tracer = d->traceToken.begin("possible imports"_t); @@ -1784,7 +1793,9 @@ void Model::setPossibleImports(Imports possibleImports) d->notifyPossibleImportsChanged(d->m_possibleImportList); } } +#endif +#ifndef QDS_USE_PROJECTSTORAGE void Model::setUsedImports(Imports usedImports) { auto tracer = d->traceToken.begin("used imports"_t); @@ -1796,6 +1807,7 @@ void Model::setUsedImports(Imports usedImports) d->notifyUsedImportsChanged(d->m_usedImportList); } } +#endif static bool compareVersions(const Import &import1, const Import &import2, bool allowHigherVersion) { @@ -2022,14 +2034,6 @@ bool Model::isImportPossible(const Import &import, bool ignoreAlias, bool allowH return false; } -QString Model::pathForImport(const Import &import) -{ - if (!rewriterView()) - return QString(); - - return rewriterView()->pathForImport(import); -} - QStringList Model::importPaths() const { if (rewriterView()) diff --git a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp index 4ae2261e604..17d40daca3e 100644 --- a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp +++ b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp @@ -933,10 +933,12 @@ bool RewriterView::renameId(const QString& oldId, const QString& newId) return false; } +#ifndef QDS_USE_PROJECTSTORAGE const QmlJS::ScopeChain *RewriterView::scopeChain() const { return textToModelMerger()->scopeChain(); } +#endif const QmlJS::Document *RewriterView::document() const { @@ -989,25 +991,6 @@ QString RewriterView::convertTypeToImportAlias(const QString &type) const return result; } -QString RewriterView::pathForImport(const Import &import) -{ - if (scopeChain() && scopeChain()->context() && document()) { - const QString importStr = import.isFileImport() ? import.file() : import.url(); - const QmlJS::Imports *imports = scopeChain()->context()->imports(document()); - - QmlJS::ImportInfo importInfo; - - for (const QmlJS::Import &qmljsImport : imports->all()) { - if (qmljsImport.info.name() == importStr) - importInfo = qmljsImport.info; - } - const QString importPath = importInfo.path(); - return importPath; - } - - return QString(); -} - QStringList RewriterView::importDirectories() const { const QList list(m_textToModelMerger->vContext().paths.begin(), diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index 5951413086b..84b48a11b3c 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -417,14 +417,19 @@ namespace Internal { class ReadingContext { public: - ReadingContext(const Snapshot &snapshot, const Document::Ptr &doc, - const ViewerContext &vContext, Model *model) + ReadingContext([[maybe_unused]] const Snapshot &snapshot, + [[maybe_unused]] const Document::Ptr &doc, + [[maybe_unused]] const ViewerContext &vContext, + Model *model) : m_doc(doc) +#ifndef QDS_USE_PROJECTSTORAGE , m_context( - Link(snapshot, vContext, ModelManagerInterface::instance()->builtins(doc)) - (doc, &m_diagnosticLinkMessages)) + Link(snapshot, + vContext, + ModelManagerInterface::instance()->builtins(doc))(doc, &m_diagnosticLinkMessages)) , m_scopeChain(doc, m_context) , m_scopeBuilder(&m_scopeChain) +#endif , m_model(model) { } @@ -432,12 +437,19 @@ public: ~ReadingContext() = default; Document::Ptr doc() const - { return m_doc; } + { + return m_doc; + } +#ifndef QDS_USE_PROJECTSTORAGE void enterScope(AST::Node *node) { m_scopeBuilder.push(node); } - void leaveScope() { m_scopeBuilder.pop(); } + void leaveScope() + { + m_scopeBuilder.pop(); + } +#endif std::tuple lookup(AST::UiQualifiedId *astTypeNode) { @@ -467,113 +479,6 @@ public: return node.metaInfo().hasProperty(propertyName.toUtf8()); } - /// When something is changed here, also change Check::checkScopeObjectMember in - /// qmljscheck.cpp - /// ### Maybe put this into the context as a helper function. - /// - bool lookupProperty(const QString &prefix, - const AST::UiQualifiedId *id, - const Value **property = nullptr, - const ObjectValue **parentObject = nullptr, - QString *name = nullptr) - { - QList scopeObjects = m_scopeChain.qmlScopeObjects(); - if (scopeObjects.isEmpty()) - return false; - - if (!id) - return false; // ### error? - - if (id->name.isEmpty()) // possible after error recovery - return false; - - QString propertyName; - if (prefix.isEmpty()) - propertyName = id->name.toString(); - else - propertyName = prefix; - - if (name) - *name = propertyName; - - if (propertyName == u"id" && !id->next) - return false; // ### should probably be a special value - - // attached properties - bool isAttachedProperty = false; - if (! propertyName.isEmpty() && propertyName[0].isUpper()) { - isAttachedProperty = true; - if (const ObjectValue *qmlTypes = m_scopeChain.qmlTypes()) - scopeObjects += qmlTypes; - } - - if (scopeObjects.isEmpty()) - return false; - - // global lookup for first part of id - const ObjectValue *objectValue = nullptr; - const Value *value = nullptr; - for (int i = scopeObjects.size() - 1; i >= 0; --i) { - objectValue = scopeObjects[i]; - value = objectValue->lookupMember(propertyName, m_context); - if (value) - break; - } - if (parentObject) - *parentObject = objectValue; - if (!value) { - qCInfo(texttomodelMergerDebug) << Q_FUNC_INFO << "Skipping invalid property name" << propertyName; - return false; - } - - // can't look up members for attached properties - if (isAttachedProperty) - return false; - - // resolve references - if (const Reference *ref = value->asReference()) - value = m_context->lookupReference(ref); - - // member lookup - const AST::UiQualifiedId *idPart = id; - if (prefix.isEmpty()) - idPart = idPart->next; - for (; idPart; idPart = idPart->next) { - objectValue = value_cast(value); - if (! objectValue) { -// if (idPart->name) -// qDebug() << idPart->name->asString() << "has no property named" -// << propertyName; - return false; - } - if (parentObject) - *parentObject = objectValue; - - if (idPart->name.isEmpty()) { - // somebody typed "id." and error recovery still gave us a valid tree, - // so just bail out here. - return false; - } - - propertyName = idPart->name.toString(); - if (name) - *name = propertyName; - - value = objectValue->lookupMember(propertyName, m_context); - if (! value) { -// if (idPart->name) -// qDebug() << "In" << idPart->name->asString() << ":" -// << objectValue->className() << "has no property named" -// << propertyName; - return false; - } - } - - if (property) - *property = value; - return true; - } - bool isArrayProperty(const AbstractProperty &property) { return ModelUtils::metainfo(property).isListProperty(); @@ -671,9 +576,12 @@ public: return QVariant(); } - +#ifndef QDS_USE_PROJECTSTORAGE const ScopeChain &scopeChain() const - { return m_scopeChain; } + { + return m_scopeChain; + } +#endif QList diagnosticLinkMessages() const { return m_diagnosticLinkMessages; } @@ -681,9 +589,11 @@ public: private: Document::Ptr m_doc; QList m_diagnosticLinkMessages; +#ifndef QDS_USE_PROJECTSTORAGE ContextPtr m_context; ScopeChain m_scopeChain; ScopeBuilder m_scopeBuilder; +#endif Model *m_model; }; @@ -853,6 +763,7 @@ constexpr auto skipModules = std::make_tuple(EndsWith(u".impl"), StartsWith(u"QtWebSockets"), StartsWith(u"QtWebView")); +#ifndef QDS_USE_PROJECTSTORAGE bool skipModule(QStringView moduleName) { return std::apply([=](const auto &...skipModule) { return (skipModule(moduleName) || ...); }, @@ -918,9 +829,11 @@ QmlDesigner::Imports createQt5Modules() QmlDesigner::Import::createLibraryImport("QtQuick.Studio.MultiText", "1.0"), QmlDesigner::Import::createLibraryImport("Qt.SafeRenderer", "2.0")}; } +#endif } // namespace +#ifndef QDS_USE_PROJECTSTORAGE void TextToModelMerger::setupPossibleImports() { if (!m_rewriterView->possibleImportsEnabled()) @@ -929,10 +842,10 @@ void TextToModelMerger::setupPossibleImports() static QUrl lastProjectUrl; auto &externalDependencies = m_rewriterView->externalDependencies(); auto projectUrl = externalDependencies.projectUrl(); + auto allUsedImports = m_scopeChain->context()->imports(m_document.data())->all(); if (m_possibleModules.isEmpty() || projectUrl != lastProjectUrl) { - auto &externalDependencies = m_rewriterView->externalDependencies(); if (externalDependencies.isQt6Project()) { ModuleScanner moduleScanner{[&](QStringView moduleName) { @@ -962,7 +875,9 @@ void TextToModelMerger::setupPossibleImports() if (m_rewriterView->isAttached()) m_rewriterView->model()->setPossibleImports(modules); } +#endif +#ifndef QDS_USE_PROJECTSTORAGE void TextToModelMerger::setupUsedImports() { const QmlJS::Imports *imports = m_scopeChain->context()->imports(m_document.data()); @@ -997,6 +912,7 @@ void TextToModelMerger::setupUsedImports() if (m_rewriterView->isAttached()) m_rewriterView->model()->setUsedImports(usedImports); } +#endif Document::MutablePtr TextToModelMerger::createParsedDocument(const QUrl &url, const QString &data, QList *errors) { @@ -1087,15 +1003,16 @@ bool TextToModelMerger::load(const QString &data, DifferenceHandler &differenceH m_vContext = ModelManagerInterface::instance()->projectVContext(Dialect::Qml, m_document); ReadingContext ctxt(snapshot, m_document, m_vContext, m_rewriterView->model()); - m_scopeChain = QSharedPointer( - new ScopeChain(ctxt.scopeChain())); + +#ifndef QDS_USE_PROJECTSTORAGE + m_scopeChain = QSharedPointer(new ScopeChain(ctxt.scopeChain())); if (view()->checkLinkErrors()) { qCInfo(rewriterBenchmark) << "linked:" << time.elapsed(); collectLinkErrors(&errors, ctxt); } - setupPossibleImports(); +#endif qCInfo(rewriterBenchmark) << "possible imports:" << time.elapsed(); @@ -1129,7 +1046,9 @@ bool TextToModelMerger::load(const QString &data, DifferenceHandler &differenceH qCInfo(rewriterBenchmark) << "synced nodes:" << time.elapsed(); +#ifndef QDS_USE_PROJECTSTORAGE setupUsedImports(); +#endif setActive(false); @@ -1226,8 +1145,9 @@ void TextToModelMerger::syncNode(ModelNode &modelNode, else if (!modelNode.nodeSource().isEmpty() || modelNode.nodeSourceType() != ModelNode::NodeWithoutSource) clearImplicitComponentDelayed(modelNode, differenceHandler.isAmender()); - +#ifndef QDS_USE_PROJECTSTORAGE context->enterScope(astNode); +#endif QSet modelPropertyNames = Utils::toSet(modelNode.propertyNames()); if (!modelNode.id().isEmpty()) @@ -1241,7 +1161,7 @@ void TextToModelMerger::syncNode(ModelNode &modelNode, if (auto array = AST::cast(member)) { const QString astPropertyName = toString(array->qualifiedId); - if (isPropertyChangesType(typeName) || isConnectionsType(typeName) || context->lookupProperty(QString(), array->qualifiedId)) { + if (isPropertyChangesType(typeName) || isConnectionsType(typeName)) { AbstractProperty modelProperty = modelNode.property(astPropertyName.toUtf8()); QList arrayMembers; for (AST::UiArrayMemberList *iter = array->members; iter; iter = iter->next) @@ -1273,13 +1193,7 @@ void TextToModelMerger::syncNode(ModelNode &modelNode, // Store Behaviours in the default property defaultPropertyItems.append(member); } else { - const Value *propertyType = nullptr; - const ObjectValue *containingObject = nullptr; - if (context->lookupProperty({}, - binding->qualifiedId, - &propertyType, - &containingObject) - || isPropertyChangesType(typeName) || isConnectionsType(typeName)) { + if (isPropertyChangesType(typeName) || isConnectionsType(typeName)) { AbstractProperty modelProperty = modelNode.property(astPropertyName.toUtf8()); if (context->isArrayProperty(modelProperty)) syncArrayProperty(modelProperty, {member}, context, differenceHandler); @@ -1383,7 +1297,9 @@ void TextToModelMerger::syncNode(ModelNode &modelNode, differenceHandler.propertyAbsentFromQml(modelProperty); } +#ifndef QDS_USE_PROJECTSTORAGE context->leaveScope(); +#endif } static QVariant parsePropertyExpression(AST::ExpressionNode *expressionNode) @@ -1501,7 +1417,6 @@ QmlDesigner::PropertyName TextToModelMerger::syncScriptBinding(ModelNode &modelN } else { // Not an enum, so: if (isPropertyChangesType(modelNode.type()) || isConnectionsType(modelNode.type()) - || context->lookupProperty(prefix, script->qualifiedId) || isSupportedAttachedProperties(astPropertyName)) { AbstractProperty modelProperty = modelNode.property(astPropertyName.toUtf8()); syncExpressionProperty(modelProperty, astValue, TypeName(), differenceHandler); // TODO: parse type @@ -2252,8 +2167,10 @@ void TextToModelMerger::collectImportErrors(QList *errors) errors->append(DocumentMessage(QCoreApplication::translate("QmlDesigner::TextToModelMerger", "No import for Qt Quick found."))); } -void TextToModelMerger::collectSemanticErrorsAndWarnings(QList *errors, QList *warnings) +void TextToModelMerger::collectSemanticErrorsAndWarnings( + [[maybe_unused]] QList *errors, [[maybe_unused]] QList *warnings) { +#ifndef QDS_USE_PROJECTSTORAGE Check check(m_document, m_scopeChain->context()); check.disableMessage(StaticAnalysis::ErrPrototypeCycle); check.disableMessage(StaticAnalysis::ErrCouldNotResolvePrototype); @@ -2282,6 +2199,7 @@ void TextToModelMerger::collectSemanticErrorsAndWarnings(QList if (message.severity == Severity::Warning) warnings->append(DocumentMessage(message.toDiagnosticMessage(), fileNameUrl)); } +#endif } void TextToModelMerger::populateQrcMapping(const QString &filePath) @@ -2386,6 +2304,9 @@ QSet > TextToModelMerger::qrcMapping() const QList TextToModelMerger::getQMLSingletons() const { +#ifdef QDS_USE_PROJECTSTORAGE + return {}; +#else QList list; if (!m_scopeChain || !m_scopeChain->document()) return list; @@ -2416,6 +2337,7 @@ QList TextToModelMerger::getQMLSingletons() const } } return list; +#endif } void TextToModelMerger::clearPossibleImportKeys() diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h index f5119060405..e22f747718a 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h @@ -37,15 +37,19 @@ public: bool isActive() const; void setupImports(const QmlJS::Document::Ptr &doc, DifferenceHandler &differenceHandler); +#ifndef QDS_USE_PROJECTSTORAGE void setupPossibleImports(); +#endif void setupUsedImports(); bool load(const QString &data, DifferenceHandler &differenceHandler); RewriterView *view() const { return m_rewriterView; } +#ifndef QDS_USE_PROJECTSTORAGE const QmlJS::ScopeChain *scopeChain() const { return m_scopeChain.data(); } +#endif const QmlJS::Document *document() const { return m_document.data(); } @@ -141,7 +145,9 @@ private: private: RewriterView *m_rewriterView; bool m_isActive; +#ifndef QDS_USE_PROJECTSTORAGE QSharedPointer m_scopeChain; +#endif QmlJS::Document::Ptr m_document; QTimer m_setupTimer; QSet m_setupComponentList; diff --git a/tests/unit/tests/stubs/qmldesigner/designercore/include/rewriterview.h b/tests/unit/tests/stubs/qmldesigner/designercore/include/rewriterview.h index a10da0133c8..4c59440b0f8 100644 --- a/tests/unit/tests/stubs/qmldesigner/designercore/include/rewriterview.h +++ b/tests/unit/tests/stubs/qmldesigner/designercore/include/rewriterview.h @@ -128,8 +128,6 @@ public: void setCheckSemanticErrors(bool) {} - QString pathForImport(const Import &) { return {}; } - QStringList importDirectories() const { return {}; } QSet> qrcMapping() const { return {}; } From 682514a30403184eae7ecd1f6edef4c7e1833ccf Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Wed, 10 Apr 2024 16:25:55 +0200 Subject: [PATCH 112/202] QmlDesigner: silence warning for empty images Also correct logging category the default type is already QtDebugMsg which adds .debug Change-Id: I87803cd2fe9dc142ca8ce271547706f8d7cba3a1 Reviewed-by: Thomas Hartmann --- .../qmlpuppetcommunication/container/imagecontainer.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/libs/qmlpuppetcommunication/container/imagecontainer.cpp b/src/libs/qmlpuppetcommunication/container/imagecontainer.cpp index 545d4d927d6..7747a9d1188 100644 --- a/src/libs/qmlpuppetcommunication/container/imagecontainer.cpp +++ b/src/libs/qmlpuppetcommunication/container/imagecontainer.cpp @@ -15,7 +15,7 @@ #define QTC_ASSERT_STRING(cond) qDebug("SOFT ASSERT: \"" cond"\" in file " __FILE__ ", line " QTC_ASSERT_STRINGIFY(__LINE__)) #define QTC_ASSERT(cond, action) if (cond) {} else { QTC_ASSERT_STRING(#cond); action; } do {} while (0) -static Q_LOGGING_CATEGORY(imageContainerDebug, "qtc.imagecontainer.debug", QtDebugMsg) +static Q_LOGGING_CATEGORY(imageContainerDebug, "qtc.imagecontainer") namespace QmlDesigner { @@ -194,9 +194,10 @@ static void readSharedMemory(qint32 key, ImageContainer &container) QImage image = QImage(imageWidth, imageHeight, QImage::Format(imageFormat)); image.setDevicePixelRatio(pixelRatio); - if (image.isNull()) - qCInfo(imageContainerDebug()) << Q_FUNC_INFO << "Not able to create image:" << imageWidth << imageHeight << imageFormat; - else + if (image.isNull()) { + if (imageWidth || imageHeight || imageFormat) + qCWarning(imageContainerDebug) << Q_FUNC_INFO << "Not able to create image:" << imageWidth << imageHeight << imageFormat; + } else std::memcpy(image.bits(), reinterpret_cast(sharedMemory.constData()) + 6, byteCount); container.setImage(image); From ab6bd28b2261fd707ed2d4ccaa0c9c87e08423e8 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 10 Apr 2024 19:57:45 +0200 Subject: [PATCH 113/202] QmlDesigner Fix regression There was code model based code for this check that was hard to understand and accidentally removed. We simply check if the property does exist. Change-Id: I7fc0b414af526f15f0bf35006f6aee151506f660 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- .../qmldesigner/designercore/model/texttomodelmerger.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index 84b48a11b3c..7637c006368 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -1161,7 +1161,8 @@ void TextToModelMerger::syncNode(ModelNode &modelNode, if (auto array = AST::cast(member)) { const QString astPropertyName = toString(array->qualifiedId); - if (isPropertyChangesType(typeName) || isConnectionsType(typeName)) { + if (isPropertyChangesType(typeName) || isConnectionsType(typeName) + || modelNode.metaInfo().hasProperty(astPropertyName.toUtf8())) { AbstractProperty modelProperty = modelNode.property(astPropertyName.toUtf8()); QList arrayMembers; for (AST::UiArrayMemberList *iter = array->members; iter; iter = iter->next) @@ -1379,9 +1380,9 @@ QmlDesigner::PropertyName TextToModelMerger::syncScriptBinding(ModelNode &modelN } if (isLiteralValue(script)) { - if (isPropertyChangesType(modelNode.type()) - || isConnectionsType(modelNode.type()) - || isListElementType(modelNode.type())) { + if (isPropertyChangesType(modelNode.type()) || isConnectionsType(modelNode.type()) + || isListElementType(modelNode.type()) + || modelNode.metaInfo().hasProperty(astPropertyName.toUtf8())) { AbstractProperty modelProperty = modelNode.property(astPropertyName.toUtf8()); QVariant variantValue = parsePropertyScriptBinding(script); if (!variantValue.isValid()) From a2474d59842b7e76fa52e85736883eb8854268b0 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Tue, 9 Apr 2024 23:51:27 +0300 Subject: [PATCH 114/202] QmlDesigner: Create user bundle folders if missing Change-Id: I955b1f1e5db3208eb578172529771910453adf68 Reviewed-by: Miikka Heikkinen Reviewed-by: --- .../contentlibraryusermodel.cpp | 20 ++++++++++++++----- .../contentlibrary/contentlibraryusermodel.h | 2 +- .../contentlibrary/contentlibraryview.cpp | 4 ++++ 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp index 6616b3f2528..a299b02db34 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp @@ -30,7 +30,7 @@ ContentLibraryUserModel::ContentLibraryUserModel(ContentLibraryWidget *parent) { m_userCategories = {tr("Materials")/*, tr("Textures"), tr("3D"), tr("Effects"), tr("2D components")*/}; // TODO - loadUserBundle(); + loadMaterialBundle(); } int ContentLibraryUserModel::rowCount(const QModelIndex &) const @@ -192,22 +192,32 @@ QJsonObject &ContentLibraryUserModel::bundleJsonObjectRef() return m_bundleObj; } -void ContentLibraryUserModel::loadUserBundle() +void ContentLibraryUserModel::loadMaterialBundle() { if (m_matBundleExists) return; QDir bundleDir{Paths::bundlesPathSetting() + "/User/materials"}; + bundleDir.mkpath("."); if (m_bundleObj.isEmpty()) { - QFile matsJsonFile(bundleDir.filePath("user_materials_bundle.json")); + auto jsonFilePath = Utils::FilePath::fromString(bundleDir.filePath("user_materials_bundle.json")); + if (!jsonFilePath.exists()) { + QString jsonContent = "{\n"; + jsonContent += " \"id\": \"UserMaterialBundle\",\n"; + jsonContent += " \"materials\": {\n"; + jsonContent += " }\n"; + jsonContent += "}"; + jsonFilePath.writeFileContents(jsonContent.toLatin1()); + } - if (!matsJsonFile.open(QIODevice::ReadOnly)) { + QFile jsonFile(jsonFilePath.path()); + if (!jsonFile.open(QIODevice::ReadOnly)) { qWarning("Couldn't open user_materials_bundle.json"); return; } - QJsonDocument matBundleJsonDoc = QJsonDocument::fromJson(matsJsonFile.readAll()); + QJsonDocument matBundleJsonDoc = QJsonDocument::fromJson(jsonFile.readAll()); if (matBundleJsonDoc.isNull()) { qWarning("Invalid user_materials_bundle.json file"); return; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h index 1bf939d3b82..e703ffea7d6 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h @@ -93,7 +93,7 @@ signals: void matBundleExistsChanged(); private: - void loadUserBundle(); + void loadMaterialBundle(); bool isValidIndex(int idx) const; void createImporter(const QString &bundlePath, const QString &bundleId, const QStringList &sharedFiles); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp index 632997b7c8f..91e402116a4 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp @@ -533,6 +533,10 @@ void ContentLibraryView::addLibMaterial(const ModelNode &mat, const QPixmap &ico auto [name, qml] = m_widget->userModel()->getUniqueLibMaterialNameAndQml( mat.variantProperty("objectName").value().toString()); + bundlePath.pathAppended("icons").createDir(); + bundlePath.pathAppended("images").createDir(); + bundlePath.pathAppended("shaders").createDir(); + QString iconPath = QLatin1String("icons/%1.png").arg(mat.id()); QString fullIconPath = bundlePath.pathAppended(iconPath).toString(); From bedf04175f748a4484fd7da6c8396a220eddd569 Mon Sep 17 00:00:00 2001 From: Teea Poldsam Date: Wed, 10 Apr 2024 13:50:47 +0300 Subject: [PATCH 115/202] Doc: Update Curves view doc - Update and add icons. - Add missing information. - Update obsolete information. Fixes: QDS-12444 Change-Id: I8063536ab98ca16541a66d9d12473f96446f7fc9 Reviewed-by: Johanna Vanhatapio Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tanja Remes --- .../images/icons/easing-curve-spline.png | Bin 0 -> 1606 bytes ...spline-icon.png => easing-curve-unify.png} | Bin .../src/views/qtquick-curve-editor.qdoc | 25 +++++++++++++----- 3 files changed, 18 insertions(+), 7 deletions(-) create mode 100644 doc/qtdesignstudio/images/icons/easing-curve-spline.png rename doc/qtdesignstudio/images/icons/{easing-curve-spline-icon.png => easing-curve-unify.png} (100%) diff --git a/doc/qtdesignstudio/images/icons/easing-curve-spline.png b/doc/qtdesignstudio/images/icons/easing-curve-spline.png new file mode 100644 index 0000000000000000000000000000000000000000..d76bf15f2105aab263eff491b29ea70866b2cd95 GIT binary patch literal 1606 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7I14-?iy0WWg+Z8+Vb&Z8 z1_qXQnIRD+5xzcF$@#f@i7EL>sd^Q;1q>iyV_#8_n4FzjqL7rDo|$K>^nUk#C56ls zTcvPQUjyF)=hTc$kE){7;3~h6MhI|Z8xtBTx$+|-gpg^Jvqyke^gTP3i$ zR(Zu%AYpwa1+bEmY+EHqkcA2nz5xo(`9-M;mU@P|$p!{S3g(u2DM^XODT&593PuJ- z#`=av`i9232Bua9W>zMa3Q(YAr(jc*l4cd;;s&*>C?(BSDWjyMz)D}gyu4hm+*mKa zC|%#s($Z4jz)0W7NVg~@O}Dr*uOzWTH?LS3W`avc7m0xRdD)WnkfqLBRj99T>Rz?`gLWU6PTpb?&#my%yztcj!{)g`ec)mF*K zz{o_`z(m*3AjHtv%Fx)#*hJgF*vh~FqSQA(B{QuOt5Q=dV>Ib3SVC<&%EN2#JuEGPZwLI zirfOd%*+(4l;jk1Ba1`}U6Z6F6J0YilQiALwB#gRQ`4jrBcoK~6ca-en0}Z1XEz% zmP5+3Fy%omZXhPOd;yg;b_$3>EHy91R;ftI-fr6TwEYYW4F5b`978Nl&%Lx)yERdS z;lur{T4mB~k{aFCiYs^jP)|`jF}*v{p4nzd5?jm|6J75oxx9$*dYjy_Q{Q^!)NjOE Date: Thu, 4 Apr 2024 17:22:26 +0200 Subject: [PATCH 116/202] QmlDesigner: Add property editor backend for editing non selected nodes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Nodes can be accessed via QmlModelNodeProxy * In QML ModelNodes are simply integers, we use the internal id * PropertyEditorSubSelectionWrapper can expose all properties of a node * Notifiers work Change-Id: Ic11ad7a77c851d116080139b9fd24a32e5986e30 Reviewed-by: Henning GrĂĽndl Reviewed-by: Qt CI Patch Build Bot --- .../propertyeditorqmlbackend.cpp | 21 ++ .../propertyeditor/propertyeditorqmlbackend.h | 14 +- .../propertyeditor/propertyeditorvalue.cpp | 261 +++++++++++++++++- .../propertyeditor/propertyeditorvalue.h | 37 +++ .../propertyeditor/propertyeditorview.cpp | 207 ++++++++------ .../propertyeditor/propertyeditorview.h | 10 + .../propertyeditor/qmlmodelnodeproxy.cpp | 232 ++++++++++++++++ .../propertyeditor/qmlmodelnodeproxy.h | 35 +++ 8 files changed, 736 insertions(+), 81 deletions(-) diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp index 7f1ab00bb94..6f3242a18e9 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp @@ -284,6 +284,27 @@ void PropertyEditorQmlBackend::setupAuxiliaryProperties(const QmlObjectNode &qml } } +void PropertyEditorQmlBackend::handleInstancePropertyChangedInModelNodeProxy( + const ModelNode &modelNode, const PropertyName &propertyName) +{ + m_backendModelNode.handleInstancePropertyChanged(modelNode, propertyName); +} + +void PropertyEditorQmlBackend::handleVariantPropertyChangedInModelNodeProxy(const VariantProperty &property) +{ + m_backendModelNode.handleVariantPropertyChanged(property); +} + +void PropertyEditorQmlBackend::handleBindingPropertyChangedInModelNodeProxy(const BindingProperty &property) +{ + m_backendModelNode.handleBindingPropertyChanged(property); +} + +void PropertyEditorQmlBackend::handlePropertiesRemovedInModelNodeProxy(const AbstractProperty &property) +{ + m_backendModelNode.handlePropertiesRemoved(property); +} + void PropertyEditorQmlBackend::createPropertyEditorValue(const QmlObjectNode &qmlObjectNode, const PropertyName &name, const QVariant &value, diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h index b677258488b..d4e158fca4e 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h @@ -3,10 +3,10 @@ #pragma once -#include "qmlanchorbindingproxy.h" #include "designerpropertymap.h" -#include "propertyeditorvalue.h" #include "propertyeditorcontextobject.h" +#include "propertyeditorvalue.h" +#include "qmlanchorbindingproxy.h" #include "qmlmodelnodeproxy.h" #include "quick2propertyeditorview.h" @@ -71,7 +71,15 @@ public: PropertyEditorView *propertyEditor); void setupInsightAttachedProperties(const QmlObjectNode &qmlObjectNode, PropertyEditorView *propertyEditor); - void setupAuxiliaryProperties(const QmlObjectNode &qmlObjectNode, PropertyEditorView *propertyEditor); + void setupAuxiliaryProperties(const QmlObjectNode &qmlObjectNode, + PropertyEditorView *propertyEditor); + + void handleInstancePropertyChangedInModelNodeProxy(const ModelNode &modelNode, + const PropertyName &propertyName); + + void handleVariantPropertyChangedInModelNodeProxy(const VariantProperty &property); + void handleBindingPropertyChangedInModelNodeProxy(const BindingProperty &property); + void handlePropertiesRemovedInModelNodeProxy(const AbstractProperty &property); static NodeMetaInfo findCommonAncestor(const ModelNode &node); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp index c1d91f907a9..70686f31ae9 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp @@ -10,9 +10,12 @@ #include "designmodewidget.h" #include "nodemetainfo.h" #include "nodeproperty.h" +#include "propertyeditorview.h" +#include "qmldesignerplugin.h" #include "qmlitemnode.h" #include "qmlobjectnode.h" -#include "qmldesignerplugin.h" +#include "rewritertransaction.h" +#include "rewritingexception.h" #include @@ -726,4 +729,260 @@ void PropertyEditorNodeWrapper::update() emit typeChanged(); } +QQmlPropertyMap *PropertyEditorSubSelectionWrapper::properties() +{ + return &m_valuesPropertyMap; +} + +static QObject *variantToQObject(const QVariant &value) +{ + if (value.typeId() == QMetaType::QObjectStar || value.typeId() > QMetaType::User) + return *(QObject **)value.constData(); + + return nullptr; +} + +void PropertyEditorSubSelectionWrapper::createPropertyEditorValue(const QmlObjectNode &qmlObjectNode, + const PropertyName &name, + const QVariant &value) +{ + PropertyName propertyName(name); + propertyName.replace('.', '_'); + auto valueObject = qobject_cast(variantToQObject(m_valuesPropertyMap.value(QString::fromUtf8(propertyName)))); + if (!valueObject) { + valueObject = new PropertyEditorValue(&m_valuesPropertyMap); + QObject::connect(valueObject, &PropertyEditorValue::valueChanged, this, &PropertyEditorSubSelectionWrapper::changeValue); + QObject::connect(valueObject, &PropertyEditorValue::expressionChanged, this, &PropertyEditorSubSelectionWrapper::changeExpression); + QObject::connect(valueObject, &PropertyEditorValue::exportPropertyAsAliasRequested, this, &PropertyEditorSubSelectionWrapper::exportPropertyAsAlias); + QObject::connect(valueObject, &PropertyEditorValue::removeAliasExportRequested, this, &PropertyEditorSubSelectionWrapper::removeAliasExport); + m_valuesPropertyMap.insert(QString::fromUtf8(propertyName), QVariant::fromValue(valueObject)); + } + valueObject->setName(name); + valueObject->setModelNode(qmlObjectNode); + + if (qmlObjectNode.propertyAffectedByCurrentState(name) && !(qmlObjectNode.modelNode().property(name).isBindingProperty())) + valueObject->setValue(qmlObjectNode.modelValue(name)); + + else + valueObject->setValue(value); + + if (propertyName != "id" && + qmlObjectNode.currentState().isBaseState() && + qmlObjectNode.modelNode().property(propertyName).isBindingProperty()) { + valueObject->setExpression(qmlObjectNode.modelNode().bindingProperty(propertyName).expression()); + } else { + if (qmlObjectNode.hasBindingProperty(name)) + valueObject->setExpression(qmlObjectNode.expression(name)); + else + valueObject->setExpression(qmlObjectNode.instanceValue(name).toString()); + } +} + +void PropertyEditorSubSelectionWrapper::exportPropertyAsAlias(const QString &name) +{ + if (name.isNull()) + return; + + if (locked()) + return; + + QTC_ASSERT(m_modelNode.isValid(), return); + + view()->executeInTransaction("PropertyEditorView::exportPropertyAsAlias", [this, name](){ + PropertyEditorView::generateAliasForProperty(m_modelNode, name); + }); +} + +void PropertyEditorSubSelectionWrapper::removeAliasExport(const QString &name) +{ + if (name.isNull()) + return; + + if (locked()) + return; + + QTC_ASSERT(m_modelNode.isValid(), return ); + + view()->executeInTransaction("PropertyEditorView::exportPropertyAsAlias", [this, name]() { + PropertyEditorView::removeAliasForProperty(m_modelNode, name); + }); +} + +bool PropertyEditorSubSelectionWrapper::locked() const +{ + return m_locked; +} + +PropertyEditorSubSelectionWrapper::PropertyEditorSubSelectionWrapper(const ModelNode &modelNode) + : m_modelNode(modelNode) +{ + QmlObjectNode qmlObjectNode(modelNode); + + QTC_ASSERT(qmlObjectNode.isValid(), return ); + + for (const auto &property : qmlObjectNode.modelNode().metaInfo().properties()) { + auto propertyName = property.name(); + createPropertyEditorValue(qmlObjectNode, + propertyName, + qmlObjectNode.instanceValue(propertyName)); + } +} + +ModelNode PropertyEditorSubSelectionWrapper::modelNode() const +{ + return m_modelNode; +} + +void PropertyEditorSubSelectionWrapper::deleteModelNode() +{ + QmlObjectNode objectNode(m_modelNode); + + view()->executeInTransaction("PropertyEditorView::changeExpression", [&] { + if (objectNode.isValid()) + objectNode.destroy(); + }); +} + +void PropertyEditorSubSelectionWrapper::changeValue(const QString &name) +{ + QTC_ASSERT(m_modelNode.isValid(), return ); + + if (name.isNull()) + return; + + if (locked()) + return; + + const QScopeGuard cleanup([&] { m_locked = false; }); + m_locked = true; + + const NodeMetaInfo metaInfo = m_modelNode.metaInfo(); + QVariant castedValue; + PropertyEditorValue *value = qobject_cast( + variantToQObject(m_valuesPropertyMap.value(name))); + + if (auto property = metaInfo.property(name.toUtf8())) { + castedValue = property.castedValue(value->value()); + + if (castedValue.typeId() == QVariant::Color) { + QColor color = castedValue.value(); + QColor newColor = QColor(color.name()); + newColor.setAlpha(color.alpha()); + castedValue = QVariant(newColor); + } + + if (!value->value().isValid()) { // reset + removePropertyFromModel(name.toUtf8()); + } else { + if (castedValue.isValid()) + commitVariantValueToModel(name.toUtf8(), castedValue); + } + } +} + +void PropertyEditorSubSelectionWrapper::setValueFromModel(const PropertyName &name, + const QVariant &value) +{ + m_locked = true; + + QmlObjectNode qmlObjectNode(m_modelNode); + + PropertyName propertyName = name; + propertyName.replace('.', '_'); + auto propertyValue = qobject_cast( + variantToQObject(m_valuesPropertyMap.value(QString::fromUtf8(propertyName)))); + if (propertyValue) + propertyValue->setValue(value); + m_locked = false; +} + +void PropertyEditorSubSelectionWrapper::resetValue(const PropertyName &name) +{ + auto propertyValue = qobject_cast( + variantToQObject(m_valuesPropertyMap.value(QString::fromUtf8(name)))); + if (propertyValue) + propertyValue->resetValue(); +} + +bool PropertyEditorSubSelectionWrapper::isRelevantModelNode(const ModelNode &modelNode) const +{ + QmlObjectNode objectNode(m_modelNode); + return modelNode == m_modelNode || objectNode.propertyChangeForCurrentState() == modelNode; +} + +void PropertyEditorSubSelectionWrapper::changeExpression(const QString &propertyName) +{ + PropertyName name = propertyName.toUtf8(); + + QTC_ASSERT(m_modelNode.isValid(), return ); + + if (name.isNull()) + return; + + if (locked()) + return; + + const QScopeGuard cleanup([&] { m_locked = false; }); + m_locked = true; + + view()->executeInTransaction("PropertyEditorView::changeExpression", [this, name, propertyName] { + QmlObjectNode qmlObjectNode{m_modelNode}; + PropertyEditorValue *value = qobject_cast( + variantToQObject(m_valuesPropertyMap.value(propertyName))); + + if (!value) { + qWarning() << "PropertyEditor::changeExpression no value for " << propertyName; + return; + } + + if (value->expression().isEmpty()) { + value->resetValue(); + return; + } + PropertyEditorView::setExpressionOnObjectNode(qmlObjectNode, name, value->expression()); + }); /* end of transaction */ +} + +void PropertyEditorSubSelectionWrapper::removePropertyFromModel(const PropertyName &propertyName) +{ + QTC_ASSERT(m_modelNode.isValid(), return ); + + m_locked = true; + try { + RewriterTransaction transaction = view()->beginRewriterTransaction( + "PropertyEditorView::removePropertyFromModel"); + + QmlObjectNode(m_modelNode).removeProperty(propertyName); + + transaction.commit(); + } catch (const RewritingException &e) { + e.showException(); + } + m_locked = false; +} + +void PropertyEditorSubSelectionWrapper::commitVariantValueToModel(const PropertyName &propertyName, + const QVariant &value) +{ + QTC_ASSERT(m_modelNode.isValid(), return ); + + try { + RewriterTransaction transaction = view()->beginRewriterTransaction( + "PropertyEditorView::commitVariantValueToMode"); + + QmlObjectNode(m_modelNode).setVariantProperty(propertyName, value); + + transaction.commit(); + } catch (const RewritingException &e) { + e.showException(); + } +} + +AbstractView *PropertyEditorSubSelectionWrapper::view() const +{ + QTC_CHECK(m_modelNode.isValid()); + + return m_modelNode.view(); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h index f621c9a5008..70a51fffc2c 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h @@ -14,6 +14,43 @@ namespace QmlDesigner { class PropertyEditorValue; +class PropertyEditorSubSelectionWrapper : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QQmlPropertyMap *properties READ properties NOTIFY propertiesChanged) + +signals: + void propertiesChanged(); + +public: + QQmlPropertyMap *properties(); + PropertyEditorSubSelectionWrapper(const ModelNode &modelNode); + ModelNode modelNode() const; + + Q_INVOKABLE void deleteModelNode(); + + void setValueFromModel(const PropertyName &name, const QVariant &value); + void resetValue(const PropertyName &name); + + bool isRelevantModelNode(const ModelNode &modelNode) const; + +private: + void changeValue(const QString &name); + void changeExpression(const QString &propertyName); + void createPropertyEditorValue(const QmlObjectNode &qmlObjectNode, const PropertyName &name, const QVariant &value); + void exportPropertyAsAlias(const QString &name); + void removeAliasExport(const QString &name); + bool locked() const; + + ModelNode m_modelNode; + QQmlPropertyMap m_valuesPropertyMap; + bool m_locked = false; + void removePropertyFromModel(const PropertyName &propertyName); + void commitVariantValueToModel(const PropertyName &propertyName, const QVariant &value); + AbstractView *view() const; +}; + class PropertyEditorNodeWrapper : public QObject { Q_OBJECT diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp index 1ff098f4eac..b22b39e238b 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp @@ -256,66 +256,19 @@ void PropertyEditorView::changeExpression(const QString &propertyName) underscoreName.replace('.', '_'); QmlObjectNode qmlObjectNode{m_selectedNode}; - PropertyEditorValue *value = m_qmlBackEndForCurrentType->propertyValueForName(QString::fromLatin1(underscoreName)); + PropertyEditorValue *value = m_qmlBackEndForCurrentType->propertyValueForName( + QString::fromUtf8(underscoreName)); if (!value) { qWarning() << "PropertyEditor::changeExpression no value for " << underscoreName; return; } - if (auto property = qmlObjectNode.modelNode().metaInfo().property(name)) { - const auto &propertType = property.propertyType(); - if (propertType.isColor()) { - if (QColor(value->expression().remove('"')).isValid()) { - qmlObjectNode.setVariantProperty(name, QColor(value->expression().remove('"'))); - return; - } - } else if (propertType.isBool()) { - if (isTrueFalseLiteral(value->expression())) { - if (value->expression().compare("true", Qt::CaseInsensitive) == 0) - qmlObjectNode.setVariantProperty(name, true); - else - qmlObjectNode.setVariantProperty(name, false); - return; - } - } else if (propertType.isInteger()) { - bool ok; - int intValue = value->expression().toInt(&ok); - if (ok) { - qmlObjectNode.setVariantProperty(name, intValue); - return; - } - } else if (propertType.isFloat()) { - bool ok; - qreal realValue = value->expression().toDouble(&ok); - if (ok) { - qmlObjectNode.setVariantProperty(name, realValue); - return; - } - } else if (propertType.isVariant()) { - bool ok; - qreal realValue = value->expression().toDouble(&ok); - if (ok) { - qmlObjectNode.setVariantProperty(name, realValue); - return; - } else if (isTrueFalseLiteral(value->expression())) { - if (value->expression().compare("true", Qt::CaseInsensitive) == 0) - qmlObjectNode.setVariantProperty(name, true); - else - qmlObjectNode.setVariantProperty(name, false); - return; - } - } - } - if (value->expression().isEmpty()) { value->resetValue(); return; } - - if (qmlObjectNode.expression(name) != value->expression() - || !qmlObjectNode.propertyAffectedByCurrentState(name)) - qmlObjectNode.setBindingProperty(name, value->expression()); + setExpressionOnObjectNode(qmlObjectNode, name, value->expression()); }); /* end of transaction */ } @@ -330,21 +283,8 @@ void PropertyEditorView::exportPropertyAsAlias(const QString &name) if (noValidSelection()) return; - executeInTransaction("PropertyEditorView::exportPropertyAsAlias", [this, name](){ - const QString id = m_selectedNode.validId(); - QString upperCasePropertyName = name; - upperCasePropertyName.replace(0, 1, upperCasePropertyName.at(0).toUpper()); - QString aliasName = id + upperCasePropertyName; - aliasName.replace(".", ""); //remove all dots - - PropertyName propertyName = aliasName.toUtf8(); - if (rootModelNode().hasProperty(propertyName)) { - Core::AsynchronousMessageBox::warning(tr("Cannot Export Property as Alias"), - tr("Property %1 does already exist for root component.").arg(aliasName)); - return; - } - rootModelNode().bindingProperty(propertyName).setDynamicTypeNameAndExpression("alias", id + "." + name); - }); + executeInTransaction("PropertyEditorView::exportPropertyAsAlias", + [this, name]() { generateAliasForProperty(m_selectedNode, name); }); } void PropertyEditorView::removeAliasExport(const QString &name) @@ -358,15 +298,8 @@ void PropertyEditorView::removeAliasExport(const QString &name) if (noValidSelection()) return; - executeInTransaction("PropertyEditorView::exportPropertyAsAlias", [this, name](){ - const QString id = m_selectedNode.validId(); - - for (const BindingProperty &property : rootModelNode().bindingProperties()) - if (property.expression() == (id + "." + name)) { - rootModelNode().removeProperty(property.name()); - break; - } - }); + executeInTransaction("PropertyEditorView::exportPropertyAsAlias", + [this, name]() { removeAliasForProperty(m_selectedNode, name); }); } bool PropertyEditorView::locked() const @@ -384,11 +317,113 @@ void PropertyEditorView::refreshMetaInfos(const TypeIds &deletedTypeIds) m_propertyComponentGenerator.refreshMetaInfos(deletedTypeIds); } +void PropertyEditorView::setExpressionOnObjectNode(const QmlObjectNode &constObjectNode, + const PropertyName &name, + const QString &newExpression) +{ + auto qmlObjectNode = constObjectNode; + auto expression = newExpression; + if (auto property = qmlObjectNode.modelNode().metaInfo().property(name)) { + const auto &propertType = property.propertyType(); + if (propertType.isColor()) { + if (QColor(expression.remove('"')).isValid()) { + qmlObjectNode.setVariantProperty(name, QColor(expression.remove('"'))); + return; + } + } else if (propertType.isBool()) { + if (isTrueFalseLiteral(expression)) { + if (expression.compare("true", Qt::CaseInsensitive) == 0) + qmlObjectNode.setVariantProperty(name, true); + else + qmlObjectNode.setVariantProperty(name, false); + return; + } + } else if (propertType.isInteger()) { + bool ok; + int intValue = expression.toInt(&ok); + if (ok) { + qmlObjectNode.setVariantProperty(name, intValue); + return; + } + } else if (propertType.isFloat()) { + bool ok; + qreal realValue = expression.toDouble(&ok); + if (ok) { + qmlObjectNode.setVariantProperty(name, realValue); + return; + } + } else if (propertType.isVariant()) { + bool ok; + qreal realValue = expression.toDouble(&ok); + if (ok) { + qmlObjectNode.setVariantProperty(name, realValue); + return; + } else if (isTrueFalseLiteral(expression)) { + if (expression.compare("true", Qt::CaseInsensitive) == 0) + qmlObjectNode.setVariantProperty(name, true); + else + qmlObjectNode.setVariantProperty(name, false); + return; + } + } + } + + if (qmlObjectNode.expression(name) != expression + || !qmlObjectNode.propertyAffectedByCurrentState(name)) + qmlObjectNode.setBindingProperty(name, expression); +} + +void PropertyEditorView::generateAliasForProperty(const ModelNode &modelNode, const QString &name) +{ + QTC_ASSERT(modelNode.isValid(), return ); + + auto view = modelNode.view(); + + auto rootNode = view->rootModelNode(); + + auto nonConstModelNode = modelNode; + const QString id = nonConstModelNode.validId(); + + QString upperCasePropertyName = name; + upperCasePropertyName.replace(0, 1, upperCasePropertyName.at(0).toUpper()); + QString aliasName = id + upperCasePropertyName; + aliasName.replace(".", ""); //remove all dots + + PropertyName propertyName = aliasName.toUtf8(); + if (rootNode.hasProperty(propertyName)) { + Core::AsynchronousMessageBox::warning( + tr("Cannot Export Property as Alias"), + tr("Property %1 does already exist for root component.").arg(aliasName)); + return; + } + rootNode.bindingProperty(propertyName).setDynamicTypeNameAndExpression("alias", id + "." + name); +} + +void PropertyEditorView::removeAliasForProperty(const ModelNode &modelNode, const QString &propertyName) +{ + QTC_ASSERT(modelNode.isValid(), return ); + + auto view = modelNode.view(); + + auto rootNode = view->rootModelNode(); + + auto nonConstModelNode = modelNode; + + const QString id = nonConstModelNode.validId(); + + for (const BindingProperty &property : rootNode.bindingProperties()) { + if (property.expression() == (id + "." + propertyName)) { + rootNode.removeProperty(property.name()); + break; + } + } +} + void PropertyEditorView::updateSize() { if (!m_qmlBackEndForCurrentType) return; - auto frame = m_qmlBackEndForCurrentType->widget()->findChild("propertyEditorFrame"); + auto frame = m_qmlBackEndForCurrentType->widget()->findChild("propertyEditorFrame"); if (frame) frame->resize(m_stackedWidget->size()); } @@ -747,7 +782,11 @@ void PropertyEditorView::propertiesRemoved(const QList &proper if (noValidSelection()) return; + QTC_ASSERT(m_qmlBackEndForCurrentType, return ); + for (const AbstractProperty &property : propertyList) { + m_qmlBackEndForCurrentType->handlePropertiesRemovedInModelNodeProxy(property); + ModelNode node(property.parentModelNode()); if (node.isRootNode() && !m_selectedNode.isRootNode()) @@ -805,7 +844,11 @@ void PropertyEditorView::variantPropertiesChanged(const QList& if (noValidSelection()) return; + QTC_ASSERT(m_qmlBackEndForCurrentType, return ); + for (const VariantProperty &property : propertyList) { + m_qmlBackEndForCurrentType->handleVariantPropertyChangedInModelNodeProxy(property); + ModelNode node(property.parentModelNode()); if (propertyIsAttachedLayoutProperty(property.name())) @@ -830,7 +873,11 @@ void PropertyEditorView::bindingPropertiesChanged(const QList & if (locked() || noValidSelection()) return; + QTC_ASSERT(m_qmlBackEndForCurrentType, return ); + for (const BindingProperty &property : propertyList) { + m_qmlBackEndForCurrentType->handleBindingPropertyChangedInModelNodeProxy(property); + ModelNode node(property.parentModelNode()); if (property.isAliasExport()) @@ -952,6 +999,9 @@ void PropertyEditorView::instancePropertyChanged(const QList; @@ -960,7 +1010,11 @@ void PropertyEditorView::instancePropertyChanged(const QListhandleInstancePropertyChangedInModelNodeProxy(modelNode, + propertyName); + + if (qmlObjectNode.isValid() && m_qmlBackEndForCurrentType && modelNode == m_selectedNode + && qmlObjectNode.currentState().isValid()) { const AbstractProperty property = modelNode.property(propertyName); if (modelNode == m_selectedNode || qmlObjectNode.propertyChangeForCurrentState() == qmlObjectNode) { if ( !modelNode.hasProperty(propertyName) || modelNode.property(property.name()).isBindingProperty() ) @@ -969,7 +1023,6 @@ void PropertyEditorView::instancePropertyChanged(const QList + +#include +#include +#include + +#include +#include + #include namespace QmlDesigner { @@ -17,6 +26,8 @@ void QmlModelNodeProxy::setup(const QmlObjectNode &objectNode) { m_qmlObjectNode = objectNode; + m_subselection.clear(); + emit modelNodeChanged(); } @@ -75,4 +86,225 @@ QString QmlModelNodeProxy::simplifiedTypeName() const return m_qmlObjectNode.simplifiedTypeName(); } +static QList toInternalIdList(const QList &nodes) +{ + return Utils::transform(nodes, [](const ModelNode &node) { return node.internalId(); }); } + +QList QmlModelNodeProxy::allChildren(int internalId) const +{ + ModelNode modelNode = m_qmlObjectNode.modelNode(); + + QTC_ASSERT(modelNode.isValid(), return {}); + + if (internalId >= 0) + modelNode = modelNode.view()->modelNodeForInternalId(internalId); + + return allChildren(modelNode); +} + +QList QmlModelNodeProxy::allChildrenOfType(const QString &typeName, int internalId) const +{ + ModelNode modelNode = m_qmlObjectNode.modelNode(); + + QTC_ASSERT(modelNode.isValid(), return {}); + + if (internalId >= 0) + modelNode = modelNode.view()->modelNodeForInternalId(internalId); + + return allChildrenOfType(modelNode, typeName); +} + +QString QmlModelNodeProxy::simplifiedTypeName(int internalId) const +{ + ModelNode modelNode = m_qmlObjectNode.modelNode(); + + QTC_ASSERT(modelNode.isValid(), return {}); + + return modelNode.view()->modelNodeForInternalId(internalId).simplifiedTypeName(); +} + +PropertyEditorSubSelectionWrapper *QmlModelNodeProxy::findWrapper(int internalId) const +{ + for (const auto &item : qAsConst(m_subselection)) { + if (item->modelNode().internalId() == internalId) + return item.data(); + } + + return nullptr; +} + +PropertyEditorSubSelectionWrapper *QmlModelNodeProxy::registerSubSelectionWrapper(int internalId) +{ + auto result = findWrapper(internalId); + + if (result) + return result; + + QTC_ASSERT(m_qmlObjectNode.isValid(), return nullptr); + + ModelNode node = m_qmlObjectNode.view()->modelNodeForInternalId(internalId); + + QTC_ASSERT(node.isValid(), return nullptr); + + QSharedPointer wrapper( + new PropertyEditorSubSelectionWrapper(node)); + m_subselection.append(wrapper); + + return wrapper.data(); +} + +void QmlModelNodeProxy::createModelNode(int internalIdParent, + const QString &property, + const QString &typeName, + const QString &requiredImport) +{ + QTC_ASSERT(m_qmlObjectNode.isValid(), return ); + + auto modelNode = m_qmlObjectNode.modelNode(); + + AbstractView *view = modelNode.view(); + + auto parentModelNode = m_qmlObjectNode.modelNode(); + if (internalIdParent >= 0) + parentModelNode = view->modelNodeForInternalId(internalIdParent); + + QTC_ASSERT(parentModelNode.isValid(), return ); + + Import import; + Q_ASSERT(import.isEmpty()); + + if (!requiredImport.isEmpty() && !view->model()->hasImport(requiredImport)) + import = Import::createLibraryImport(requiredImport); + + view->executeInTransaction("QmlModelNodeProxy::createModelNode", [&] { + if (!import.isEmpty()) + view->model()->changeImports({import}, {}); + +#ifdef QDS_USE_PROJECTSTORAGE + ModelNode newNode = view->createModelNode(type); +#else + NodeMetaInfo metaInfo = modelNode.model()->metaInfo(typeName.toUtf8()); + ModelNode newNode = view->createModelNode(metaInfo.typeName(), + metaInfo.majorVersion(), + metaInfo.minorVersion()); +#endif + parentModelNode.nodeAbstractProperty(property.toUtf8()).reparentHere(newNode); + }); +} + +void QmlModelNodeProxy::moveNode(int internalIdParent, + const QString &propertyName, + int fromIndex, + int toIndex) +{ + QTC_ASSERT(m_qmlObjectNode.isValid(), return ); + + ModelNode node = m_qmlObjectNode.view()->modelNodeForInternalId(internalIdParent); + + QTC_ASSERT(node.isValid(), return ); + AbstractView *view = m_qmlObjectNode.view(); + view->executeInTransaction("QmlModelNodeProxy::swapNode", [&] { + node.nodeListProperty(propertyName.toUtf8()).slide(fromIndex, toIndex); + }); +} + +bool QmlModelNodeProxy::isInstanceOf(const QString &typeName, int internalId) const +{ + ModelNode modelNode = m_qmlObjectNode.modelNode(); + + QTC_ASSERT(modelNode.isValid(), return {}); + + if (internalId >= 0) + modelNode = modelNode.view()->modelNodeForInternalId(internalId); + + NodeMetaInfo metaInfo = modelNode.model()->metaInfo(typeName.toUtf8()); + + return modelNode.metaInfo().isBasedOn(metaInfo); +} + +void QmlModelNodeProxy::changeType(int internalId, const QString &typeName) +{ + QTC_ASSERT(m_qmlObjectNode.isValid(), return ); + + ModelNode node = m_qmlObjectNode.view()->modelNodeForInternalId(internalId); + + QTC_ASSERT(node.isValid(), return ); + + QTC_ASSERT(!node.isRootNode(), return ); +#ifdef QDS_USE_PROJECTSTORAGE + node.changeType(typeName.toUtf8(), -1, -1); +#else + NodeMetaInfo metaInfo = node.model()->metaInfo(typeName.toUtf8()); + node.changeType(metaInfo.typeName(), metaInfo.majorVersion(), metaInfo.minorVersion()); +#endif +} + +void QmlModelNodeProxy::handleInstancePropertyChanged(const ModelNode &modelNode, + const PropertyName &propertyName) +{ + const QmlObjectNode qmlObjectNode(modelNode); + + for (const auto &item : qAsConst(m_subselection)) { + if (item->isRelevantModelNode(modelNode)) { + if (!modelNode.hasProperty(propertyName) + || modelNode.property(propertyName).isBindingProperty()) { + item->setValueFromModel(propertyName, qmlObjectNode.instanceValue(propertyName)); + } else { + item->setValueFromModel(propertyName, qmlObjectNode.modelValue(propertyName)); + } + } + } +} + +void QmlModelNodeProxy::handleBindingPropertyChanged(const BindingProperty &property) +{ + for (const auto &item : qAsConst(m_subselection)) { + if (item->isRelevantModelNode(property.parentModelNode())) { + QmlObjectNode objectNode(item->modelNode()); + if (objectNode.modelNode().property(property.name()).isBindingProperty()) + item->setValueFromModel(property.name(), objectNode.instanceValue(property.name())); + else + item->setValueFromModel(property.name(), objectNode.modelValue(property.name())); + } + } +} + +void QmlModelNodeProxy::handleVariantPropertyChanged(const VariantProperty &property) +{ + for (const auto &item : qAsConst(m_subselection)) { + if (item->isRelevantModelNode(property.parentModelNode())) { + QmlObjectNode objectNode(item->modelNode()); + if (objectNode.modelNode().property(property.name()).isBindingProperty()) + item->setValueFromModel(property.name(), objectNode.instanceValue(property.name())); + else + item->setValueFromModel(property.name(), objectNode.modelValue(property.name())); + } + } +} + +void QmlModelNodeProxy::handlePropertiesRemoved(const AbstractProperty &property) +{ + for (const auto &item : qAsConst(m_subselection)) { + if (item->isRelevantModelNode(property.parentModelNode())) { + QmlObjectNode objectNode(item->modelNode()); + item->resetValue(property.name()); + item->setValueFromModel(property.name(), objectNode.instanceValue(property.name())); + } + } +} + +QList QmlModelNodeProxy::allChildren(const ModelNode &modelNode) const +{ + return toInternalIdList(modelNode.directSubModelNodes()); +} + +QList QmlModelNodeProxy::allChildrenOfType(const ModelNode &modelNode, const QString &typeName) const +{ + QTC_ASSERT(modelNode.isValid(), return {}); + + NodeMetaInfo metaInfo = modelNode.model()->metaInfo(typeName.toUtf8()); + + return toInternalIdList(modelNode.directSubModelNodesOfType(metaInfo)); +} +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.h b/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.h index 4740b01fbda..d8a49d7e100 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.h +++ b/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.h @@ -3,6 +3,8 @@ #pragma once +#include "propertyeditorvalue.h" + #include #include @@ -16,6 +18,7 @@ class QMLDESIGNERCORE_EXPORT QmlModelNodeProxy : public QObject Q_PROPERTY(QmlDesigner::ModelNode modelNode READ modelNode NOTIFY modelNodeChanged) Q_PROPERTY(bool multiSelection READ multiSelection NOTIFY modelNodeChanged) + public: explicit QmlModelNodeProxy(QObject *parent = nullptr); @@ -36,13 +39,45 @@ public: QString simplifiedTypeName() const; + Q_INVOKABLE QList allChildren(int internalId = -1) const; + Q_INVOKABLE QList allChildrenOfType(const QString &typeName, int internalId = -1) const; + + Q_INVOKABLE QString simplifiedTypeName(int internalId) const; + + Q_INVOKABLE PropertyEditorSubSelectionWrapper *registerSubSelectionWrapper(int internalId); + + Q_INVOKABLE void createModelNode(int internalIdParent, + const QString &property, + const QString &typeName, + const QString &requiredImport = {}); + + Q_INVOKABLE void moveNode(int internalIdParent, + const QString &propertyName, + int fromIndex, + int toIndex); + + Q_INVOKABLE bool isInstanceOf(const QString &typeName, int internalId = -1) const; + + Q_INVOKABLE void changeType(int internalId, const QString &typeName); + + void handleInstancePropertyChanged(const ModelNode &modelNode, const PropertyName &propertyName); + + void handleBindingPropertyChanged(const BindingProperty &property); + void handleVariantPropertyChanged(const VariantProperty &property); + void handlePropertiesRemoved(const AbstractProperty &property); + signals: void modelNodeChanged(); void selectionToBeChanged(); void selectionChanged(); private: + QList allChildren(const ModelNode &modelNode) const; + QList allChildrenOfType(const ModelNode &modelNode, const QString &typeName) const; + PropertyEditorSubSelectionWrapper *findWrapper(int internalId) const; + QmlObjectNode m_qmlObjectNode; + QList> m_subselection; }; } //QmlDesigner From eae76dedffc5be389f4cda09bee57eb74a789ed8 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 9 Apr 2024 15:27:42 +0300 Subject: [PATCH 117/202] QmlDesigner: Change asset import path The only import path should be project root, so change things that are generated under asset_imports to be generated in new folder GeneratedComponents under project root. GeneratedComponents is prefixed to the type names of generated items as well. If project already contains asset_imports folder, then old location is used. Fixes: QDS-12430 Change-Id: I7a419fe1c5411e3d39bf3c1e659df0043c60ba33 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- .../imports/HelperWidgets/UrlChooser.qml | 5 +- .../effectcomposer/effectcomposermodel.cpp | 11 +- .../effectcomposer/effectcomposermodel.h | 3 + .../effectcomposer/effectcomposerview.cpp | 2 + .../effectcomposer/effectcomposerview.h | 2 + src/plugins/qmldesigner/CMakeLists.txt | 2 + .../assetslibrary/assetslibrarywidget.cpp | 10 +- .../modelnodecontextmenu_helper.cpp | 4 +- .../componentcore/modelnodeoperations.cpp | 38 ++--- .../contentlibrarybundleimporter.cpp | 38 ++--- .../contentlibrarybundleimporter.h | 2 +- .../contentlibraryeffectsmodel.cpp | 11 +- .../contentlibrarymaterialsmodel.cpp | 15 +- .../contentlibraryusermodel.cpp | 5 +- .../components/edit3d/edit3dview.cpp | 6 +- .../components/edit3d/edit3dwidget.cpp | 9 +- .../itemlibraryassetimportdialog.cpp | 45 +----- .../itemlibrary/itemlibraryassetimporter.cpp | 8 +- .../itemlibrary/itemlibrarymodel.cpp | 13 +- .../choosefrompropertylistdialog.cpp | 7 +- .../designercore/generatedcomponentutils.cpp | 141 ++++++++++++++++++ .../designercore/generatedcomponentutils.h | 38 +++++ .../include/subcomponentmanager.h | 2 + .../metainfo/subcomponentmanager.cpp | 17 ++- .../designercore/model/qmlitemnode.cpp | 7 +- src/plugins/qmldesigner/documentmanager.cpp | 5 + src/plugins/qmldesigner/documentmanager.h | 6 + .../qmldesigner/qmldesignerconstants.h | 12 +- 28 files changed, 336 insertions(+), 128 deletions(-) create mode 100644 src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp create mode 100644 src/plugins/qmldesigner/designercore/generatedcomponentutils.h diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml index c73e736ef42..0e42515c76c 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml @@ -448,9 +448,10 @@ Row { for (var j = 0; j < myModel.length; ++j) { let item = myModel[j] if (root.hideDuplicates && nameMap.has(item.fileName)) { - // Prefer hiding imported asset files rather than other project files + // Prefer hiding generated component files rather than other project files let listIndex = nameMap.get(item.fileName) - if (comboBox.listModel.get(listIndex).absoluteFilePath.includes("/asset_imports/")) { + let absPath = comboBox.listModel.get(listIndex).absoluteFilePath + if (absPath.includes("/GeneratedComponents/") || absPath.includes("/asset_imports/")) { comboBox.listModel.set(listIndex, { absoluteFilePath: item.absoluteFilePath, relativeFilePath: item.relativeFilePath, diff --git a/src/plugins/effectcomposer/effectcomposermodel.cpp b/src/plugins/effectcomposer/effectcomposermodel.cpp index 606ca8e93e2..f62af067c86 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.cpp +++ b/src/plugins/effectcomposer/effectcomposermodel.cpp @@ -94,6 +94,11 @@ bool EffectComposerModel::setData(const QModelIndex &index, const QVariant &valu return true; } +void EffectComposerModel::setEffectsTypePrefix(const QString &prefix) +{ + m_effectTypePrefix = prefix; +} + void EffectComposerModel::setIsEmpty(bool val) { if (m_isEmpty != val) { @@ -1059,7 +1064,7 @@ void EffectComposerModel::saveResources(const QString &name) Utils::FilePath qmldirPath = effectsResDir.resolvePath(qmldirFileName); QString qmldirContent = QString::fromUtf8(qmldirPath.fileContents().value_or(QByteArray())); if (qmldirContent.isEmpty()) { - qmldirContent.append("module Effects\n"); + qmldirContent.append(QString("module %1\n").arg(m_effectTypePrefix)); qmldirPath.writeFileContents(qmldirContent.toUtf8()); } @@ -1077,7 +1082,7 @@ void EffectComposerModel::saveResources(const QString &name) qmldirPath = effectPath.resolvePath(qmldirFileName); qmldirContent = QString::fromUtf8(qmldirPath.fileContents().value_or(QByteArray())); if (qmldirContent.isEmpty()) { - qmldirContent.append("module Effects."); + qmldirContent.append(QString("module %1.").arg(m_effectTypePrefix)); qmldirContent.append(name); qmldirContent.append('\n'); qmldirContent.append(name); @@ -1187,7 +1192,7 @@ void EffectComposerModel::saveResources(const QString &name) endResetModel(); } - emit resourcesSaved(QString("Effects.%1.%1").arg(name).toUtf8(), effectPath); + emit resourcesSaved(QString("%1.%2.%2").arg(m_effectTypePrefix, name).toUtf8(), effectPath); } void EffectComposerModel::resetEffectError(int type) diff --git a/src/plugins/effectcomposer/effectcomposermodel.h b/src/plugins/effectcomposer/effectcomposermodel.h index 0c1b6355f9e..2381b09ec1c 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.h +++ b/src/plugins/effectcomposer/effectcomposermodel.h @@ -59,6 +59,8 @@ public: QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex &index, const QVariant &value, int role) override; + void setEffectsTypePrefix(const QString &prefix); + bool isEmpty() const { return m_isEmpty; } void setIsEmpty(bool val); @@ -221,6 +223,7 @@ private: QString m_currentComposition; QTimer m_rebakeTimer; int m_extraMargin = 0; + QString m_effectTypePrefix; const QRegularExpression m_spaceReg = QRegularExpression("\\s+"); }; diff --git a/src/plugins/effectcomposer/effectcomposerview.cpp b/src/plugins/effectcomposer/effectcomposerview.cpp index ed50c389a83..c7967db4ae9 100644 --- a/src/plugins/effectcomposer/effectcomposerview.cpp +++ b/src/plugins/effectcomposer/effectcomposerview.cpp @@ -31,6 +31,7 @@ void EffectComposerContext::contextHelp(const HelpCallback &callback) const EffectComposerView::EffectComposerView(QmlDesigner::ExternalDependenciesInterface &externalDependencies) : AbstractView{externalDependencies} + , m_componentUtils(externalDependencies) { } @@ -91,6 +92,7 @@ void EffectComposerView::modelAttached(QmlDesigner::Model *model) if (m_currProjectPath != currProjectPath) { // starting a new project m_widget->effectComposerNodesModel()->loadModel(); m_widget->effectComposerModel()->clear(true); + m_widget->effectComposerModel()->setEffectsTypePrefix(m_componentUtils.composedEffectsTypePrefix()); m_widget->effectComposerModel()->setIsEnabled( !QmlDesigner::DesignerMcuManager::instance().isMCUProject()); m_widget->initView(); diff --git a/src/plugins/effectcomposer/effectcomposerview.h b/src/plugins/effectcomposer/effectcomposerview.h index 49a7b32621b..b264fe0fd9a 100644 --- a/src/plugins/effectcomposer/effectcomposerview.h +++ b/src/plugins/effectcomposer/effectcomposerview.h @@ -6,6 +6,7 @@ #include "abstractview.h" #include "modelnode.h" +#include #include #include @@ -45,6 +46,7 @@ private: QPointer m_widget; QString m_currProjectPath; + QmlDesigner::GeneratedComponentUtils m_componentUtils; }; } // namespace EffectComposer diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 1c7bebffa32..520c4ebc799 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -94,6 +94,8 @@ add_qtc_library(QmlDesignerCore STATIC SOURCES rewritertransaction.cpp rewritertransaction.h + generatedcomponentutils.cpp + generatedcomponentutils.h ) extend_qtc_library(QmlDesignerCore diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp index 3b98eb6baf9..bcaa5351fd0 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp @@ -287,14 +287,16 @@ void AssetsLibraryWidget::handleDeleteEffects([[maybe_unused]] const QStringList // Remove usages of deleted effects from the current document m_assetsView->executeInTransaction(__FUNCTION__, [&]() { QList allNodes = m_assetsView->allModelNodes(); - const QString typeTemplate = "Effects.%1.%1"; - const QString importUrlTemplate = "Effects.%1"; + const QString typeTemplate = "%1.%2.%2"; + const QString importUrlTemplate = "%1.%2"; const Imports imports = m_assetsView->model()->imports(); Imports removedImports; + const QString typePrefix = QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().composedEffectsTypePrefix(); for (const QString &effectName : effectNames) { if (effectName.isEmpty()) continue; - const TypeName type = typeTemplate.arg(effectName).toUtf8(); + const TypeName type = typeTemplate.arg(typePrefix, effectName).toUtf8(); for (ModelNode &node : allNodes) { if (node.metaInfo().typeName() == type) { clearStacks = true; @@ -302,7 +304,7 @@ void AssetsLibraryWidget::handleDeleteEffects([[maybe_unused]] const QStringList } } - const QString importPath = importUrlTemplate.arg(effectName); + const QString importPath = importUrlTemplate.arg(typePrefix, effectName); Import removedImport = Utils::findOrDefault(imports, [&importPath](const Import &import) { return import.url() == importPath; }); diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp index f6e18458b2f..8257926d720 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp @@ -105,8 +105,10 @@ bool selectionIsImported3DAsset(const SelectionContext &selectionState) // Node is not a file component, so we have to check if the current doc itself is fileName = node.model()->fileUrl().toLocalFile(); } - if (fileName.contains(Constants::QUICK_3D_ASSETS_FOLDER)) + if (fileName.contains(Constants::OLD_QUICK_3D_ASSETS_FOLDER) + || fileName.contains(Constants::QUICK_3D_COMPONENTS_FOLDER)) { return true; + } } return false; } diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index cebe7d7c534..8b41cfb6929 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -1145,11 +1145,8 @@ static QString getAssetDefaultDirectory(const QString &assetDir, const QString & Utils::FilePath assetPath = contentPath.pathAppended(assetDir); - if (!assetPath.exists()) { - // Create the default asset type directory if it doesn't exist - QDir dir(contentPath.toString()); - dir.mkpath(assetDir); - } + if (!assetPath.exists()) + assetPath.createDir(); if (assetPath.exists() && assetPath.isDir()) adjustedDefaultDirectory = assetPath.toString(); @@ -1727,13 +1724,12 @@ void openOldEffectMaker(const QString &filePath) return; } - Utils::FilePath projectPath = target->project()->projectDirectory(); - QString effectName = QFileInfo(filePath).baseName(); - QString effectResDir = QLatin1String(Constants::DEFAULT_EFFECTS_IMPORT_FOLDER) - + "/" + effectName; - Utils::FilePath effectResPath = projectPath.pathAppended(effectResDir); + Utils::FilePath effectResPath = QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().composedEffectsBasePath() + .pathAppended(QFileInfo(filePath).baseName()); + if (!effectResPath.exists()) - QDir().mkpath(effectResPath.toString()); + effectResPath.createDir(); const QtSupport::QtVersion *baseQtVersion = QtSupport::QtKitAspect::qtVersion(target->kit()); if (baseQtVersion) { @@ -1769,14 +1765,11 @@ void openOldEffectMaker(const QString &filePath) Utils::FilePath getEffectsImportDirectory() { - QString defaultDir = QLatin1String(Constants::DEFAULT_EFFECTS_IMPORT_FOLDER); - Utils::FilePath projectPath = QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath(); - Utils::FilePath effectsPath = projectPath.pathAppended(defaultDir); + Utils::FilePath effectsPath = QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().composedEffectsBasePath(); - if (!effectsPath.exists()) { - QDir dir(projectPath.toString()); - dir.mkpath(effectsPath.toString()); - } + if (!effectsPath.exists()) + effectsPath.createDir(); return effectsPath; } @@ -1794,12 +1787,9 @@ QString getEffectsDefaultDirectory(const QString &defaultDir) QString getEffectIcon(const QString &effectPath) { - Utils::FilePath projectPath = QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath(); - QString effectName = QFileInfo(effectPath).baseName(); - QString effectResDir = "asset_imports/Effects/" + effectName; - Utils::FilePath effectResPath = projectPath.resolvePath(effectResDir + "/" + effectName + ".qml"); - - return effectResPath.exists() ? QString("effectExported") : QString("effectClass"); + Utils::FilePath effectFile = QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().composedEffectPath(effectPath); + return effectFile.exists() ? QString("effectExported") : QString("effectClass"); } bool useLayerEffect() diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp index 9e6bdd03b9b..5c8d42a3065 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp @@ -32,9 +32,6 @@ ContentLibraryBundleImporter::ContentLibraryBundleImporter(const QString &bundle { m_importTimer.setInterval(200); connect(&m_importTimer, &QTimer::timeout, this, &ContentLibraryBundleImporter::handleImportTimer); - m_moduleName = QStringLiteral("%1.%2").arg( - QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER), - m_bundleId).mid(1); // Chop leading slash } // Returns empty string on success or an error message on failure. @@ -69,7 +66,7 @@ QString ContentLibraryBundleImporter::importComponent(const QString &qmlFile, QString qmldirContent = QString::fromUtf8(qmldirPath.fileContents().value_or(QByteArray())); if (qmldirContent.isEmpty()) { qmldirContent.append("module "); - qmldirContent.append(m_moduleName); + qmldirContent.append(moduleName()); qmldirContent.append('\n'); } @@ -77,7 +74,9 @@ QString ContentLibraryBundleImporter::importComponent(const QString &qmlFile, const bool qmlFileExists = qmlSourceFile.exists(); const QString qmlType = qmlSourceFile.baseName(); const QString fullTypeName = QStringLiteral("%1.%2.%3") - .arg(QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1), m_bundleId, qmlType); + .arg(QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().componentBundlesTypePrefix(), + m_bundleId, qmlType); if (m_pendingTypes.contains(fullTypeName) && !m_pendingTypes[fullTypeName]) return QStringLiteral("Unable to import while unimporting the same type: '%1'").arg(fullTypeName); if (!qmldirContent.contains(qmlFile)) { @@ -126,7 +125,7 @@ QString ContentLibraryBundleImporter::importComponent(const QString &qmlFile, if (!model) return "Model not available, cannot add import statement or update code model"; - Import import = Import::createLibraryImport(m_moduleName, "1.0"); + Import import = Import::createLibraryImport(moduleName(), "1.0"); if (!model->hasImport(import)) { if (model->possibleImports().contains(import)) { m_importAddPending = false; @@ -134,7 +133,7 @@ QString ContentLibraryBundleImporter::importComponent(const QString &qmlFile, model->changeImports({import}, {}); } catch (const RewritingException &) { // No point in trying to add import asynchronously either, so just fail out - return QStringLiteral("Failed to add import statement for: '%1'").arg(m_moduleName); + return QStringLiteral("Failed to add import statement for: '%1'").arg(moduleName()); } } else { // If import is not yet possible, import statement needs to be added asynchronously to @@ -188,7 +187,7 @@ void ContentLibraryBundleImporter::handleImportTimer() if (m_importAddPending) { try { - Import import = Import::createLibraryImport(m_moduleName, "1.0"); + Import import = Import::createLibraryImport(moduleName(), "1.0"); if (model->possibleImports().contains(import)) { model->changeImports({import}, {}); m_importAddPending = false; @@ -253,6 +252,13 @@ void ContentLibraryBundleImporter::writeAssetRefMap(const Utils::FilePath &bundl } } +QString ContentLibraryBundleImporter::moduleName() +{ + return QStringLiteral("%1.%2").arg(QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().componentBundlesTypePrefix(), + m_bundleId); +} + QString ContentLibraryBundleImporter::unimportComponent(const QString &qmlFile) { FilePath bundleImportPath = resolveBundleImportPath(); @@ -275,7 +281,9 @@ QString ContentLibraryBundleImporter::unimportComponent(const QString &qmlFile) QString qmlType = qmlFilePath.baseName(); const QString fullTypeName = QStringLiteral("%1.%2.%3") - .arg(QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1), m_bundleId, qmlType); + .arg(QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().componentBundlesTypePrefix(), + m_bundleId, qmlType); if (m_pendingTypes.contains(fullTypeName) && m_pendingTypes[fullTypeName]) return QStringLiteral("Unable to unimport while importing the same type: '%1'").arg(fullTypeName); @@ -327,7 +335,7 @@ QString ContentLibraryBundleImporter::unimportComponent(const QString &qmlFile) auto doc = QmlDesignerPlugin::instance()->currentDesignDocument(); Model *model = doc ? doc->currentModel() : nullptr; if (model) { - Import import = Import::createLibraryImport(m_moduleName, "1.0"); + Import import = Import::createLibraryImport(moduleName(), "1.0"); if (model->imports().contains(import)) model->changeImports({}, {import}); } @@ -342,16 +350,12 @@ QString ContentLibraryBundleImporter::unimportComponent(const QString &qmlFile) FilePath ContentLibraryBundleImporter::resolveBundleImportPath() { - FilePath bundleImportPath = QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath(); + FilePath bundleImportPath = QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().componentBundlesBasePath(); if (bundleImportPath.isEmpty()) return bundleImportPath; - const QString projectBundlePath = QStringLiteral("%1%2/%3").arg( - QLatin1String(Constants::DEFAULT_ASSET_IMPORT_FOLDER), - QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER), - m_bundleId).mid(1); // Chop leading slash - - return bundleImportPath.resolvePath(projectBundlePath); + return bundleImportPath.resolvePath(m_bundleId); } } // namespace QmlDesigner::Internal diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h index 3aff09fe343..7fb2a48886d 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h @@ -46,10 +46,10 @@ private: void handleImportTimer(); QVariantHash loadAssetRefMap(const Utils::FilePath &bundlePath); void writeAssetRefMap(const Utils::FilePath &bundlePath, const QVariantHash &assetRefMap); + QString moduleName(); Utils::FilePath m_bundleDir; QString m_bundleId; - QString m_moduleName; QStringList m_sharedFiles; QTimer m_importTimer; int m_importTimerCount = 0; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp index 6b1de2d2a73..334c017116c 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp @@ -7,8 +7,8 @@ #include "contentlibraryeffect.h" #include "contentlibraryeffectscategory.h" #include "contentlibrarywidget.h" -#include "qmldesignerconstants.h" +#include #include #include #include @@ -187,10 +187,11 @@ void ContentLibraryEffectsModel::loadBundle() QUrl icon = QUrl::fromLocalFile(bundleDir.filePath(itemObj.value("icon").toString())); QString qml = itemObj.value("qml").toString(); - TypeName type = QLatin1String("%1.%2.%3").arg( - QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1), - bundleId, - qml.chopped(4)).toLatin1(); // chopped(4): remove .qml + TypeName type = QLatin1String("%1.%2.%3") + .arg(QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().componentBundlesTypePrefix(), + bundleId, + qml.chopped(4)).toLatin1(); // chopped(4): remove .qml auto bundleItem = new ContentLibraryEffect(category, item, qml, type, icon, files); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp index 7594c691b50..53624bfeaa8 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp @@ -8,12 +8,12 @@ #include "contentlibrarymaterialscategory.h" #include "contentlibrarywidget.h" -#include +#include "designerpaths.h" #include "filedownloader.h" #include "fileextractor.h" #include "multifiledownloader.h" -#include "qmldesignerconstants.h" -#include "qmldesignerplugin.h" + +#include #include #include @@ -286,10 +286,11 @@ void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &matBundleDir) QUrl icon = QUrl::fromLocalFile(matBundleDir.filePath(matObj.value("icon").toString())); QString qml = matObj.value("qml").toString(); - TypeName type = QLatin1String("%1.%2.%3").arg( - QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1), - bundleId, - qml.chopped(4)).toLatin1(); // chopped(4): remove .qml + TypeName type = QLatin1String("%1.%2.%3") + .arg(QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().componentBundlesTypePrefix(), + bundleId, + qml.chopped(4)).toLatin1(); // chopped(4): remove .qml auto bundleMat = new ContentLibraryMaterial(category, mat, qml, type, icon, files, m_downloadPath, m_baseUrl); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp index a299b02db34..98659221e41 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp @@ -7,9 +7,9 @@ #include "contentlibrarymaterial.h" #include "contentlibrarymaterialscategory.h" #include "contentlibrarywidget.h" -#include "qmldesignerconstants.h" #include +#include #include #include @@ -133,7 +133,8 @@ QPair ContentLibraryUserModel::getUniqueLibMaterialNameAndQml( TypeName ContentLibraryUserModel::qmlToModule(const QString &qmlName) const { - return QLatin1String("%1.%2.%3").arg(QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1), + return QLatin1String("%1.%2.%3").arg(QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().componentBundlesTypePrefix(), m_bundleId, qmlName.chopped(4)).toLatin1(); // chopped(4): remove .qml } diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index 911ee1fb156..4a05c1bc5a7 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -356,7 +356,8 @@ void Edit3DView::handleEntriesChanged() append(model()->qtQuick3DOrthographicCameraMetaInfo(), EK_cameras); append(model()->qtQuick3DPerspectiveCameraMetaInfo(), EK_cameras); - auto assetsModule = model()->module("Quick3DAssets"); + auto assetsModule = model()->module(QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().import3dTypePrefix()); for (const auto &metaInfo : model()->metaInfosForModule(assetsModule)) append(metaInfo, EK_importedModels); @@ -373,7 +374,8 @@ void Edit3DView::handleEntriesChanged() } else if (entry.typeName() == "QtQuick3D.OrthographicCamera" || entry.typeName() == "QtQuick3D.PerspectiveCamera") { entryKey = EK_cameras; - } else if (entry.typeName().startsWith("Quick3DAssets.") + } else if (entry.typeName().startsWith(QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().import3dTypePrefix().toUtf8()) && NodeHints::fromItemLibraryEntry(entry).canBeDroppedInView3D()) { entryKey = EK_importedModels; } else { diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index 07102ae8936..033cc627701 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -766,7 +766,10 @@ void Edit3DWidget::dropEvent(QDropEvent *dropEvent) QString fileName = QFileInfo(assetPath).baseName(); fileName = fileName.at(0).toUpper() + fileName.mid(1); // capitalize first letter auto model = m_view->model(); - auto metaInfo = model->metaInfo(model->module("Quick3DAssets"), fileName.toUtf8()); + auto metaInfo = model->metaInfo(model->module( + QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().import3dTypePrefix()), + fileName.toUtf8()); if (auto entries = metaInfo.itemLibrariesEntries(); entries.size()) { auto entry = ItemLibraryEntry{entries.front(), *model->projectStorage()}; QmlVisualNode::createQml3DNode(view(), entry, m_canvas->activeScene(), {}, false); @@ -780,7 +783,9 @@ void Edit3DWidget::dropEvent(QDropEvent *dropEvent) for (const QString &assetPath : added3DAssets) { QString fileName = QFileInfo(assetPath).baseName(); fileName = fileName.at(0).toUpper() + fileName.mid(1); // capitalize first letter - QString type = QString("Quick3DAssets.%1.%1").arg(fileName); + QString type = QString("%1.%2.%2").arg(QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().import3dTypePrefix(), + fileName); QList entriesForType = itemLibInfo->entriesForType(type.toUtf8()); if (!entriesForType.isEmpty()) { // should always be true, but just in case QmlVisualNode::createQml3DNode(view(), diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp index 29fff4b359a..b8a75851f62 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -130,43 +131,8 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog( if (targetDir.isEmpty()) targetDir = defaulTargetDirectory; - // Import is always done under known folder. The order of preference for folder is: - // 1) An existing QUICK_3D_ASSETS_FOLDER under DEFAULT_ASSET_IMPORT_FOLDER project import path - // 2) An existing QUICK_3D_ASSETS_FOLDER under any project import path - // 3) New QUICK_3D_ASSETS_FOLDER under DEFAULT_ASSET_IMPORT_FOLDER project import path - // 4) New QUICK_3D_ASSETS_FOLDER under any project import path - // 5) New QUICK_3D_ASSETS_FOLDER under new DEFAULT_ASSET_IMPORT_FOLDER under project - const QString defaultAssetFolder = QLatin1String(Constants::DEFAULT_ASSET_IMPORT_FOLDER); - const QString quick3DFolder = QLatin1String(Constants::QUICK_3D_ASSETS_FOLDER); - QString candidatePath = targetDir + defaultAssetFolder + quick3DFolder; - int candidatePriority = 5; - - for (const auto &importPath : std::as_const(importPaths)) { - if (importPath.startsWith(targetDir)) { - const bool isDefaultFolder = importPath.endsWith(defaultAssetFolder); - const QString assetFolder = importPath + quick3DFolder; - const bool exists = QFileInfo::exists(assetFolder); - if (exists) { - if (isDefaultFolder) { - // Priority one location, stop looking - candidatePath = assetFolder; - break; - } else if (candidatePriority > 2) { - candidatePriority = 2; - candidatePath = assetFolder; - } - } else { - if (candidatePriority > 3 && isDefaultFolder) { - candidatePriority = 3; - candidatePath = assetFolder; - } else if (candidatePriority > 4) { - candidatePriority = 4; - candidatePath = assetFolder; - } - } - } - } - m_quick3DImportPath = candidatePath; + m_quick3DImportPath = QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().import3dBasePath().toString(); if (!m_quick3DFiles.isEmpty()) { QVector groups; @@ -294,11 +260,14 @@ void ItemLibraryAssetImportDialog::updateImport(const ModelNode &updateNode, QFileInfo compFileInfo{compFileName}; // Find to top asset folder - const QString assetFolder = QLatin1String(Constants::QUICK_3D_ASSETS_FOLDER).mid(1); + const QString oldAssetFolder = QLatin1String(Constants::OLD_QUICK_3D_ASSETS_FOLDER); + QString assetFolder = QLatin1String(Constants::QUICK_3D_COMPONENTS_FOLDER); const QStringList parts = compFileName.split('/'); int i = parts.size() - 1; int previousSize = 0; for (; i >= 0; --i) { + if (parts[i] == oldAssetFolder) + assetFolder = oldAssetFolder; if (parts[i] == assetFolder) break; previousSize = parts[i].size(); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp index 48958ceec9e..ed1f8041e9c 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp @@ -1,6 +1,7 @@ // Copyright (C) 2019 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "itemlibraryassetimporter.h" + #include "assetimportupdatedialog.h" #include "qmldesignerplugin.h" #include "qmldesignerconstants.h" @@ -329,12 +330,15 @@ void ItemLibraryAssetImporter::postParseQuick3DAsset(ParseData &pd) if (qmldirFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { QString qmlInfo; qmlInfo.append("module "); - qmlInfo.append(m_importPath.split('/').last()); + qmlInfo.append(QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().import3dTypePrefix()); qmlInfo.append("."); qmlInfo.append(pd.assetName); qmlInfo.append('\n'); m_requiredImports.append( - QStringLiteral("%1.%2").arg(pd.targetDir.dirName(), pd.assetName)); + QStringLiteral("%1.%2").arg(QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().import3dTypePrefix(), + pd.assetName)); while (qmlIt.hasNext()) { qmlIt.next(); QFileInfo fi = QFileInfo(qmlIt.filePath()); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp index dbeacc75954..50bb01a5bbb 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp @@ -312,9 +312,12 @@ void ItemLibraryModel::update([[maybe_unused]] ItemLibraryInfo *itemLibraryInfo, beginResetModel(); clearSections(); + GeneratedComponentUtils compUtils = QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils(); + QStringList excludedImports { - QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1) + ".MaterialBundle", - QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1) + ".EffectBundle" + compUtils.componentBundlesTypePrefix() + ".MaterialBundle", + compUtils.componentBundlesTypePrefix() + ".EffectBundle" }; // create import sections @@ -323,10 +326,12 @@ void ItemLibraryModel::update([[maybe_unused]] ItemLibraryInfo *itemLibraryInfo, QHash importHash; for (const Import &import : model->imports()) { if (import.url() != projectName) { - if (excludedImports.contains(import.url()) || import.url().startsWith("Effects.")) + if (excludedImports.contains(import.url()) + || import.url().startsWith(compUtils.composedEffectsTypePrefix())) { continue; + } bool addNew = true; - bool isQuick3DAsset = import.url().startsWith("Quick3DAssets."); + bool isQuick3DAsset = import.url().startsWith(compUtils.import3dTypePrefix()); QString importUrl = import.url(); if (isQuick3DAsset) importUrl = ItemLibraryImport::quick3DAssetsTitle(); diff --git a/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp b/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp index 58b4c427499..a3ab5f2cd71 100644 --- a/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp +++ b/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp @@ -5,6 +5,8 @@ #include "nodemetainfo.h" #include "ui_choosefrompropertylistdialog.h" +#include + namespace QmlDesigner { // This will filter and return possible properties that the given type can be bound to @@ -100,7 +102,10 @@ ChooseFromPropertyListFilter::ChooseFromPropertyListFilter(const NodeMetaInfo &i #ifdef QDS_USE_PROJECTSTORAGE // TODO add the types here or use the module #else - } else if (insertInfo.typeName().startsWith("ComponentBundles.MaterialBundle")) { + } else if (insertInfo.typeName().startsWith( + QString("%1.MaterialBundle").arg(QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().componentBundlesTypePrefix()) + .toUtf8())) { if (parentInfo.isQtQuick3DModel()) propertyList.append("materials"); #endif diff --git a/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp b/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp new file mode 100644 index 00000000000..a86388625a5 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp @@ -0,0 +1,141 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "generatedcomponentutils.h" + +#include + +namespace QmlDesigner { + +GeneratedComponentUtils::GeneratedComponentUtils(ExternalDependenciesInterface &externalDependencies) + : m_externalDependencies(externalDependencies) +{ +} + +Utils::FilePath GeneratedComponentUtils::generatedComponentsPath() const +{ + Utils::FilePath projectPath = Utils::FilePath::fromString(m_externalDependencies.currentProjectDirPath()); + if (projectPath.isEmpty()) + return {}; + + Utils::FilePath assetImportsPath = projectPath.resolvePath(QLatin1String(Constants::OLD_ASSET_IMPORT_FOLDER)); + if (assetImportsPath.exists()) + return assetImportsPath; + + Utils::FilePath componentsPath = projectPath.resolvePath(QLatin1String(Constants::GENERATED_COMPONENTS_FOLDER)); + if (!componentsPath.exists()) + componentsPath.createDir(); + + return componentsPath; +} + +Utils::FilePath GeneratedComponentUtils::composedEffectsBasePath() const +{ + Utils::FilePath basePath = generatedComponentsPath(); + if (basePath.isEmpty()) + return {}; + + QString effectsImportPath; + if (basePath.endsWith(Constants::OLD_ASSET_IMPORT_FOLDER)) + effectsImportPath = Constants::OLD_EFFECTS_FOLDER; + else + effectsImportPath = Constants::COMPOSED_EFFECTS_TYPE; + + return basePath.resolvePath(effectsImportPath); +} + +Utils::FilePath GeneratedComponentUtils::composedEffectPath(const QString &effectPath) const +{ + Utils::FilePath effectsBasePath = composedEffectsBasePath(); + + QString effectName = Utils::FilePath::fromString(effectPath).baseName(); + + return effectsBasePath.resolvePath(effectName + "/" + effectName + ".qml"); +} + +Utils::FilePath GeneratedComponentUtils::componentBundlesBasePath() const +{ + Utils::FilePath basePath = generatedComponentsPath(); + + if (basePath.isEmpty()) + return {}; + + return basePath.resolvePath(QLatin1String(Constants::COMPONENT_BUNDLES_TYPE)); +} + +Utils::FilePath GeneratedComponentUtils::import3dBasePath() const +{ + Utils::FilePath basePath = generatedComponentsPath(); + + if (basePath.isEmpty()) + return {}; + + Utils::FilePath import3dPath; + if (basePath.endsWith(Constants::OLD_ASSET_IMPORT_FOLDER)) + return basePath.resolvePath(QLatin1String(Constants::OLD_QUICK_3D_ASSETS_FOLDER)); + + return basePath.resolvePath(QLatin1String(Constants::QUICK_3D_COMPONENTS_FOLDER)); +} + +bool GeneratedComponentUtils::isImport3dPath(const QString &path) const +{ + return path.contains('/' + QLatin1String(Constants::OLD_QUICK_3D_ASSETS_FOLDER)) + || path.contains('/' + QLatin1String(Constants::QUICK_3D_COMPONENTS_FOLDER)); +} + +bool GeneratedComponentUtils::isComposedEffectPath(const QString &path) const +{ + return path.contains(Constants::OLD_EFFECTS_IMPORT_FOLDER) + || path.contains('/' + QLatin1String(Constants::COMPOSED_EFFECTS_TYPE)); +} + +QString GeneratedComponentUtils::generatedComponentTypePrefix() const +{ + Utils::FilePath basePath = generatedComponentsPath(); + if (basePath.isEmpty() || basePath.endsWith(Constants::OLD_ASSET_IMPORT_FOLDER)) + return {}; + + return Constants::GENERATED_COMPONENTS_FOLDER; +} + +QString GeneratedComponentUtils::import3dTypePrefix() const +{ + QString basePrefix = generatedComponentTypePrefix(); + + if (basePrefix == Constants::GENERATED_COMPONENTS_FOLDER) + return basePrefix + '.' + QLatin1String(Constants::QUICK_3D_COMPONENTS_FOLDER); + + return Constants::OLD_QUICK_3D_ASSETS_FOLDER; +} + +QString GeneratedComponentUtils::import3dSimplifiedTypePrefix() const +{ + QString basePrefix = generatedComponentTypePrefix(); + + if (basePrefix.endsWith(Constants::QUICK_3D_COMPONENTS_FOLDER)) + return Constants::QUICK_3D_COMPONENTS_FOLDER; + + return Constants::OLD_QUICK_3D_ASSETS_FOLDER; +} + +QString GeneratedComponentUtils::componentBundlesTypePrefix() const +{ + QString basePrefix = generatedComponentTypePrefix(); + + if (basePrefix.endsWith(Constants::GENERATED_COMPONENTS_FOLDER)) + return basePrefix + '.' + QLatin1String(Constants::COMPONENT_BUNDLES_TYPE); + + return Constants::COMPONENT_BUNDLES_TYPE; +} + +QString GeneratedComponentUtils::composedEffectsTypePrefix() const +{ + QString basePrefix = generatedComponentTypePrefix(); + + if (basePrefix == Constants::GENERATED_COMPONENTS_FOLDER) + return basePrefix + '.' + QLatin1String(Constants::COMPOSED_EFFECTS_TYPE); + + return Constants::OLD_EFFECTS_FOLDER; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/generatedcomponentutils.h b/src/plugins/qmldesigner/designercore/generatedcomponentutils.h new file mode 100644 index 00000000000..f8eceaed4be --- /dev/null +++ b/src/plugins/qmldesigner/designercore/generatedcomponentutils.h @@ -0,0 +1,38 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include +#include + +#include + +#include + +namespace QmlDesigner { + +class QMLDESIGNERCORE_EXPORT GeneratedComponentUtils { +public: + GeneratedComponentUtils(ExternalDependenciesInterface &externalDependencies); + + Utils::FilePath generatedComponentsPath() const; + Utils::FilePath composedEffectsBasePath() const; + Utils::FilePath composedEffectPath(const QString &effectPath) const; + Utils::FilePath componentBundlesBasePath() const; + Utils::FilePath import3dBasePath() const; + + bool isImport3dPath(const QString &path) const; + bool isComposedEffectPath(const QString &path) const; + + QString generatedComponentTypePrefix() const; + QString import3dTypePrefix() const; + QString import3dSimplifiedTypePrefix() const; + QString componentBundlesTypePrefix() const; + QString composedEffectsTypePrefix() const; + +private: + ExternalDependenciesInterface &m_externalDependencies; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/include/subcomponentmanager.h b/src/plugins/qmldesigner/designercore/include/subcomponentmanager.h index 7fa23488543..a42164d1bdc 100644 --- a/src/plugins/qmldesigner/designercore/include/subcomponentmanager.h +++ b/src/plugins/qmldesigner/designercore/include/subcomponentmanager.h @@ -7,6 +7,7 @@ # include "qmldesignercorelib_global.h" +# include # include # include @@ -62,6 +63,7 @@ private: // variables QDir m_filePathDir; QPointer m_model; ExternalDependenciesInterface &m_externalDependencies; + GeneratedComponentUtils m_componentUtils; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp index 403731d1c43..feeb9173a05 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp @@ -44,6 +44,7 @@ SubComponentManager::SubComponentManager(Model *model, ExternalDependenciesInterface &externalDependencies) : m_model(model) , m_externalDependencies{externalDependencies} + , m_componentUtils{externalDependencies} { connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, [this](const QString &path) { parseDirectory(path); }); @@ -192,7 +193,7 @@ void SubComponentManager::parseDirectory(const QString &canonicalDirPath, bool a if (!model() || !model()->rewriterView()) return; - if (canonicalDirPath.endsWith(QLatin1String(Constants::QUICK_3D_ASSETS_FOLDER))) { + if (m_componentUtils.isImport3dPath(canonicalDirPath)) { parseQuick3DAssetsDir(canonicalDirPath); return; } @@ -345,8 +346,8 @@ void SubComponentManager::registerQmlFile(const QFileInfo &fileInfo, const QStri bool addToLibrary) { if (!addToLibrary || !model() - || fileInfo.path().contains(QLatin1String(Constants::QUICK_3D_ASSETS_FOLDER)) - || fileInfo.path().contains(QLatin1String(Constants::DEFAULT_EFFECTS_IMPORT_FOLDER))) { + || m_componentUtils.isImport3dPath(fileInfo.path()) + || m_componentUtils.isComposedEffectPath(fileInfo.path())) { return; } @@ -395,7 +396,7 @@ void SubComponentManager::parseQuick3DAssetsDir(const QString &quick3DAssetsPath QDir quick3DAssetsDir(quick3DAssetsPath); QStringList assets = quick3DAssetsDir.entryList(QDir::Dirs | QDir::NoDot | QDir::NoDotDot); for (QString &asset : assets) - asset.prepend(QString(Constants::QUICK_3D_ASSETS_FOLDER).mid(1) + '.'); + asset.prepend(m_componentUtils.import3dTypePrefix() + '.'); // Create item library entries for Quick3D assets that are imported by document for (auto &import : std::as_const(m_imports)) { @@ -460,7 +461,9 @@ QStringList SubComponentManager::quick3DAssetPaths() const const auto impPaths = importPaths(); QStringList retPaths; for (const auto &impPath : impPaths) { - const QString assetPath = impPath + QLatin1String(Constants::QUICK_3D_ASSETS_FOLDER); + QString path3d = m_componentUtils.import3dTypePrefix(); + path3d.replace('.', '/'); + const QString assetPath = impPath + '/' + path3d; if (QFileInfo::exists(assetPath)) retPaths << assetPath; } @@ -520,7 +523,7 @@ void SubComponentManager::update(const QUrl &filePath, const Imports &imports) // Remove old watched asset paths const QStringList watchPaths = m_watcher.directories(); - const QString &quick3DAssetFolder = QLatin1String(Constants::QUICK_3D_ASSETS_FOLDER); + const QString &quick3DAssetFolder = m_componentUtils.import3dSimplifiedTypePrefix(); for (const auto &watchPath : watchPaths) { if (watchPath.endsWith(quick3DAssetFolder)) m_watcher.removePath(watchPath); @@ -580,7 +583,7 @@ void SubComponentManager::addAndParseImport(const Import &import) } else { QString url = import.url(); - if (url.startsWith(QString(Constants::QUICK_3D_ASSETS_FOLDER).mid(1))) { + if (url.startsWith(m_componentUtils.import3dTypePrefix())) { parseQuick3DAssetsItem(import.url()); return; } diff --git a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp index c211a9e8fe0..6e3b739096f 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp @@ -9,8 +9,9 @@ #include "bindingproperty.h" #include "qmlanchors.h" -#include #include +#include +#include #include @@ -196,7 +197,9 @@ QmlItemNode QmlItemNode::createQmlItemNodeForEffect(AbstractView *view, auto createEffectNode = [=, &newQmlItemNode, &parentProperty]() { const QString effectName = QFileInfo(effectPath).baseName(); - Import import = Import::createLibraryImport("Effects." + effectName, "1.0"); + Import import = Import::createLibraryImport(GeneratedComponentUtils(view->externalDependencies()) + .composedEffectsTypePrefix() + + '.' + effectName, "1.0"); try { if (!view->model()->hasImport(import, true, true)) view->model()->changeImports({import}, {}); diff --git a/src/plugins/qmldesigner/documentmanager.cpp b/src/plugins/qmldesigner/documentmanager.cpp index fcaac762baf..75830d19b3d 100644 --- a/src/plugins/qmldesigner/documentmanager.cpp +++ b/src/plugins/qmldesigner/documentmanager.cpp @@ -266,6 +266,11 @@ void DocumentManager::resetPossibleImports() } } +const GeneratedComponentUtils &DocumentManager::generatedComponentUtils() const +{ + return m_generatedComponentUtils; +} + bool DocumentManager::goIntoComponent(const ModelNode &modelNode) { QImage image = QmlDesignerPlugin::instance()->viewManager().takeFormEditorScreenshot(); diff --git a/src/plugins/qmldesigner/documentmanager.h b/src/plugins/qmldesigner/documentmanager.h index 090630e5fe4..64476943391 100644 --- a/src/plugins/qmldesigner/documentmanager.h +++ b/src/plugins/qmldesigner/documentmanager.h @@ -5,6 +5,8 @@ #include "qmldesigner_global.h" +#include + #include #include #include @@ -31,6 +33,7 @@ public: ExternalDependenciesInterface &externalDependencies) : m_projectManager{projectManager} , m_externalDependencies{externalDependencies} + , m_generatedComponentUtils(externalDependencies) {} void setCurrentDesignDocument(Core::IEditor *editor); @@ -41,6 +44,8 @@ public: void resetPossibleImports(); + const GeneratedComponentUtils &generatedComponentUtils() const; + static bool goIntoComponent(const ModelNode &modelNode); static void goIntoComponent(const QString &fileName); @@ -64,6 +69,7 @@ private: QPointer m_currentDesignDocument; QmlDesignerProjectManager &m_projectManager; ExternalDependenciesInterface &m_externalDependencies; + GeneratedComponentUtils m_generatedComponentUtils; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index db63d894fec..e3772244a62 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -78,15 +78,19 @@ inline constexpr char EDIT3D_SNAP_CONFIG[] = "QmlDesigner.Editor3D.SnapConfig"; inline constexpr char EDIT3D_CAMERA_SPEED_CONFIG[] = "QmlDesigner.Editor3D.CameraSpeedConfig"; inline constexpr char QML_DESIGNER_SUBFOLDER[] = "/designer/"; -inline constexpr char COMPONENT_BUNDLES_FOLDER[] = "/ComponentBundles"; +inline constexpr char COMPONENT_BUNDLES_TYPE[] = "ComponentBundles"; +inline constexpr char GENERATED_COMPONENTS_FOLDER[] = "GeneratedComponents"; inline constexpr char COMPONENT_BUNDLES_ASSET_REF_FILE[] = "_asset_ref.json"; -inline constexpr char QUICK_3D_ASSETS_FOLDER[] = "/Quick3DAssets"; +inline constexpr char OLD_QUICK_3D_ASSETS_FOLDER[] = "Quick3DAssets"; +inline constexpr char QUICK_3D_COMPONENTS_FOLDER[] = "QtQuick3DComponents"; inline constexpr char QUICK_3D_ASSET_LIBRARY_ICON_SUFFIX[] = "_libicon"; inline constexpr char QUICK_3D_ASSET_IMPORT_DATA_NAME[] = "_importdata.json"; inline constexpr char QUICK_3D_ASSET_IMPORT_DATA_OPTIONS_KEY[] = "import_options"; inline constexpr char QUICK_3D_ASSET_IMPORT_DATA_SOURCE_KEY[] = "source_scene"; -inline constexpr char DEFAULT_ASSET_IMPORT_FOLDER[] = "/asset_imports"; -inline constexpr char DEFAULT_EFFECTS_IMPORT_FOLDER[] = "/asset_imports/Effects"; +inline constexpr char OLD_ASSET_IMPORT_FOLDER[] = "asset_imports"; +inline constexpr char OLD_EFFECTS_IMPORT_FOLDER[] = "/asset_imports/Effects"; +inline constexpr char OLD_EFFECTS_FOLDER[] = "Effects"; +inline constexpr char COMPOSED_EFFECTS_TYPE[] = "ComposedEffects"; inline constexpr char MATERIAL_LIB_ID[] = "__materialLibrary__"; inline constexpr char MIME_TYPE_ITEM_LIBRARY_INFO[] From 9791a71e0620c6563df208454e4a8537cbdee61f Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Wed, 10 Apr 2024 16:33:06 +0200 Subject: [PATCH 118/202] QmlDesigner: improve texttomodelmerger log output - use QtWarningMsg together with qCInfo(texttomodelMergerLog) to silence the output until QT_LOGGING_RULES=qtc.rewriter.load.*=true or QT_LOGGING_RULES=qtc.texttomodelmerger.*=true is set. Change-Id: Ia7d99bfc9d3e487c3c26065ef928ae830fa7e2ed Reviewed-by: Marco Bubke Reviewed-by: Qt CI Patch Build Bot --- .../qmldesigner/designercore/model/texttomodelmerger.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index 7637c006368..708578e671e 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -57,7 +57,7 @@ using namespace QmlJS; using namespace Qt::StringLiterals; static Q_LOGGING_CATEGORY(rewriterBenchmark, "qtc.rewriter.load", QtWarningMsg) -static Q_LOGGING_CATEGORY(texttomodelMergerDebug, "qtc.texttomodelmerger.debug", QtDebugMsg) +static Q_LOGGING_CATEGORY(texttomodelMergerLog, "qtc.texttomodelmerger", QtWarningMsg) namespace { @@ -502,8 +502,8 @@ public: const bool isAttached = !propertyName.isEmpty() && propertyName[0].isUpper(); // Only list elements might have unknown properties. if (!node.metaInfo().isQtQuickListElement() && !isAttached) { - qCInfo(texttomodelMergerDebug) - << Q_FUNC_INFO << "Unknown property" + qCInfo(texttomodelMergerLog) + << Q_FUNC_INFO << "\nUnknown property" << propertyPrefix + QLatin1Char('.') + toString(propertyId) << "on line" << propertyId->identifierToken.startLine << "column" << propertyId->identifierToken.startColumn; @@ -1423,7 +1423,7 @@ QmlDesigner::PropertyName TextToModelMerger::syncScriptBinding(ModelNode &modelN syncExpressionProperty(modelProperty, astValue, TypeName(), differenceHandler); // TODO: parse type return astPropertyName.toUtf8(); } else { - qWarning() << Q_FUNC_INFO << "Skipping invalid expression property" << astPropertyName + qCInfo(texttomodelMergerLog) << Q_FUNC_INFO << "\nSkipping invalid expression property" << astPropertyName << "for node type" << modelNode.type(); return PropertyName(); } From 476ea1404b0c8075c61a0335a7471ad0bb835a7b Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 4 Apr 2024 17:22:51 +0200 Subject: [PATCH 119/202] QmlDesigner: Add Design Effects UI * Add EffectsSection * Make HelperWidgtes.Section able to have custom content * Cleanup on IconButton and Section Change-Id: I9aff4838ed9f2df9155161f36506f1514245ce6b Reviewed-by: Thomas Hartmann --- .../QtQuick/EffectsSection.qml | 475 ++++++++++++++++++ .../QtQuick/ItemPane.qml | 4 + .../imports/HelperWidgets/IconButton.qml | 34 +- .../imports/HelperWidgets/Section.qml | 203 +++++--- 4 files changed, 615 insertions(+), 101 deletions(-) create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml new file mode 100644 index 00000000000..37fca814423 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml @@ -0,0 +1,475 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Layouts +import HelperWidgets +import StudioTheme as StudioTheme +import StudioControls as StudioControls + +Section { + id: root + + property bool hasDesignerEffect: false + property var model + property var effectNode + property var effectNodeWrapper + + // Draggging + property Item draggedSec: null + property var secsY: [] + property int moveFromIdx: 0 + property int moveToIdx: 0 + + function invalidate() { + root.effectNode = null + root.model = null + + var effect = modelNodeBackend.allChildrenOfType("DesignEffect") + root.effectNode = effect + root.effectNodeWrapper = modelNodeBackend.registerSubSelectionWrapper(effect) + root.hasDesignerEffect = effect.length === 1 + + if (!root.hasDesignerEffect) + return + + root.model = modelNodeBackend.allChildren(effect[0]) //ids for all effects + } + + leftPadding: 0 + anchors.left: parent.left + anchors.right: parent.right + caption: qsTr('Effects
[beta]').arg(StudioTheme.Values.themeInteraction) + visible: backendValues.layer_effect.isAvailable + + property Connections connection: Connections { + target: modelNodeBackend + + function onSelectionChanged() { root.invalidate() } + function onSelectionToBeChanged() { root.model = [] } + } + + SectionLayout { + PropertyLabel {} + + SecondColumnLayout { + Spacer { implicitWidth: StudioTheme.Values.actionIndicatorWidth } + + AbstractButton { + id: effectButton + implicitWidth: StudioTheme.Values.singleControlColumnWidth + width: StudioTheme.Values.singleControlColumnWidth + buttonIcon: root.hasDesignerEffect ? qsTr("Remove Effects") : qsTr("Add Effects") + iconFont: StudioTheme.Constants.font + tooltip: qsTr("Adds a note with a title to explain the component.") + onClicked: { + if (root.hasDesignerEffect) { + root.effectNodeWrapper.deleteModelNode() + } else { + modelNodeBackend.createModelNode(-1, "data", "DesignEffect") + var effectNode = modelNodeBackend.allChildrenOfType("DesignEffect") + modelNodeBackend.createModelNode(effectNode, "effects", "DesignDropShadow") + } + root.invalidate() + } + } + } + + PropertyLabel { text: qsTr("Visibility") } + + SecondColumnLayout { + CheckBox { + text: qsTr("Visible") + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + backendValue: root.effectNodeWrapper.properties.visible + } + + ExpandingSpacer {} + } + } + + Item { + width: 1 + height: StudioTheme.Values.sectionHeadSpacerHeight + } + + function handleDragMove() { + root.dragTimer.stop() + if (root.secsY.length === 0) { + for (let i = 0; i < repeater.count; ++i) + root.secsY[i] = repeater.itemAt(i).y + } + + let scrollView = Controller.mainScrollView + + let oldContentY = scrollView.contentY + if (root.draggedSec.y < scrollView.dragScrollMargin + scrollView.contentY + && scrollView.contentY > 0) { + scrollView.contentY -= scrollView.dragScrollMargin / 2 + } else if (root.draggedSec.y > scrollView.contentY + scrollView.height - scrollView.dragScrollMargin + && scrollView.contentY < scrollView.contentHeight - scrollView.height) { + scrollView.contentY += scrollView.dragScrollMargin / 2 + if (scrollView.contentY > scrollView.contentHeight - scrollView.height) + scrollView.contentY = scrollView.contentHeight - scrollView.height + } + + if (scrollView.contentY < 0) + scrollView.contentY = 0 + + if (oldContentY !== scrollView.contentY) { + // Changing dragged section position in drag handler doesn't seem to stick + // when triggered by mouse move, so do it again async + root.dragTimer.targetY = root.draggedSec.y - oldContentY + scrollView.contentY + root.dragTimer.restart() + root.dragConnection.enabled = false + root.draggedSec.y = root.dragTimer.targetY + root.dragConnection.enabled = true + } + + root.moveToIdx = root.moveFromIdx + for (let i = 0; i < repeater.count; ++i) { + let currItem = repeater.itemAt(i) + if (i > root.moveFromIdx) { + if (root.draggedSec.y > currItem.y) { + currItem.y = root.secsY[i] - root.draggedSec.height - nodesCol.spacing + root.moveToIdx = i + } else { + currItem.y = root.secsY[i] + } + } else if (i < root.moveFromIdx) { + if (root.draggedSec.y < currItem.y) { + currItem.y = root.secsY[i] + root.draggedSec.height + nodesCol.spacing + root.moveToIdx = Math.min(root.moveToIdx, i) + } else { + currItem.y = root.secsY[i] + } + } + } + } + + property Connections dragConnection: Connections { + target: root.draggedSec + + function onYChanged() { root.handleDragMove() } + } + + property Timer dragTimer: Timer { + running: false + interval: 16 + repeat: false + + property real targetY: -1 + + onTriggered: { + // Ensure we get position change triggers even if user holds mouse still to + // make scrolling smooth + root.draggedSec.y = targetY + root.handleDragMove() + } + } + + Column { + id: nodesCol + anchors.left: parent.left + anchors.right: parent.right + spacing: 1 + + Section { + sectionHeight: 37 + anchors.left: parent.left + anchors.right: parent.right + caption: qsTr("Layer Blur") + labelCapitalization: Font.MixedCase + visible: root.hasDesignerEffect + category: "DesignEffects" + expanded: false + + SectionLayout { + + PropertyLabel { text: qsTr("Visibility") } + + SecondColumnLayout { + CheckBox { + text: qsTr("Visible") + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + backendValue: root.effectNodeWrapper.properties.layerBlurVisible + } + + ExpandingSpacer {} + } + + PropertyLabel { text: qsTr("Blur") } + + SecondColumnLayout { + SpinBox { + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + backendValue: root.effectNodeWrapper.properties.layerBlurRadius + } + + ExpandingSpacer {} + } + } + } + + Section { + sectionHeight: 37 + anchors.left: parent.left + anchors.right: parent.right + caption: qsTr("Background Blur") + labelCapitalization: Font.MixedCase + visible: root.hasDesignerEffect + category: "DesignEffects" + expanded: false + + SectionLayout { + + PropertyLabel { text: qsTr("Visibility") } + + SecondColumnLayout { + CheckBox { + text: qsTr("Visible") + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + backendValue: root.effectNodeWrapper.properties.backgroundBlurVisible + } + + ExpandingSpacer {} + } + + PropertyLabel { text: qsTr("Blur") } + + SecondColumnLayout { + SpinBox { + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + backendValue: root.effectNodeWrapper.properties.backgroundBlurRadius + } + + ExpandingSpacer {} + } + + PropertyLabel { text: qsTr("Background") } + + SecondColumnLayout { + ItemFilterComboBox { + implicitWidth: StudioTheme.Values.singleControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + width: implicitWidth + typeFilter: "QtQuick.Item" + backendValue: root.effectNodeWrapper.properties.background + } + + ExpandingSpacer {} + } + } + } + + Repeater { + id: repeater + model: root.model + + Section { + id: delegate + + property QtObject wrapper: modelNodeBackend.registerSubSelectionWrapper(modelData) + property bool wasExpanded: false + + Behavior on y { + id: dragAnimation + + PropertyAnimation { + duration: 300 + easing.type: Easing.InOutQuad + } + } + + onStartDrag: function(section) { + root.draggedSec = section + root.moveFromIdx = index + // We only need to animate non-dragged sections + dragAnimation.enabled = false + delegate.wasExpanded = delegate.expanded + delegate.expanded = false + delegate.highlightBorder = true + root.secsY = [] + } + + onStopDrag: { + if (root.secsY.length !== 0) { + if (root.moveFromIdx === root.moveToIdx) + root.draggedSec.y = root.secsY[root.moveFromIdx] + else + modelNodeBackend.moveNode(root.effectNode, "effects", root.moveFromIdx, root.moveToIdx) + } + + delegate.highlightBorder = false + root.draggedSec = null + delegate.expanded = delegate.wasExpanded + dragAnimation.enabled = true + + Qt.callLater(root.invalidate) + } + + sectionHeight: 37 + anchors.left: parent.left + anchors.right: parent.right + category: "DesignEffects" + fillBackground: true + expanded: false + + draggable: true + showCloseButton: true + + content: StudioControls.ComboBox { + id: shadowComboBox + actionIndicatorVisible: false + width: 200 + textRole: "text" + valueRole: "value" + model: [ + { value: "DesignDropShadow", text: qsTr("Drop Shadow") }, + { value: "DesignInnerShadow", text: qsTr("Inner Shadow") } + ] + anchors.verticalCenter: parent.verticalCenter + + // When an item is selected, update the backend. + onActivated: modelNodeBackend.changeType(modelData, shadowComboBox.currentValue) + // Set the initial currentIndex to the value stored in the backend. + Component.onCompleted: { + shadowComboBox.currentIndex = shadowComboBox.indexOfValue(modelNodeBackend.simplifiedTypeName(modelData)) + } + } + + onCloseButtonClicked: { + delegate.wrapper.deleteModelNode() + Qt.callLater(root.invalidate) + } + + SectionLayout { + + PropertyLabel { text: qsTr("Visibility") } + + SecondColumnLayout { + CheckBox { + text: qsTr("Visible") + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + backendValue: delegate.wrapper.properties.visible + } + + ExpandingSpacer {} + } + + PropertyLabel { text: qsTr("Blur") } + + SecondColumnLayout { + SpinBox { + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + backendValue: delegate.wrapper.properties.blur + } + + ExpandingSpacer {} + } + + PropertyLabel { + text: qsTr("Spread") + enabled: modelNodeBackend.isInstanceOf("Rectangle") + } + + SecondColumnLayout { + SpinBox { + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + backendValue: delegate.wrapper.properties.spread + enabled: modelNodeBackend.isInstanceOf("Rectangle") + } + + ExpandingSpacer {} + } + + PropertyLabel { + text: qsTr("Color") + tooltip: qsTr("Sets the color.") + } + + ColorEditor { + backendValue: delegate.wrapper.properties.color + supportGradient: false + } + + PropertyLabel { text: qsTr("Offset") } + + SecondColumnLayout { + SpinBox { + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + backendValue: delegate.wrapper.properties.offsetX + maximumValue: 0xffff + minimumValue: -0xffff + } + + Spacer { implicitWidth: StudioTheme.Values.controlLabelGap } + + ControlLabel { + text: "X" + tooltip: qsTr("X-coordinate") + } + + Spacer { implicitWidth: StudioTheme.Values.controlGap } + + SpinBox { + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + backendValue: delegate.wrapper.properties.offsetY + maximumValue: 0xffff + minimumValue: -0xffff + } + + Spacer { implicitWidth: StudioTheme.Values.controlLabelGap } + + ControlLabel { + text: "Y" + tooltip: qsTr("Y-coordinate") + } + + ExpandingSpacer {} + } + } + } + } + } + + Item { + width: 1 + height: StudioTheme.Values.sectionHeadSpacerHeight + } + + SectionLayout { + visible: root.hasDesignerEffect + + PropertyLabel {} + + SecondColumnLayout { + Spacer { implicitWidth: StudioTheme.Values.actionIndicatorWidth } + + AbstractButton { + id: addShadowEffectButton + implicitWidth: StudioTheme.Values.singleControlColumnWidth + width: StudioTheme.Values.singleControlColumnWidth + buttonIcon: qsTr("Add Shadow Effect") + iconFont: StudioTheme.Constants.font + tooltip: qsTr("Adds a Design Drop Shadow.") + onClicked: { + modelNodeBackend.createModelNode(root.effectNode, + "effects", + "DesignDropShadow") + root.invalidate() + } + } + } + } +} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ItemPane.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ItemPane.qml index 60bd415a6af..aeef8a9598e 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ItemPane.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/ItemPane.qml @@ -133,6 +133,10 @@ PropertyEditorPane { visible: specificsOne.source.toString() !== "" } + EffectsSection { + expanded: false + } + AdvancedSection { expanded: false } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/IconButton.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/IconButton.qml index 008320cb92a..4534d3fe7da 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/IconButton.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/IconButton.qml @@ -8,9 +8,9 @@ import StudioTheme as StudioTheme Rectangle { id: root - signal clicked() - signal pressed() - signal released() + signal clicked(mouse: var) + signal pressed(mouse: var) + signal released(mouse: var) property alias icon: icon.text property alias tooltip: toolTip.text @@ -30,18 +30,17 @@ Rectangle { property color hoverColor: root.transparentBg ? "transparent" : StudioTheme.Values.themeControlBackgroundHover property color pressColor: root.transparentBg ? "transparent" : StudioTheme.Values.themeControlBackgroundInteraction - width: buttonSize - height: buttonSize + width: root.buttonSize + height: root.buttonSize - color: !enabled ? normalColor - : mouseArea.pressed ? pressColor - : mouseArea.containsMouse ? hoverColor - : normalColor + color: !root.enabled ? root.normalColor + : mouseArea.pressed ? root.pressColor + : mouseArea.containsMouse ? root.hoverColor + : root.normalColor Text { id: icon anchors.centerIn: root - color: root.enabled ? StudioTheme.Values.themeTextColor : StudioTheme.Values.themeTextColorDisabled font.family: StudioTheme.Constants.iconFont.family font.pixelSize: StudioTheme.Values.baseIconFontSize @@ -49,30 +48,29 @@ Rectangle { MouseArea { id: mouseArea - anchors.fill: parent hoverEnabled: root.visible - onClicked: { + onClicked: function(mouse) { // We need to keep mouse area enabled even when button is disabled to make tooltip work if (root.enabled) - root.clicked() + root.clicked(mouse) } - onPressed: { + onPressed: function(mouse) { if (root.enabled) - root.pressed() + root.pressed(mouse) } - onReleased: { + onReleased: function(mouse) { if (root.enabled) - root.released() + root.released(mouse) } } ToolTip { id: toolTip - visible: mouseArea.containsMouse && text !== "" + visible: mouseArea.containsMouse && toolTip.text !== "" delay: 1000 } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml index dbbb200f738..245b8506a28 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml @@ -9,11 +9,13 @@ import StudioTheme as StudioTheme Item { id: section - property alias caption: label.text - property alias labelColor: label.color + + property string caption: "Title" + property color labelColor: StudioTheme.Values.themeTextColor + property int labelCapitalization: Font.AllUppercase property alias sectionHeight: header.height property alias sectionBackgroundColor: header.color - property alias sectionFontSize: label.font.pixelSize + property int sectionFontSize: StudioTheme.Values.myFontSize property alias showTopSeparator: topSeparator.visible property alias showArrow: arrow.visible property alias showLeftBorder: leftBorder.visible @@ -26,6 +28,17 @@ Item { property alias fillBackground: sectionBackground.visible property alias highlightBorder: sectionBorder.visible + property Item content: Controls.Label { + id: label + text: section.caption + color: section.labelColor + elide: Text.ElideRight + font.pixelSize: section.sectionFontSize + font.capitalization: section.labelCapitalization + anchors.verticalCenter: parent?.verticalCenter + textFormat: Text.RichText + } + property int leftPadding: StudioTheme.Values.sectionLeftPadding property int rightPadding: 0 property int topPadding: StudioTheme.Values.sectionHeadSpacerHeight @@ -59,7 +72,7 @@ Item { Connections { target: Controller function onCollapseAll(cat) { - if (collapsible && cat === section.category) { + if (section.collapsible && cat === section.category) { if (section.expandOnClick) section.expanded = false else @@ -106,6 +119,22 @@ Item { onExited: section.dropExit() } + StudioControls.Menu { + id: contextMenu + + StudioControls.MenuItem { + text: qsTr("Expand All") + onTriggered: Controller.expandAll(section.category) + } + + StudioControls.MenuItem { + text: qsTr("Collapse All") + onTriggered: Controller.collapseAll(section.category) + } + + onOpenedChanged: Controller.contextMenuOpened = contextMenu.opened + } + Rectangle { id: header height: section.hideHeader ? 0 : StudioTheme.Values.sectionHeadHeight @@ -116,43 +145,6 @@ Item { : Qt.lighter(StudioTheme.Values.themeSectionHeadBackground, 1.0 + (0.2 * section.level)) - Item { - StudioControls.Menu { - id: contextMenu - - StudioControls.MenuItem { - text: qsTr("Expand All") - onTriggered: Controller.expandAll(section.category) - } - - StudioControls.MenuItem { - text: qsTr("Collapse All") - onTriggered: Controller.collapseAll(section.category) - } - - onOpenedChanged: Controller.contextMenuOpened = contextMenu.opened - } - } - - Image { - id: arrow - width: 8 - height: 4 - source: "image://icons/down-arrow" - anchors.left: parent.left - anchors.leftMargin: 4 + (section.level * section.levelShift) + (section.draggable ? 20 : 0) + (section.showEyeButton ? 25 : 0) - anchors.verticalCenter: parent.verticalCenter - } - - Controls.Label { - id: label - anchors.verticalCenter: parent.verticalCenter - color: StudioTheme.Values.themeTextColor - x: arrow.x + 18 - font.pixelSize: StudioTheme.Values.myFontSize - font.capitalization: Font.AllUppercase - } - MouseArea { id: mouseArea anchors.fill: parent @@ -173,58 +165,102 @@ Item { } } - IconButton { - id: closeButton + RowLayout { + spacing: 1 + anchors.fill: parent - icon: StudioTheme.Constants.closeCross - buttonSize: 22 - iconScale: containsMouse ? 1.2 : 1 - transparentBg: true - anchors.right: parent.right - anchors.rightMargin: 10 - visible: false + IconButton { + id: dragButton + visible: false + icon: StudioTheme.Constants.dragmarks + buttonSize: 21 + iconScale: dragButton.enabled && dragButton.containsMouse ? 1.2 : 1 + transparentBg: true - onClicked: root.closeButtonClicked() - } + Layout.alignment: Qt.AlignVCenter + Layout.preferredWidth: dragButton.width + Layout.maximumWidth: dragButton.width - IconButton { - id: dragButton + drag.target: dragButton.enabled ? section : null + drag.axis: Drag.YAxis - icon: StudioTheme.Constants.dragmarks - buttonSize: 22 - iconScale: dragButton.enabled && dragButton.containsMouse ? 1.2 : 1 - transparentBg: true + onPressed: { + section.startDrag(section) + section.z = ++section.parent.z // put the dragged section on top + } - visible: false - drag.target: dragButton.enabled ? section : null - drag.axis: Drag.YAxis - - onPressed: { - section.startDrag(section) - - section.z = ++section.parent.z // put the dragged section on top + onReleased: { + section.stopDrag() + } } - onReleased: { - section.stopDrag() + IconButton { + id: eyeButton + + visible: false + icon: section.eyeEnabled ? StudioTheme.Constants.visible_small + : StudioTheme.Constants.invisible_small + buttonSize: 21 + iconScale: eyeButton.containsMouse ? 1.2 : 1 + transparentBg: true + + Layout.alignment: Qt.AlignVCenter + Layout.preferredWidth: eyeButton.width + Layout.maximumWidth: eyeButton.width + + onClicked: { + section.eyeEnabled = !section.eyeEnabled + section.eyeButtonClicked() + } } - } - IconButton { - id: eyeButton + IconButton { + id: arrow + icon: StudioTheme.Constants.sectionToggle + transparentBg: true - anchors.left: dragButton.right + buttonSize: 21 + iconSize: StudioTheme.Values.smallIconFontSize + iconColor: StudioTheme.Values.themeTextColor - icon: section.eyeEnabled ? StudioTheme.Constants.visible_small : StudioTheme.Constants.invisible_small - buttonSize: 22 - iconScale: eyeButton.containsMouse ? 1.2 : 1 - transparentBg: true + Layout.alignment: Qt.AlignVCenter + Layout.preferredWidth: arrow.width + Layout.maximumWidth: arrow.width - visible: false + onClicked: function(mouse) { + if (!section.collapsible && section.expanded) + return - onClicked: { - section.eyeEnabled = !section.eyeEnabled - root.eyeButtonClicked() + transition.enabled = true + if (section.expandOnClick) + section.expanded = !section.expanded + else + section.toggleExpand() + } + } + + Item { + id: headerContent + height: header.height + Layout.fillWidth: true + children: [ section.content ] + } + + IconButton { + id: closeButton + + visible: false + icon: StudioTheme.Constants.closeCross + buttonSize: 21 + iconScale: closeButton.containsMouse ? 1.2 : 1 + transparentBg: true + + Layout.alignment: Qt.AlignVCenter + Layout.preferredWidth: closeButton.width + Layout.maximumWidth: closeButton.width + Layout.rightMargin: 10 + + onClicked: section.closeButtonClicked() } } } @@ -266,6 +302,7 @@ Item { border.width: 1 visible: false } + Item { id: topSpacer height: section.addTopPadding && column.height > 0 ? section.topPadding : 0 @@ -285,7 +322,7 @@ Item { id: leftBorder visible: false width: 1 - height: parent.height - bottomPadding + height: parent.height - section.bottomPadding color: header.color } @@ -320,8 +357,8 @@ Item { } onRunningChanged: { - if (!running) - enabled = false + if (!transition.running) + transition.enabled = false } } } From df123a00800deca05e0e16d5ad83bf3703c430dd Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Thu, 11 Apr 2024 17:46:32 +0200 Subject: [PATCH 120/202] Fix syntax and remove warning Change-Id: Ifea0ebae64365792eefe47995d0ae6a1192590d1 Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../qmldesigner/effectComposerQmlSources/EffectComposer.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposer.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposer.qml index dda1d93ff96..305bbc7925c 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposer.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposer.qml @@ -365,7 +365,7 @@ ColumnLayout { Connections { id: dragConnection target: root.draggedSec - onYChanged: root.handleDragMove() + function onYChanged() { root.handleDragMove() } } Timer { From 74d56d54f588ce8a44e42d51586022d816a08262 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Mon, 8 Apr 2024 18:35:00 +0200 Subject: [PATCH 121/202] QmlPreviewer: fix activate setDirty Task-number: QDS-12177 Change-Id: I9518071d188cf959811dd1942721c81e853dc94b Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- src/plugins/qmlpreview/qmlpreviewplugin.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/plugins/qmlpreview/qmlpreviewplugin.cpp b/src/plugins/qmlpreview/qmlpreviewplugin.cpp index 31638f76622..70f905448e1 100644 --- a/src/plugins/qmlpreview/qmlpreviewplugin.cpp +++ b/src/plugins/qmlpreview/qmlpreviewplugin.cpp @@ -166,12 +166,8 @@ QmlPreviewPluginPrivate::QmlPreviewPluginPrivate(QmlPreviewPlugin *parent) runPreviewAction->setEnabled(ProjectManager::startupProject() != nullptr); connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged, runPreviewAction, &QAction::setEnabled); - connect(runPreviewAction, &QAction::triggered, this, [runPreviewAction, this] { + connect(runPreviewAction, &QAction::triggered, this, [&, runPreviewAction] { runPreviewAction->setEnabled(false); - attachToEditorManager(); - setDirty(); - onEditorChanged(Core::EditorManager::currentEditor()); - if (auto multiLanguageAspect = QmlProjectManager::QmlMultiLanguageAspect::current()) m_localeIsoCode = multiLanguageAspect->currentLocale(); bool skipDeploy = false; @@ -425,7 +421,7 @@ void QmlPreviewPluginPrivate::onEditorAboutToClose(Core::IEditor *editor) void QmlPreviewPluginPrivate::setDirty() { m_dirty = true; - QTimer::singleShot(1000, this, [this](){ + QTimer::singleShot(1000, this, [&](){ if (m_dirty && m_lastEditor) { m_dirty = false; checkEditor(); @@ -435,6 +431,10 @@ void QmlPreviewPluginPrivate::setDirty() void QmlPreviewPlugin::addPreview(RunControl *preview) { + d->attachToEditorManager(); + d->setDirty(); + d->onEditorChanged(Core::EditorManager::currentEditor()); + d->m_runningPreviews.append(preview); emit runningPreviewsChanged(d->m_runningPreviews); } From 00bbf37cff70f9e3f2397c612f6d9cba234452e5 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 11 Apr 2024 17:14:27 +0300 Subject: [PATCH 122/202] QmlDesigner: Fix composed drop shadow effect resize Source width/height changes were not listened for. Added connections for those. Note that Connections element was not used as it doesn't seem to work in puppet. Fixes: QDS-12395 Change-Id: I30abdfa3e7ea08b3f126fbafeab6cb522040f16b Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri Reviewed-by: --- .../effectcomposer/effectcomposermodel.cpp | 60 +++++++++++++------ 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/src/plugins/effectcomposer/effectcomposermodel.cpp b/src/plugins/effectcomposer/effectcomposermodel.cpp index f62af067c86..f08053518fa 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.cpp +++ b/src/plugins/effectcomposer/effectcomposermodel.cpp @@ -805,10 +805,22 @@ R"( onExtraMarginChanged: setupSourceRect() function setupSourceRect() { - if (rootItem.source) { - var width = source.width + extraMargin * 2 - var height = source.height + extraMargin * 2 - source.layer.sourceRect = Qt.rect(-extraMargin, -extraMargin, width, height) + if (source) { + var w = source.width + extraMargin * 2 + var h = source.height + extraMargin * 2 + source.layer.sourceRect = Qt.rect(-extraMargin, -extraMargin, w, h) + } + } + + function connectSource(enable) { + if (source) { + if (enable) { + source.widthChanged.connect(setupSourceRect) + source.heightChanged.connect(setupSourceRect) + } else { + source.widthChanged.disconnect(setupSourceRect) + source.heightChanged.disconnect(setupSourceRect) + } } } )" @@ -837,7 +849,8 @@ R"( if (_oldParent && _oldParent !== parent) { _oldParent.layer.enabled = false _oldParent.layer.effect = null - %2 + %7 + %4%2 _oldParent.update() _oldParent = null } @@ -847,8 +860,8 @@ R"( parent.layer.enabled = true parent.layer.effect = effectComponent } - %1 - %3 + %6 + %4%1%5%3 } } @@ -856,36 +869,47 @@ R"( if (visible) { parent.layer.enabled = true parent.layer.effect = effectComponent - source = parent - %3 + %6 + %4%1%5%3 } else { parent.layer.enabled = false parent.layer.effect = null - source = null + %8 + %4%2 } parent.update() } + )" }; + QString mipmap1; + QString mipmap2; + QString mipmap3; if (m_shaderFeatures.enabled(ShaderFeatures::Mipmap)) { - QString mipmap1{ + mipmap1 = QString { R"(parent.layer.smooth = true - parent.layer.mipmap = true - %1)" + parent.layer.mipmap = true)" }; - QString mipmap2{ + mipmap2 = QString { R"(_oldParent.layer.smooth = false - _oldParent.layer.mipmap = false - %2)" + _oldParent.layer.mipmap = false)" + }; + mipmap3 = QString { + R"(parent.layer.smooth = false + parent.layer.mipmap = false)" }; - parentChanged = parentChanged.arg(mipmap1, mipmap2); } if (m_shaderFeatures.enabled(ShaderFeatures::Source)) { parentChanged = parentChanged.arg(QString("source = parent"), QString("source = null"), - m_extraMargin ? QString("setupSourceRect()") : QString()); + m_extraMargin ? QString(" setupSourceRect()") : QString(), + m_extraMargin ? QString("connectSource(false)\n ") : QString(), + m_extraMargin ? QString("\n connectSource(true)\n") : QString(), + mipmap1, + mipmap2, + mipmap3); } else { parentChanged = parentChanged.arg(QString(), QString(), QString()); } From 247d2dbf6d27800b3b95a1ae366ec742ebac045c Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Thu, 11 Apr 2024 18:27:02 +0300 Subject: [PATCH 123/202] QmlDesigner: Cleanups in the content library Change-Id: I4bbb6f6d89c3e35a265624365eb61664280e9151 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Miikka Heikkinen Reviewed-by: --- .../ContentLibraryTexture.qml | 6 +- .../contentlibrarymaterialsmodel.cpp | 8 +- .../contentlibrary/contentlibrarytexture.cpp | 47 +++---- .../contentlibrary/contentlibrarytexture.h | 24 ++-- .../contentlibrarytexturescategory.cpp | 10 +- .../contentlibrarytexturescategory.h | 2 +- .../contentlibrarytexturesmodel.cpp | 32 ++--- .../contentlibrarytexturesmodel.h | 4 +- .../contentlibrary/contentlibrarywidget.cpp | 132 +++++++----------- .../contentlibrary/contentlibrarywidget.h | 15 +- 10 files changed, 123 insertions(+), 157 deletions(-) diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexture.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexture.qml index f74f1900405..4fbeb1b8b54 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexture.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTexture.qml @@ -148,7 +148,7 @@ Item { visible: root.delegateVisible && root.downloadState != "downloading" cache: false - property string webUrl: modelData.textureWebUrl + property string textureUrl: modelData.textureUrl IconButton { id: downloadIcon @@ -279,7 +279,7 @@ Item { FileDownloader { id: textureDownloader - url: image.webUrl + url: image.textureUrl probeUrl: false downloadEnabled: true onDownloadStarting: { @@ -333,7 +333,7 @@ Item { FileDownloader { id: iconDownloader - url: modelData.textureWebIconUrl + url: modelData.textureIconUrl probeUrl: false downloadEnabled: true targetFilePath: modelData.textureIconPath diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp index 53624bfeaa8..26747d359cd 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp @@ -275,9 +275,9 @@ void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &matBundleDir) auto category = new ContentLibraryMaterialsCategory(this, cat); const QJsonObject matsObj = catsObj.value(cat).toObject(); - const QStringList mats = matsObj.keys(); - for (const QString &mat : mats) { - const QJsonObject matObj = matsObj.value(mat).toObject(); + const QStringList matsNames = matsObj.keys(); + for (const QString &matName : matsNames) { + const QJsonObject matObj = matsObj.value(matName).toObject(); QStringList files; const QJsonArray assetsArr = matObj.value("files").toArray(); @@ -292,7 +292,7 @@ void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &matBundleDir) bundleId, qml.chopped(4)).toLatin1(); // chopped(4): remove .qml - auto bundleMat = new ContentLibraryMaterial(category, mat, qml, type, icon, files, + auto bundleMat = new ContentLibraryMaterial(category, matName, qml, type, icon, files, m_downloadPath, m_baseUrl); category->addBundleMaterial(bundleMat); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp index 7ab239aab4e..c2fec6e0f73 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp @@ -12,20 +12,19 @@ namespace QmlDesigner { ContentLibraryTexture::ContentLibraryTexture(QObject *parent, const QFileInfo &iconFileInfo, - const QString &downloadPath, const QUrl &icon, - const QString &key, const QString &webTextureUrl, - const QString &webIconUrl, const QString &fileExt, - const QSize &dimensions, const qint64 sizeInBytes, - bool hasUpdate, bool isNew) + const QString &dirPath, const QString &key, + const QString &textureUrl, const QString &iconUrl, + const QString &suffix, const QSize &dimensions, + const qint64 sizeInBytes, bool hasUpdate, bool isNew) : QObject(parent) , m_iconPath(iconFileInfo.filePath()) - , m_downloadPath(downloadPath) - , m_webTextureUrl(webTextureUrl) - , m_webIconUrl(webIconUrl) + , m_dirPath(dirPath) + , m_textureUrl(textureUrl) + , m_iconUrl(iconUrl) , m_baseName{iconFileInfo.baseName()} - , m_fileExt(fileExt) + , m_suffix(suffix) , m_textureKey(key) - , m_icon(icon) + , m_icon(QUrl::fromLocalFile(iconFileInfo.absoluteFilePath())) , m_dimensions(dimensions) , m_sizeInBytes(sizeInBytes) , m_hasUpdate(hasUpdate) @@ -54,9 +53,9 @@ QString ContentLibraryTexture::iconPath() const return m_iconPath; } -QString ContentLibraryTexture::resolveFileExt() +QString ContentLibraryTexture::resolveSuffix() { - const QFileInfoList files = QDir(m_downloadPath).entryInfoList(QDir::Files); + const QFileInfoList files = QDir(m_dirPath).entryInfoList(QDir::Files); const QFileInfoList textureFiles = Utils::filtered(files, [this](const QFileInfo &fi) { return fi.baseName() == m_baseName; }); @@ -76,22 +75,20 @@ QString ContentLibraryTexture::resolveFileExt() QString ContentLibraryTexture::resolveToolTipText() { - if (m_fileExt.isEmpty()) { - // No supplied or resolved extension means we have just the icon and no other data - return m_baseName; - } + if (m_suffix.isEmpty()) + return m_baseName; // empty suffix means we have just the icon and no other data - QString fileName = m_baseName + m_fileExt; + QString fileName = m_baseName + m_suffix; QString imageInfo; if (!m_isDownloaded && m_sizeInBytes > 0 && !m_dimensions.isNull()) { imageInfo = ImageUtils::imageInfo(m_dimensions, m_sizeInBytes); } else { - QString fullDownloadPath = m_downloadPath + '/' + fileName; + QString fullDownloadPath = m_dirPath + '/' + fileName; imageInfo = ImageUtils::imageInfo(fullDownloadPath); } - return QStringLiteral("%1\n%2").arg(fileName, imageInfo); + return QString("%1\n%2").arg(fileName, imageInfo); } bool ContentLibraryTexture::isDownloaded() const @@ -99,9 +96,9 @@ bool ContentLibraryTexture::isDownloaded() const return m_isDownloaded; } -QString ContentLibraryTexture::downloadedTexturePath() const +QString ContentLibraryTexture::texturePath() const { - return m_downloadPath + '/' + m_baseName + m_fileExt; + return m_dirPath + '/' + m_baseName + m_suffix; } void ContentLibraryTexture::setDownloaded() @@ -116,16 +113,16 @@ void ContentLibraryTexture::setDownloaded() void ContentLibraryTexture::doSetDownloaded() { - if (m_fileExt.isEmpty()) - m_fileExt = resolveFileExt(); + if (m_suffix.isEmpty()) + m_suffix = resolveSuffix(); - m_isDownloaded = QFileInfo::exists(downloadedTexturePath()); + m_isDownloaded = QFileInfo::exists(texturePath()); m_toolTip = resolveToolTipText(); } QString ContentLibraryTexture::parentDirPath() const { - return m_downloadPath; + return m_dirPath; } QString ContentLibraryTexture::textureKey() const diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h index 9f5b46630f6..48f2314e9c4 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h @@ -19,17 +19,17 @@ class ContentLibraryTexture : public QObject Q_PROPERTY(QString textureToolTip MEMBER m_toolTip NOTIFY textureToolTipChanged) Q_PROPERTY(QUrl textureIcon MEMBER m_icon CONSTANT) Q_PROPERTY(bool textureVisible MEMBER m_visible NOTIFY textureVisibleChanged) - Q_PROPERTY(QString textureWebUrl MEMBER m_webTextureUrl CONSTANT) - Q_PROPERTY(QString textureWebIconUrl MEMBER m_webIconUrl CONSTANT) + Q_PROPERTY(QString textureUrl MEMBER m_textureUrl CONSTANT) + Q_PROPERTY(QString textureIconUrl MEMBER m_iconUrl CONSTANT) Q_PROPERTY(bool textureHasUpdate WRITE setHasUpdate READ hasUpdate NOTIFY hasUpdateChanged) Q_PROPERTY(bool textureIsNew MEMBER m_isNew CONSTANT) Q_PROPERTY(QString textureKey MEMBER m_textureKey CONSTANT) public: - ContentLibraryTexture(QObject *parent, const QFileInfo &iconFileInfo, const QString &downloadPath, - const QUrl &icon, const QString &key, const QString &webTextureUrl, - const QString &webIconUrl, const QString &fileExt, const QSize &dimensions, - const qint64 sizeInBytes, bool hasUpdate, bool isNew); + ContentLibraryTexture(QObject *parent, const QFileInfo &iconFileInfo, const QString &dirPath, + const QString &key, const QString &textureUrl, const QString &iconUrl, + const QString &suffix, const QSize &dimensions, const qint64 sizeInBytes, + bool hasUpdate = false, bool isNew = false); Q_INVOKABLE bool isDownloaded() const; Q_INVOKABLE void setDownloaded(); @@ -38,7 +38,7 @@ public: QUrl icon() const; QString iconPath() const; - QString downloadedTexturePath() const; + QString texturePath() const; QString parentDirPath() const; QString textureKey() const; @@ -51,17 +51,17 @@ signals: void hasUpdateChanged(); private: - QString resolveFileExt(); + QString resolveSuffix(); QString resolveToolTipText(); void doSetDownloaded(); QString m_iconPath; - QString m_downloadPath; - QString m_webTextureUrl; - QString m_webIconUrl; + QString m_dirPath; + QString m_textureUrl; + QString m_iconUrl; QString m_toolTip; QString m_baseName; - QString m_fileExt; + QString m_suffix; QString m_textureKey; QUrl m_icon; QSize m_dimensions; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.cpp index 77519ad88f6..7fb92c66ac5 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.cpp @@ -14,17 +14,15 @@ namespace QmlDesigner { ContentLibraryTexturesCategory::ContentLibraryTexturesCategory(QObject *parent, const QString &name) : QObject(parent), m_name(name) {} -void ContentLibraryTexturesCategory::addTexture(const QFileInfo &tex, const QString &downloadPath, +void ContentLibraryTexturesCategory::addTexture(const QFileInfo &texIcon, const QString &downloadPath, const QString &key, const QString &webTextureUrl, - const QString &webIconUrl, const QString &fileExt, + const QString &iconUrl, const QString &suffix, const QSize &dimensions, const qint64 sizeInBytes, bool hasUpdate, bool isNew) { - QUrl icon = QUrl::fromLocalFile(tex.absoluteFilePath()); - m_categoryTextures.append(new ContentLibraryTexture( - this, tex, downloadPath, icon, key, webTextureUrl, webIconUrl, - fileExt, dimensions, sizeInBytes, hasUpdate, isNew)); + this, texIcon, downloadPath, key, webTextureUrl, iconUrl, + suffix, dimensions, sizeInBytes, hasUpdate, isNew)); } bool ContentLibraryTexturesCategory::filter(const QString &searchText) diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.h index 166528f05a8..857346df06a 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.h @@ -28,7 +28,7 @@ public: ContentLibraryTexturesCategory(QObject *parent, const QString &name); void addTexture(const QFileInfo &tex, const QString &subPath, const QString &key, - const QString &webTextureUrl, const QString &webIconUrl, const QString &fileExt, + const QString &webTextureUrl, const QString &iconUrl, const QString &suffix, const QSize &dimensions, const qint64 sizeInBytes, bool hasUpdate, bool isNew); bool filter(const QString &searchText); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp index 319ca2686f2..b575b6b9b2b 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp @@ -95,37 +95,37 @@ QHash ContentLibraryTexturesModel::roleNames() const /** * @brief Load the bundle categorized icons. Actual textures are downloaded on demand * - * @param bundlePath local path to the bundle folder and icons - * @param metaData bundle textures metadata + * @param textureBundleUrl remote url to the texture bundle + * @param bundleIconPath local path to the texture bundle icons folder + * @param jsonData bundle textures information from the bundle json */ -void ContentLibraryTexturesModel::loadTextureBundle(const QString &remoteUrl, const QString &iconsUrl, +void ContentLibraryTexturesModel::loadTextureBundle(const QString &textureBundleUrl, const QString &bundleIconPath, - const QVariantMap &metaData) + const QVariantMap &jsonData) { if (!m_bundleCategories.isEmpty()) return; QDir bundleDir = QString("%1/%2").arg(bundleIconPath, m_category); - if (!bundleDir.exists()) { - qWarning() << __FUNCTION__ << "textures bundle folder doesn't exist." << bundleDir.absolutePath(); - return; - } + QTC_ASSERT(bundleDir.exists(), return); - const QVariantMap imageItems = metaData.value("image_items").toMap(); + const QVariantMap imageItems = jsonData.value("image_items").toMap(); const QFileInfoList dirs = bundleDir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); for (const QFileInfo &dir : dirs) { auto category = new ContentLibraryTexturesCategory(this, dir.fileName()); - const QFileInfoList texFiles = QDir(dir.filePath()).entryInfoList(QDir::Files); - for (const QFileInfo &tex : texFiles) { - QString textureUrl = QString("%1/%2/%3.zip").arg(remoteUrl, dir.fileName(), tex.baseName()); - QString iconUrl = QString("%1/%2/%3.png").arg(iconsUrl, dir.fileName(), tex.baseName()); + const QFileInfoList texIconFiles = QDir(dir.filePath()).entryInfoList(QDir::Files); + for (const QFileInfo &texIcon : texIconFiles) { + QString textureUrl = QString("%1/%2/%3/%4.zip").arg(textureBundleUrl, m_category, + dir.fileName(), texIcon.baseName()); + QString iconUrl = QString("%1/icons/%2/%3/%4.png").arg(textureBundleUrl, m_category, + dir.fileName(), texIcon.baseName()); - QString localDownloadPath = QString("%1/%2/%3") + QString texturePath = 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 key = QString("%1/%2/%3").arg(m_category, dir.fileName(), texIcon.baseName()); QString fileExt; QSize dimensions; qint64 sizeInBytes = -1; @@ -141,7 +141,7 @@ void ContentLibraryTexturesModel::loadTextureBundle(const QString &remoteUrl, co isNew = m_newFiles.contains(key); } - category->addTexture(tex, localDownloadPath, key, textureUrl, iconUrl, fileExt, + category->addTexture(texIcon, texturePath, key, textureUrl, iconUrl, fileExt, dimensions, sizeInBytes, hasUpdate, isNew); } m_bundleCategories.append(category); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.h index 92db4151a84..b5237b2868b 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.h @@ -37,8 +37,8 @@ public: void setHasSceneEnv(bool b); void resetModel(); - void loadTextureBundle(const QString &remoteUrl, const QString &iconsUrl, - const QString &bundlePath, const QVariantMap &metaData); + void loadTextureBundle(const QString &m_textureBundleUrl, const QString &bundlePath, + const QVariantMap &metaData); signals: void isEmptyChanged(); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp index 8a2e81cfb21..9375d43fd4f 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp @@ -101,10 +101,10 @@ bool ContentLibraryWidget::eventFilter(QObject *obj, QEvent *event) && m_textureToDrag->isDownloaded()) { QMimeData *mimeData = new QMimeData; mimeData->setData(Constants::MIME_TYPE_BUNDLE_TEXTURE, - {m_textureToDrag->downloadedTexturePath().toUtf8()}); + {m_textureToDrag->texturePath().toUtf8()}); // Allows standard file drag-n-drop. As of now needed to drop on Assets view - mimeData->setUrls({QUrl::fromLocalFile(m_textureToDrag->downloadedTexturePath())}); + mimeData->setUrls({QUrl::fromLocalFile(m_textureToDrag->texturePath())}); emit bundleTextureDragStarted(m_textureToDrag); model->startDrag(mimeData, m_textureToDrag->icon().toLocalFile()); @@ -142,18 +142,12 @@ ContentLibraryWidget::ContentLibraryWidget() m_quickWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); m_quickWidget->setClearColor(Theme::getColor(Theme::Color::DSpanelBackground)); - m_baseUrl = QmlDesignerPlugin::settings() - .value(DesignerSettingsKey::DOWNLOADABLE_BUNDLES_URL).toString() - + "/textures"; + m_textureBundleUrl = QmlDesignerPlugin::settings() + .value(DesignerSettingsKey::DOWNLOADABLE_BUNDLES_URL).toString() + "/textures"; - m_texturesUrl = m_baseUrl + "/Textures"; - m_textureIconsUrl = m_baseUrl + "/icons/Textures"; - m_environmentIconsUrl = m_baseUrl + "/icons/Environments"; - m_environmentsUrl = m_baseUrl + "/Environments"; + m_bundlePath = Paths::bundlesPathSetting(); - m_downloadPath = Paths::bundlesPathSetting(); - - loadTextureBundle(); + loadTextureBundles(); Theme::setupTheme(m_quickWidget->engine()); m_quickWidget->quickWidget()->installEventFilter(this); @@ -185,33 +179,28 @@ ContentLibraryWidget::ContentLibraryWidget() reloadQmlSource(); } -QVariantMap ContentLibraryWidget::readBundleMetadata() +QVariantMap ContentLibraryWidget::readTextureBundleJson() { - QVariantMap metaData; - QFile jsonFile(m_downloadPath + "/texture_bundle.json"); + QVariantMap jsonData; + QFile jsonFile(m_bundlePath + "/texture_bundle.json"); if (jsonFile.open(QIODevice::ReadOnly | QIODevice::Text)) - metaData = QJsonDocument::fromJson(jsonFile.readAll()).toVariant().toMap(); + jsonData = QJsonDocument::fromJson(jsonFile.readAll()).toVariant().toMap(); - int version = metaData["version"].toInt(); + int version = jsonData["version"].toInt(); if (version > TextureBundleMetadataVersion) { qWarning() << "Unrecognized texture metadata file version: " << version; return {}; } - return metaData; + return jsonData; } -void ContentLibraryWidget::loadTextureBundle() +void ContentLibraryWidget::loadTextureBundles() { - QDir bundleDir{m_downloadPath}; + QDir bundleDir{m_bundlePath}; - if (fetchTextureBundleMetadata(bundleDir) && fetchTextureBundleIcons(bundleDir)) { - QString bundleIconPath = m_downloadPath + "/TextureBundleIcons"; - QVariantMap metaData = readBundleMetadata(); - m_texturesModel->loadTextureBundle(m_texturesUrl, m_textureIconsUrl, bundleIconPath, metaData); - m_environmentsModel->loadTextureBundle(m_environmentsUrl, m_environmentIconsUrl, - bundleIconPath, metaData); - } + if (fetchTextureBundleJson(bundleDir) && fetchTextureBundleIcons(bundleDir)) + populateTextureBundleModels(); } std::tuple ContentLibraryWidget::compareTextureMetaFiles( @@ -275,9 +264,9 @@ void ContentLibraryWidget::fetchNewTextureIcons(const QVariantMap &existingFiles }); auto multidownloader = new MultiFileDownloader(this); - multidownloader->setBaseUrl(QString(m_baseUrl + "/icons")); + multidownloader->setBaseUrl(QString(m_textureBundleUrl + "/icons")); multidownloader->setFiles(fileList); - multidownloader->setTargetDirPath(m_downloadPath + "/TextureBundleIcons"); + multidownloader->setTargetDirPath(m_bundlePath + "/TextureBundleIcons"); auto downloader = new FileDownloader(this); downloader->setDownloadEnabled(true); @@ -317,15 +306,8 @@ void ContentLibraryWidget::fetchNewTextureIcons(const QVariantMap &existingFiles existingFile.flush(); } - if (fetchTextureBundleIcons(bundleDir)) { - QString bundleIconPath = m_downloadPath + "/TextureBundleIcons"; - QVariantMap metaData = readBundleMetadata(); - m_texturesModel->loadTextureBundle(m_texturesUrl, m_textureIconsUrl, bundleIconPath, - metaData); - m_environmentsModel->loadTextureBundle(m_environmentsUrl, m_environmentIconsUrl, - bundleIconPath, metaData); - } - + if (fetchTextureBundleIcons(bundleDir)) + populateTextureBundleModels(); }); multidownloader->start(); @@ -436,50 +418,45 @@ QStringList ContentLibraryWidget::saveNewTextures(const QDir &bundleDir, const Q } } -bool ContentLibraryWidget::fetchTextureBundleMetadata(const QDir &bundleDir) +bool ContentLibraryWidget::fetchTextureBundleJson(const QDir &bundleDir) { QString filePath = bundleDir.filePath("texture_bundle.json"); QFileInfo fi(filePath); - bool metaFileExists = fi.exists() && fi.size() > 0; + bool jsonFileExists = fi.exists() && fi.size() > 0; - QString metaFileUrl = m_baseUrl + "/texture_bundle.zip"; + QString bundleZipUrl = m_textureBundleUrl + "/texture_bundle.zip"; FileDownloader *downloader = new FileDownloader(this); - downloader->setUrl(metaFileUrl); + downloader->setUrl(bundleZipUrl); downloader->setProbeUrl(false); downloader->setDownloadEnabled(true); + downloader->start(); QObject::connect(downloader, &FileDownloader::downloadFailed, this, - [this, metaFileExists, bundleDir] { - if (metaFileExists) { - if (fetchTextureBundleIcons(bundleDir)) { - QString bundleIconPath = m_downloadPath + "/TextureBundleIcons"; - QVariantMap metaData = readBundleMetadata(); - m_texturesModel->loadTextureBundle(m_texturesUrl, m_textureIconsUrl, bundleIconPath, - metaData); - m_environmentsModel->loadTextureBundle(m_environmentsUrl, m_environmentIconsUrl, - bundleIconPath, metaData); - } + [this, jsonFileExists, bundleDir] { + if (jsonFileExists) { + if (fetchTextureBundleIcons(bundleDir)) + populateTextureBundleModels(); } }); QObject::connect(downloader, &FileDownloader::finishedChanged, this, - [this, downloader, bundleDir, metaFileExists, filePath] { + [this, downloader, bundleDir, jsonFileExists, filePath] { FileExtractor *extractor = new FileExtractor(this); extractor->setArchiveName(downloader->completeBaseName()); extractor->setSourceFile(downloader->outputFile()); - if (!metaFileExists) + if (!jsonFileExists) extractor->setTargetPath(bundleDir.absolutePath()); extractor->setAlwaysCreateDir(false); extractor->setClearTargetPathContents(false); QObject::connect(extractor, &FileExtractor::finishedChanged, this, - [this, downloader, bundleDir, extractor, metaFileExists, filePath] { + [this, downloader, bundleDir, extractor, jsonFileExists, filePath] { downloader->deleteLater(); extractor->deleteLater(); - if (metaFileExists) { + if (jsonFileExists) { QVariantMap newFiles, existing; QVariantMap modifiedFilesEntries; @@ -501,32 +478,35 @@ bool ContentLibraryWidget::fetchTextureBundleMetadata(const QDir &bundleDir) } } - if (fetchTextureBundleIcons(bundleDir)) { - QString bundleIconPath = m_downloadPath + "/TextureBundleIcons"; - QVariantMap metaData = readBundleMetadata(); - m_texturesModel->loadTextureBundle(m_texturesUrl, m_textureIconsUrl, bundleIconPath, - metaData); - m_environmentsModel->loadTextureBundle(m_environmentsUrl, m_environmentIconsUrl, - bundleIconPath, metaData); - } + if (fetchTextureBundleIcons(bundleDir)) + populateTextureBundleModels(); }); extractor->extract(); }); - downloader->start(); return false; } +void ContentLibraryWidget::populateTextureBundleModels() +{ + QVariantMap jsonData = readTextureBundleJson(); + + QString bundleIconPath = m_bundlePath + "/TextureBundleIcons"; + + m_texturesModel->loadTextureBundle(m_textureBundleUrl, bundleIconPath, jsonData); + m_environmentsModel->loadTextureBundle(m_textureBundleUrl, bundleIconPath, jsonData); +} + bool ContentLibraryWidget::fetchTextureBundleIcons(const QDir &bundleDir) { QString iconsPath = bundleDir.filePath("TextureBundleIcons"); QDir iconsDir(iconsPath); - if (iconsDir.exists() && iconsDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot).length() > 0) + if (iconsDir.exists() && !iconsDir.isEmpty()) return true; - QString zipFileUrl = m_baseUrl + "/icons.zip"; + QString zipFileUrl = m_textureBundleUrl + "/icons.zip"; FileDownloader *downloader = new FileDownloader(this); downloader->setUrl(zipFileUrl); @@ -546,13 +526,7 @@ bool ContentLibraryWidget::fetchTextureBundleIcons(const QDir &bundleDir) [this, downloader, extractor] { downloader->deleteLater(); extractor->deleteLater(); - - QString bundleIconPath = m_downloadPath + "/TextureBundleIcons"; - QVariantMap metaData = readBundleMetadata(); - m_texturesModel->loadTextureBundle(m_texturesUrl, m_textureIconsUrl, bundleIconPath, - metaData); - m_environmentsModel->loadTextureBundle(m_environmentsUrl, m_environmentIconsUrl, - bundleIconPath, metaData); + populateTextureBundleModels(); }); extractor->extract(); @@ -575,7 +549,7 @@ void ContentLibraryWidget::markTextureUpdated(const QString &textureKey) checksumOnServer = m_environmentsModel->removeModifiedFileEntry(textureKey); QJsonObject metaDataObj; - QFile jsonFile(m_downloadPath + "/texture_bundle.json"); + QFile jsonFile(m_bundlePath + "/texture_bundle.json"); if (jsonFile.open(QIODevice::ReadOnly | QIODevice::Text)) { metaDataObj = QJsonDocument::fromJson(jsonFile.readAll()).object(); jsonFile.close(); @@ -592,7 +566,7 @@ void ContentLibraryWidget::markTextureUpdated(const QString &textureKey) QJsonDocument outDoc(metaDataObj); QByteArray data = outDoc.toJson(); - QFile outFile(m_downloadPath + "/texture_bundle.json"); + QFile outFile(m_bundlePath + "/texture_bundle.json"); if (outFile.open(QIODeviceBase::WriteOnly | QIODeviceBase::Text)) { outFile.write(data); outFile.flush(); @@ -787,7 +761,7 @@ void ContentLibraryWidget::addImage(ContentLibraryTexture *tex) if (!tex->isDownloaded()) return; - emit addTextureRequested(tex->downloadedTexturePath(), AddTextureMode::Image); + emit addTextureRequested(tex->texturePath(), AddTextureMode::Image); } void ContentLibraryWidget::addTexture(ContentLibraryTexture *tex) @@ -795,7 +769,7 @@ void ContentLibraryWidget::addTexture(ContentLibraryTexture *tex) if (!tex->isDownloaded()) return; - emit addTextureRequested(tex->downloadedTexturePath(), AddTextureMode::Texture); + emit addTextureRequested(tex->texturePath(), AddTextureMode::Texture); } void ContentLibraryWidget::addLightProbe(ContentLibraryTexture *tex) @@ -803,7 +777,7 @@ void ContentLibraryWidget::addLightProbe(ContentLibraryTexture *tex) if (!tex->isDownloaded()) return; - emit addTextureRequested(tex->downloadedTexturePath(), AddTextureMode::LightProbe); + emit addTextureRequested(tex->texturePath(), AddTextureMode::LightProbe); } void ContentLibraryWidget::updateSceneEnvState() diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h index 729443817ea..c4d51d0362b 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h @@ -100,15 +100,16 @@ private: void updateSearch(); void setIsDragging(bool val); QString findTextureBundlePath(); - void loadTextureBundle(); - QVariantMap readBundleMetadata(); - bool fetchTextureBundleMetadata(const QDir &bundleDir); + void loadTextureBundles(); + QVariantMap readTextureBundleJson(); + bool fetchTextureBundleJson(const QDir &bundleDir); bool fetchTextureBundleIcons(const QDir &bundleDir); void fetchNewTextureIcons(const QVariantMap &existingFiles, const QVariantMap &newFiles, const QString &existingMetaFilePath, const QDir &bundleDir); std::tuple compareTextureMetaFiles( const QString &existingMetaFile, const QString downloadedMetaFile); QStringList saveNewTextures(const QDir &bundleDir, const QStringList &newFiles); + void populateTextureBundleModels(); QScopedPointer m_quickWidget; QPointer m_materialsModel; @@ -131,12 +132,8 @@ private: bool m_hasQuick3DImport = false; bool m_isDragging = false; bool m_isQt6Project = false; - QString m_baseUrl; - QString m_texturesUrl; - QString m_textureIconsUrl; - QString m_environmentIconsUrl; - QString m_environmentsUrl; - QString m_downloadPath; + QString m_textureBundleUrl; + QString m_bundlePath; }; } // namespace QmlDesigner From 25320c4fc44f4043f1d1c6c3e1352ad61fffdaef Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 11 Apr 2024 15:48:01 +0200 Subject: [PATCH 124/202] QmlDesigner: Crash fix Change-Id: Iabaf8d5bb6b060fc0ede2c65d8d93141b4e94773 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Thomas Hartmann --- .../components/componentcore/layoutingridlayout.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp b/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp index 8d3412e0e85..89b50c4d1a0 100644 --- a/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp +++ b/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp @@ -452,7 +452,9 @@ void LayoutInGridLayout::removeSpacersBySpanning(QList &nodes) { for (const ModelNode &node : std::as_const(m_spacerNodes)) { if (int index = nodes.indexOf(node)) { - ModelNode before = nodes.at(index -1); + ModelNode before; + if (index > 0) + before = nodes.at(index - 1); if (m_spacerNodes.contains(before)) { m_spacerNodes.removeAll(node); m_layoutedNodes.removeAll(node); From 2c18039de8cd4f040dd87f1de8541c9e038b447a Mon Sep 17 00:00:00 2001 From: Knud Dollereder Date: Fri, 12 Apr 2024 11:13:56 +0200 Subject: [PATCH 125/202] QmlDesigner: Allow negative values in the "Edit Keyframe" Dialog Fixes: QDS-11842 Change-Id: I03d61549d0a0155b48381f00ccdf93dfe97126cd Reviewed-by: Thomas Hartmann --- .../components/timelineeditor/setframevaluedialog.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/plugins/qmldesigner/components/timelineeditor/setframevaluedialog.cpp b/src/plugins/qmldesigner/components/timelineeditor/setframevaluedialog.cpp index 2d8998404a4..f289583cc3e 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/setframevaluedialog.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/setframevaluedialog.cpp @@ -40,7 +40,7 @@ SetFrameValueDialog::SetFrameValueDialog(qreal frame, const QVariant &value, valueLabel->setAlignment(Qt::AlignRight); valueLabel->setFixedWidth(labelWidth); - m_frameControl->setRange(std::numeric_limits::min(), std::numeric_limits::max()); + m_frameControl->setRange(std::numeric_limits::lowest(), std::numeric_limits::max()); m_frameControl->setValue(static_cast(frame)); m_frameControl->setAlignment(Qt::AlignRight); @@ -86,7 +86,6 @@ QWidget* SetFrameValueDialog::createValueControl(const QVariant& value) switch (value.metaType().id()) { - case QMetaType::QColor: { auto* widget = new ColorControl(value.value()); m_valueGetter = [widget]() { return widget->value(); }; @@ -102,7 +101,7 @@ QWidget* SetFrameValueDialog::createValueControl(const QVariant& value) case QMetaType::Int: { auto* widget = new QSpinBox; - widget->setRange(std::numeric_limits::min(), std::numeric_limits::max()); + widget->setRange(std::numeric_limits::lowest(), std::numeric_limits::max()); widget->setAlignment(Qt::AlignRight); widget->setValue(value.toInt()); m_valueGetter = [widget]() { return widget->value(); }; @@ -120,7 +119,7 @@ QWidget* SetFrameValueDialog::createValueControl(const QVariant& value) case QMetaType::Float: { auto* widget = new QDoubleSpinBox; - widget->setRange(std::numeric_limits::min(), std::numeric_limits::max()); + widget->setRange(std::numeric_limits::lowest(), std::numeric_limits::max()); widget->setAlignment(Qt::AlignRight); widget->setValue(value.toFloat()); m_valueGetter = [widget]() { return static_cast(widget->value()); }; @@ -132,7 +131,7 @@ QWidget* SetFrameValueDialog::createValueControl(const QVariant& value) default: { auto* widget = new QDoubleSpinBox; - widget->setRange(std::numeric_limits::min(), std::numeric_limits::max()); + widget->setRange(std::numeric_limits::lowest(), std::numeric_limits::max()); widget->setAlignment(Qt::AlignRight); widget->setValue(value.toDouble()); m_valueGetter = [widget]() { return widget->value(); }; From 68cb22de5274442295611f4d0fc61c7d085a862f Mon Sep 17 00:00:00 2001 From: Teea Poldsam Date: Thu, 11 Apr 2024 11:58:00 +0300 Subject: [PATCH 126/202] Doc: Add info about editing view3D from 2D view Fixes: QDS-12464 Change-Id: I0031203590b14b07e7162fc42e409ce0789f76c5 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Mahmoud Badri Reviewed-by: Miikka Heikkinen --- .../qtquick3d-editor/qtdesignstudio-3d-model.qdoc | 3 +++ .../src/qtquick3d-editor/qtdesignstudio-3d-view.qdoc | 12 ++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-model.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-model.qdoc index 1b515a6e6de..e1f6edd8dec 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-model.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-model.qdoc @@ -27,6 +27,9 @@ \note You can not create \uicontrol Empty models this way. \endlist + Double-clicking a 3D model in \uicontrol 2D view opens the \uicontrol 3D view with + the 3D model selected. + If you cannot find the model components in \uicontrol {Components}, add the \uicontrol QtQuick3D module to your project, as described in \l {Adding and Removing Modules}. diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-view.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-view.qdoc index 6635de2ad34..1ada3625ff3 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-view.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-view.qdoc @@ -35,8 +35,16 @@ \image studio-qtquick-3d-components.webp "QtQuick3D components" You can now drag a \uicontrol View3D or an \uicontrol {Extended View3D} component from - \l Components > \uicontrol QtQuick3D \uicontrol - > Items to \l Navigator or to the \l {2D} view. + \l Components > \uicontrol QtQuick3D > \uicontrol Items to \l Navigator or to the + \l {2D} view. + + To switch to the \uicontrol 3D view while maintaining the camera orientation of the + \uicontrol View3D, right-click a \uicontrol View3D or an \uicontrol {Extended View3D} + component in the \uicontrol Navigator or \uicontrol 2D view and select + \uicontrol {Edit in 3D View}. Alternatively, you can double-click a \uicontrol View3D + or an \uicontrol {Extended View3D} component in the \uicontrol 2D view to open the + \uicontrol 3D view. Double-clicking a 3D model in the \uicontrol 2D view opens the + \uicontrol 3D view with the 3D model selected. \image studio-navigator-view3d.png "A View 3D component in Navigator" From b0eccd58f50cc0a978e83bc01badd9ad181bc843 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 12 Apr 2024 11:02:05 +0200 Subject: [PATCH 127/202] QmlDesigner: Fix bindings Task-number: QDS-12471 Change-Id: Ie172b966db0e15f73868542502313f6460137543 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Tim Jenssen --- .../qmldesigner/designercore/model/texttomodelmerger.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index 708578e671e..149bcc354fa 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -1194,7 +1194,8 @@ void TextToModelMerger::syncNode(ModelNode &modelNode, // Store Behaviours in the default property defaultPropertyItems.append(member); } else { - if (isPropertyChangesType(typeName) || isConnectionsType(typeName)) { + if (isPropertyChangesType(typeName) || isConnectionsType(typeName) + || modelNode.metaInfo().hasProperty(astPropertyName.toUtf8())) { AbstractProperty modelProperty = modelNode.property(astPropertyName.toUtf8()); if (context->isArrayProperty(modelProperty)) syncArrayProperty(modelProperty, {member}, context, differenceHandler); @@ -1416,9 +1417,9 @@ QmlDesigner::PropertyName TextToModelMerger::syncScriptBinding(ModelNode &modelN syncVariantProperty(modelProperty, enumValue, TypeName(), differenceHandler); // TODO: parse type return astPropertyName.toUtf8(); } else { // Not an enum, so: - if (isPropertyChangesType(modelNode.type()) - || isConnectionsType(modelNode.type()) - || isSupportedAttachedProperties(astPropertyName)) { + if (isPropertyChangesType(modelNode.type()) || isConnectionsType(modelNode.type()) + || isSupportedAttachedProperties(astPropertyName) + || modelNode.metaInfo().hasProperty(astPropertyName.toUtf8())) { AbstractProperty modelProperty = modelNode.property(astPropertyName.toUtf8()); syncExpressionProperty(modelProperty, astValue, TypeName(), differenceHandler); // TODO: parse type return astPropertyName.toUtf8(); From a5335f5371e17af6a28f992006238d8f4f61ecac Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 12 Apr 2024 14:43:17 +0300 Subject: [PATCH 128/202] EffectComposer: Remove use of native separators from file paths Using native separators causes soft asserts in FilePath. Change-Id: I45c5002a555a21dd3780ffd5c8df91b849a67c18 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- src/plugins/effectcomposer/effectcomposermodel.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/plugins/effectcomposer/effectcomposermodel.cpp b/src/plugins/effectcomposer/effectcomposermodel.cpp index f08053518fa..b3df849e059 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.cpp +++ b/src/plugins/effectcomposer/effectcomposermodel.cpp @@ -212,14 +212,14 @@ void EffectComposerModel::clear(bool clearName) void EffectComposerModel::assignToSelected() { const QString effectsAssetsDir = QmlDesigner::ModelNodeOperations::getEffectsDefaultDirectory(); - const QString path = effectsAssetsDir + QDir::separator() + m_currentComposition + ".qep"; + const QString path = effectsAssetsDir + '/' + m_currentComposition + ".qep"; emit assignToSelectedTriggered(path); } QString EffectComposerModel::getUniqueEffectName() const { const QString effectsDir = QmlDesigner::ModelNodeOperations::getEffectsDefaultDirectory(); - const QString path = effectsDir + QDir::separator() + "Effect%1.qep"; + const QString path = effectsDir + '/' + "Effect%1.qep"; int num = 0; @@ -232,7 +232,7 @@ QString EffectComposerModel::getUniqueEffectName() const bool EffectComposerModel::nameExists(const QString &name) const { const QString effectsDir = QmlDesigner::ModelNodeOperations::getEffectsDefaultDirectory(); - const QString path = effectsDir + QDir::separator() + "%1" + ".qep"; + const QString path = effectsDir + '/' + "%1" + ".qep"; return QFile::exists(path.arg(name)); } @@ -950,7 +950,7 @@ void EffectComposerModel::saveComposition(const QString &name) } const QString effectsAssetsDir = QmlDesigner::ModelNodeOperations::getEffectsDefaultDirectory(); - const QString path = effectsAssetsDir + QDir::separator() + name + ".qep"; + const QString path = effectsAssetsDir + '/' + name + ".qep"; auto saveFile = QFile(path); if (!saveFile.open(QIODevice::WriteOnly)) { QString error = QString("Error: Couldn't save composition file: '%1'").arg(path); @@ -1080,7 +1080,7 @@ void EffectComposerModel::saveResources(const QString &name) // Get effects dir const Utils::FilePath effectsResDir = QmlDesigner::ModelNodeOperations::getEffectsImportDirectory(); - const QString effectsResPath = effectsResDir.pathAppended(name).toString() + QDir::separator(); + const QString effectsResPath = effectsResDir.pathAppended(name).toString() + '/'; Utils::FilePath effectPath = Utils::FilePath::fromString(effectsResPath); // Create the qmldir for effects From 3700eac9f34bbafe337e4fe7cd6f31b838d17744 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 12 Apr 2024 15:38:50 +0300 Subject: [PATCH 129/202] EffectComposer: Remove invalid properties from effects in use When effect is saved, the properties it exposed can change if nodes are deleted from the effect. If any of those removed exposed properties was assigned a value in the scene, those property assignments are removed from the scene. Fixes: QDS-12359 Change-Id: Ia3840584c6361b9e140c6840ff8fa3036c1b7d93 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot Reviewed-by: --- .../effectcomposer/effectcomposermodel.cpp | 41 +++++++++++- .../effectcomposer/effectcomposermodel.h | 3 + .../effectcomposer/effectcomposerview.cpp | 65 ++++++++++++++++++- 3 files changed, 106 insertions(+), 3 deletions(-) diff --git a/src/plugins/effectcomposer/effectcomposermodel.cpp b/src/plugins/effectcomposer/effectcomposermodel.cpp index b3df849e059..a983072334b 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.cpp +++ b/src/plugins/effectcomposer/effectcomposermodel.cpp @@ -1147,7 +1147,27 @@ void EffectComposerModel::saveResources(const QString &name) const QString qmlString = qmlStringList.join('\n'); QString qmlFilePath = effectsResPath + qmlFilename; - writeToFile(qmlString.toUtf8(), qmlFilePath, FileType::Text); + + // Get exposed properties from the old qml file if it exists + QSet oldExposedProps; + Utils::FilePath oldQmlFile = Utils::FilePath::fromString(qmlFilePath); + if (oldQmlFile.exists()) { + const QByteArray oldQmlContent = oldQmlFile.fileContents().value(); + oldExposedProps = getExposedProperties(oldQmlContent); + } + + const QByteArray qmlUtf8 = qmlString.toUtf8(); + if (!oldExposedProps.isEmpty()) { + const QSet newExposedProps = getExposedProperties(qmlUtf8); + oldExposedProps.subtract(newExposedProps); + if (!oldExposedProps.isEmpty()) { + // If there were exposed properties that are no longer exposed, those + // need to be removed from any instances of the effect in the scene + emit removePropertiesFromScene(oldExposedProps, name); + } + } + + writeToFile(qmlUtf8, qmlFilePath, FileType::Text); newFileNames.append(qmlFilename); // Save shaders and images @@ -1998,6 +2018,25 @@ void EffectComposerModel::updateExtraMargin() m_extraMargin = qMax(node->extraMargin(), m_extraMargin); } +QSet EffectComposerModel::getExposedProperties(const QByteArray &qmlContent) +{ + QSet returnSet; + const QByteArrayList lines = qmlContent.split('\n'); + const QByteArray propertyTag {" property"}; // Match only toplevel exposed properties + for (const QByteArray &line : lines) { + if (line.startsWith(propertyTag)) { + QByteArrayList words = line.trimmed().split(' '); + if (words.size() >= 3) { + QByteArray propName = words[2]; + if (propName.endsWith(':')) + propName.chop(1); + returnSet.insert(propName); + } + } + } + return returnSet; +} + QString EffectComposerModel::currentComposition() const { return m_currentComposition; diff --git a/src/plugins/effectcomposer/effectcomposermodel.h b/src/plugins/effectcomposer/effectcomposermodel.h index 2381b09ec1c..14ef09e8a97 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.h +++ b/src/plugins/effectcomposer/effectcomposermodel.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -124,6 +125,7 @@ signals: void resourcesSaved(const QByteArray &type, const Utils::FilePath &path); void hasUnsavedChangesChanged(); void assignToSelectedTriggered(const QString &effectPath); + void removePropertiesFromScene(QSet props, const QString &typeName); private: enum Roles { @@ -185,6 +187,7 @@ private: void connectCompositionNode(CompositionNode *node); void updateExtraMargin(); + QSet getExposedProperties(const QByteArray &qmlContent); QList m_nodes; diff --git a/src/plugins/effectcomposer/effectcomposerview.cpp b/src/plugins/effectcomposer/effectcomposerview.cpp index c7967db4ae9..48c6a33c4b6 100644 --- a/src/plugins/effectcomposer/effectcomposerview.cpp +++ b/src/plugins/effectcomposer/effectcomposerview.cpp @@ -10,7 +10,9 @@ #include #include #include +#include #include +#include #include @@ -49,14 +51,73 @@ QmlDesigner::WidgetInfo EffectComposerView::widgetInfo() m_widget = new EffectComposerWidget{this}; connect(m_widget->effectComposerModel(), &EffectComposerModel::assignToSelectedTriggered, this, - [&] (const QString &effectPath) { - executeInTransaction("EffectComposerView::widgetInfo", [&] { + [this] (const QString &effectPath) { + executeInTransaction("EffectComposerView assignToSelectedTriggered", [&] { const QList selectedNodes = selectedModelNodes(); for (const QmlDesigner::ModelNode &node : selectedNodes) QmlDesigner::ModelNodeOperations::handleItemLibraryEffectDrop(effectPath, node); }); }); + connect(m_widget->effectComposerModel(), &EffectComposerModel::removePropertiesFromScene, this, + [this] (QSet props, const QString &typeName) { + // Remove specified properties from all instances of specified type + + QmlDesigner::DesignDocument *document + = QmlDesigner::QmlDesignerPlugin::instance()->currentDesignDocument(); + if (!document) + return; + + const QByteArray fullType = QString("%1.%2.%2").arg(m_componentUtils.composedEffectsTypePrefix(), + typeName).toUtf8(); + const QList allNodes = allModelNodes(); + QList typeNodes; + QList propertyChangeNodes; + for (const QmlDesigner::ModelNode &node : allNodes) { + if (QmlDesigner::QmlPropertyChanges::isValidQmlPropertyChanges(node)) + propertyChangeNodes.append(node); +#ifdef QDS_USE_PROJECTSTORAGE +// TODO: typeName() shouldn't be used with projectstorage. Needs alternative solution (using modules?) +#else + else if (node.metaInfo().typeName() == fullType) + typeNodes.append(node); +#endif + } + if (!typeNodes.isEmpty()) { + bool clearStacks = false; + + executeInTransaction("EffectComposerView removePropertiesFromScene", [&] { + for (QmlDesigner::ModelNode node : std::as_const(propertyChangeNodes)) { + QmlDesigner::ModelNode targetNode = QmlDesigner::QmlPropertyChanges(node).target(); + if (typeNodes.contains(targetNode)) { + for (const QByteArray &prop : props) { + if (node.hasProperty(prop)) { + node.removeProperty(prop); + clearStacks = true; + } + } + QList remainingProps = node.properties(); + if (remainingProps.size() == 1 && remainingProps[0].name() == "target") + node.destroy(); // Remove empty changes node + } + } + for (const QmlDesigner::ModelNode &node : std::as_const(typeNodes)) { + for (const QByteArray &prop : props) { + if (node.hasProperty(prop)) { + node.removeProperty(prop); + clearStacks = true; + } + } + } + }); + + // Reset undo stack as changing of the actual effect cannot be undone, and thus the + // stack will contain only unworkable states + if (clearStacks) + document->clearUndoRedoStacks(); + } + }); + auto context = new EffectComposerContext(m_widget.data()); Core::ICore::addContextObject(context); } From 97def664148c4daf79a872476ef8db5d02c9446b Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 12 Apr 2024 16:21:14 +0200 Subject: [PATCH 130/202] QmlDesigner: Properly parse properties Checking here for the property name was wrong since, we only want to skip conversion for property changes, list elements and connections. Task-number: QDS-12482 Change-Id: Ibff7164b8c3a28d50c483da256450a661a472bb7 Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- .../qmldesigner/designercore/model/texttomodelmerger.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index 149bcc354fa..885868ddd00 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -1382,8 +1382,7 @@ QmlDesigner::PropertyName TextToModelMerger::syncScriptBinding(ModelNode &modelN if (isLiteralValue(script)) { if (isPropertyChangesType(modelNode.type()) || isConnectionsType(modelNode.type()) - || isListElementType(modelNode.type()) - || modelNode.metaInfo().hasProperty(astPropertyName.toUtf8())) { + || isListElementType(modelNode.type())) { AbstractProperty modelProperty = modelNode.property(astPropertyName.toUtf8()); QVariant variantValue = parsePropertyScriptBinding(script); if (!variantValue.isValid()) From 36f666174fb2e02c60fe0794d39e3714deab477e Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 12 Apr 2024 17:14:12 +0200 Subject: [PATCH 131/202] QmlDesigner: Add import for DesignEffects Change-Id: Ib6a2c8bd8e6d04fe532fe524225d501c40a7a042 Reviewed-by: Thomas Hartmann --- .../propertyEditorQmlSources/QtQuick/EffectsSection.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml index 37fca814423..769cb679d59 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml @@ -68,7 +68,7 @@ Section { } else { modelNodeBackend.createModelNode(-1, "data", "DesignEffect") var effectNode = modelNodeBackend.allChildrenOfType("DesignEffect") - modelNodeBackend.createModelNode(effectNode, "effects", "DesignDropShadow") + modelNodeBackend.createModelNode(effectNode, "effects", "DesignDropShadow", "QtQuick.Studio.DesignEffects") } root.invalidate() } From 8a21dc325cf88d7a86ddb793193bcf12ce17bf24 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 12 Apr 2024 17:06:46 +0200 Subject: [PATCH 132/202] QmlDesigner: Fix DesignerEffects rendering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The property name changed and we adda bit more padding. Change-Id: I9432f3d5a053d38feb32649be52a99696a9f3b6c Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Henning GrĂĽndl --- .../qml2puppet/qml2puppet/instances/servernodeinstance.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/qml2puppet/qml2puppet/instances/servernodeinstance.cpp b/src/tools/qml2puppet/qml2puppet/instances/servernodeinstance.cpp index 9600a0b2b0d..aa91b8ffaa7 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/servernodeinstance.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/servernodeinstance.cpp @@ -126,8 +126,8 @@ QRectF ServerNodeInstance::effectAdjustedBoundingRect(QQuickItem *item) if (pItem && pItem->layer() && pItem->layer()->sourceRect().isValid()) { return pItem->layer()->sourceRect(); } else if (prop.read().toBool()) { - prop = QQmlProperty(item, "allEffects"); - QRectF rect = prop.read().toRectF().adjusted(-20, -20, 20, 20); + prop = QQmlProperty(item, "effectBoundingBox"); + QRectF rect = prop.read().toRectF().adjusted(-40, -40, 40, 40); if (rect.isValid()) return rect; return item->boundingRect(); From c9f9804edf9d15a251b90724eff4b0587ebbe018 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 12 Apr 2024 17:08:14 +0200 Subject: [PATCH 133/202] QmlDesigner: Fix getting the bounding rectangle for an effect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When getting the bounding rect for an effect we actually have to find the item with the effect first. Change-Id: I4e20f77c6b8dca9d58ccffb42d8b803f57317cb9 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Henning GrĂĽndl --- .../instances/quickitemnodeinstance.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/tools/qml2puppet/qml2puppet/instances/quickitemnodeinstance.cpp b/src/tools/qml2puppet/qml2puppet/instances/quickitemnodeinstance.cpp index f2fbb97abab..a9c49b96611 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/quickitemnodeinstance.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/quickitemnodeinstance.cpp @@ -277,11 +277,25 @@ static bool layerEnabledAndEffect(QQuickItem *item) return false; } +static QRectF getBoundingRectForEffect(QQuickItem *item) +{ + const auto siblings = item->parentItem()->childItems(); + for (auto sibling : siblings) { + QQmlProperty prop(sibling, "__effect"); + if (prop.read().toBool()) { + prop = QQmlProperty(sibling, "source"); + if (prop.read().value() == item) + return ServerNodeInstance::effectAdjustedBoundingRect(sibling); + } + } + return ServerNodeInstance::effectAdjustedBoundingRect(item); +} + QRectF QuickItemNodeInstance::boundingRect() const { if (quickItem()) { - if (layerEnabledAndEffect(quickItem())) { - return ServerNodeInstance::effectAdjustedBoundingRect(quickItem()); + if (layerEnabledAndEffect(quickItem()) && quickItem()->parentItem()) { + return getBoundingRectForEffect(quickItem()); } else if (quickItem()->clip()) { return quickItem()->boundingRect(); } else { From db84bc43a9086e1ebc2662f7f04a03ab1aed82ca Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 11 Apr 2024 18:13:10 +0200 Subject: [PATCH 134/202] QmlDesigner: Improve node creation with project storage The file url was missing, so no valid source id could be created. That lead to invalid imports. The qualified path for the node instances is now created in the node instance view. Change-Id: I2685ab2fdbdb0fa8a5f89793be52c8fae2b8db8c Reviewed-by: Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../components/integration/designdocument.cpp | 5 +- .../components/integration/designdocument.h | 3 +- .../qmldesigner/designercore/include/model.h | 1 + .../designercore/include/nodemetainfo.h | 2 +- .../instances/nodeinstanceview.cpp | 59 +++++++++++++------ .../designercore/metainfo/nodemetainfo.cpp | 2 +- .../qmldesigner/designercore/model/model.cpp | 10 ++++ .../designercore/model/texttomodelmerger.cpp | 2 +- .../projectstorage/commontypecache.h | 3 + .../projectstorage/projectstorage.h | 9 ++- .../projectstorage/projectstorageinterface.h | 1 + src/plugins/qmldesigner/documentmanager.cpp | 4 +- tests/unit/tests/mocks/projectstoragemock.cpp | 13 ++++ tests/unit/tests/mocks/projectstoragemock.h | 8 +++ .../tests/unittests/model/modelutils-test.cpp | 2 +- 15 files changed, 95 insertions(+), 29 deletions(-) diff --git a/src/plugins/qmldesigner/components/integration/designdocument.cpp b/src/plugins/qmldesigner/components/integration/designdocument.cpp index c0bebbb82bd..804ac076e62 100644 --- a/src/plugins/qmldesigner/components/integration/designdocument.cpp +++ b/src/plugins/qmldesigner/components/integration/designdocument.cpp @@ -64,13 +64,14 @@ namespace QmlDesigner { DesignDocument acts as a facade to a model representing a qml document, and the different views/widgets accessing it. */ -DesignDocument::DesignDocument(ProjectStorageDependencies projectStorageDependencies, +DesignDocument::DesignDocument([[maybe_unused]] const QUrl &filePath, + ProjectStorageDependencies projectStorageDependencies, ExternalDependenciesInterface &externalDependencies) #ifdef QDS_USE_PROJECTSTORAGE : m_documentModel(Model::create(projectStorageDependencies, "Item", {Import::createLibraryImport("QtQuick")}, - {}, + filePath, std::make_unique())) #else : m_documentModel( diff --git a/src/plugins/qmldesigner/components/integration/designdocument.h b/src/plugins/qmldesigner/components/integration/designdocument.h index 0d75141205e..52089d67c1b 100644 --- a/src/plugins/qmldesigner/components/integration/designdocument.h +++ b/src/plugins/qmldesigner/components/integration/designdocument.h @@ -41,7 +41,8 @@ class QMLDESIGNERCOMPONENTS_EXPORT DesignDocument : public QObject Q_OBJECT public: - DesignDocument(ProjectStorageDependencies projectStorageDependencies, + DesignDocument(const QUrl &filePath, + ProjectStorageDependencies projectStorageDependencies, ExternalDependenciesInterface &externalDependencies); ~DesignDocument() override; diff --git a/src/plugins/qmldesigner/designercore/include/model.h b/src/plugins/qmldesigner/designercore/include/model.h index 8dd87baa5bf..85f129cdbce 100644 --- a/src/plugins/qmldesigner/designercore/include/model.h +++ b/src/plugins/qmldesigner/designercore/include/model.h @@ -166,6 +166,7 @@ public: NodeMetaInfo qtQmlConnectionsMetaInfo() const; NodeMetaInfo qtQmlModelsListModelMetaInfo() const; NodeMetaInfo qtQmlModelsListElementMetaInfo() const; + NodeMetaInfo qtQmlXmlListModelXmlListModelRoleMetaInfo() const; NodeMetaInfo qtQuick3DBakedLightmapMetaInfo() const; NodeMetaInfo qtQuick3DDefaultMaterialMetaInfo() const; NodeMetaInfo qtQuick3DDirectionalLightMetaInfo() const; diff --git a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h index 53c755ddc88..23110175c1b 100644 --- a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h +++ b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h @@ -167,6 +167,7 @@ public: bool isQtMultimediaSoundEffect() const; bool isQtObject() const; bool isQtQmlConnections() const; + bool isQtQmlModelsListElement() const; bool isQtQuick3DBakedLightmap() const; bool isQtQuick3DBuffer() const; bool isQtQuick3DCamera() const; @@ -176,7 +177,6 @@ public: bool isQtQuick3DInstanceList() const; bool isQtQuick3DInstanceListEntry() const; bool isQtQuick3DLight() const; - bool isQtQuickListElement() const; bool isQtQuickListModel() const; bool isQtQuickListView() const; bool isQtQuick3DMaterial() const; diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index 33a100f7b20..26f2687c233 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -64,6 +64,8 @@ #include #include +#include + #include #include @@ -205,22 +207,17 @@ NodeInstanceView::~NodeInstanceView() static bool isSkippedRootNode(const ModelNode &node) { - static const PropertyNameList skipList({"Qt.ListModel", "QtQuick.ListModel", "Qt.ListModel", "QtQuick.ListModel"}); - - if (skipList.contains(node.type())) - return true; - - return false; + return node.metaInfo().isQtQuickListModel(); } static bool isSkippedNode(const ModelNode &node) { - static const PropertyNameList skipList({"QtQuick.XmlRole", "Qt.XmlRole", "QtQuick.ListElement", "Qt.ListElement"}); + auto model = node.model(); - if (skipList.contains(node.type())) - return true; + auto listElement = model->qtQmlModelsListElementMetaInfo(); + auto xmlRole = model->qtQmlXmlListModelXmlListModelRoleMetaInfo(); - return false; + return node.metaInfo().isBasedOn(listElement, xmlRole); } static bool parentTakesOverRendering(const ModelNode &modelNode) @@ -644,7 +641,7 @@ void NodeInstanceView::auxiliaryDataChanged(const ModelNode &node, TypeName(), key.type}; m_nodeInstanceServer->changeAuxiliaryValues({{container}}); - }; + } break; case AuxiliaryDataType::NodeInstanceAuxiliary: @@ -656,7 +653,7 @@ void NodeInstanceView::auxiliaryDataChanged(const ModelNode &node, TypeName(), key.type}; m_nodeInstanceServer->changeAuxiliaryValues({{container}}); - }; + } break; case AuxiliaryDataType::NodeInstancePropertyOverwrite: @@ -991,6 +988,8 @@ QRectF NodeInstanceView::sceneRect() const return {}; } +namespace { + QList filterNodesForSkipItems(const QList &nodeList) { QList filteredNodeList; @@ -1003,14 +1002,12 @@ QList filterNodesForSkipItems(const QList &nodeList) return filteredNodeList; } -namespace { bool shouldSendAuxiliary(const AuxiliaryDataKey &key) { return key.type == AuxiliaryDataType::NodeInstancePropertyOverwrite || key.type == AuxiliaryDataType::NodeInstanceAuxiliary || key == invisibleProperty || key == lockedProperty; } -} // namespace bool parentIsBehavior(ModelNode node) { @@ -1024,6 +1021,31 @@ bool parentIsBehavior(ModelNode node) return false; } +TypeName createQualifiedTypeName(const ModelNode &node) +{ + if (!node) + return {}; + +#ifdef QDS_USE_PROJECTSTORAGE + auto model = node.model(); + auto exportedTypes = node.metaInfo().exportedTypeNamesForSourceId(model->fileUrlSourceId()); + if (exportedTypes.size()) { + const auto &exportedType = exportedTypes.front(); + Utils::PathString typeName = model->projectStorage()->moduleName(exportedType.moduleId); + typeName += '/'; + typeName += exportedType.name; + + return typeName.toQByteArray(); + } + + return {}; +#else + return node.type(); +#endif +} + +} // namespace + CreateSceneCommand NodeInstanceView::createCreateSceneCommand() { QList nodeList = allModelNodes(); @@ -1079,8 +1101,9 @@ CreateSceneCommand NodeInstanceView::createCreateSceneCommand() nodeFlags |= InstanceContainer::ParentTakesOverRendering; const auto modelNode = instance.modelNode(); + InstanceContainer container(instance.instanceId(), - modelNode.type(), + createQualifiedTypeName(modelNode), modelNode.majorVersion(), modelNode.minorVersion(), ModelUtils::componentFilePath(modelNode), @@ -1243,7 +1266,7 @@ CreateInstancesCommand NodeInstanceView::createCreateInstancesCommand(const QLis const auto modelNode = instance.modelNode(); InstanceContainer container(instance.instanceId(), - modelNode.type(), + createQualifiedTypeName(modelNode), modelNode.majorVersion(), modelNode.minorVersion(), ModelUtils::componentFilePath(modelNode), @@ -1850,7 +1873,7 @@ QVariant NodeInstanceView::previewImageDataForImageNode(const ModelNode &modelNo ModelNodePreviewImageData imageData; imageData.id = modelNode.id(); - imageData.type = QString::fromLatin1(modelNode.type()); + imageData.type = QString::fromUtf8(createQualifiedTypeName(modelNode)); const double ratio = m_externalDependencies.formEditorDevicePixelRatio(); if (imageSource.isEmpty() && modelNode.metaInfo().isQtQuick3DTexture()) { @@ -1958,7 +1981,7 @@ QVariant NodeInstanceView::previewImageDataForGenericNode(const ModelNode &model if (m_imageDataMap.contains(id)) { imageData = m_imageDataMap[id]; } else { - imageData.type = QString::fromLatin1(modelNode.type()); + imageData.type = QString::fromLatin1(createQualifiedTypeName(modelNode)); imageData.id = id; m_imageDataMap.insert(id, imageData); } diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index 45da52254d0..845a1db1e52 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -2766,7 +2766,7 @@ bool NodeMetaInfo::isQtQuick3DLight() const } } -bool NodeMetaInfo::isQtQuickListElement() const +bool NodeMetaInfo::isQtQmlModelsListElement() const { if constexpr (useProjectStorage()) { using namespace Storage::Info; diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index 714480991d0..4f0bfba1ced 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -2210,6 +2210,16 @@ NodeMetaInfo Model::qtQmlModelsListElementMetaInfo() const } } +NodeMetaInfo Model::qtQmlXmlListModelXmlListModelRoleMetaInfo() const +{ + if constexpr (useProjectStorage()) { + using namespace Storage::Info; + return createNodeMetaInfo(); + } else { + return metaInfo("QtQml.XmlListModel.XmlListModelRole"); + } +} + NodeMetaInfo Model::qmlQtObjectMetaInfo() const { if constexpr (useProjectStorage()) { diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index 885868ddd00..1c1aba5feb9 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -501,7 +501,7 @@ public: if (!propertyMetaInfo.isValid()) { const bool isAttached = !propertyName.isEmpty() && propertyName[0].isUpper(); // Only list elements might have unknown properties. - if (!node.metaInfo().isQtQuickListElement() && !isAttached) { + if (!node.metaInfo().isQtQmlModelsListElement() && !isAttached) { qCInfo(texttomodelMergerLog) << Q_FUNC_INFO << "\nUnknown property" << propertyPrefix + QLatin1Char('.') + toString(propertyId) << "on line" diff --git a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h index 03c25dfac79..35658c005f1 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h @@ -91,6 +91,7 @@ inline constexpr char QtMultimedia[] = "QtMultimedia"; inline constexpr char QtObject[] = "QtObject"; inline constexpr char QtQml[] = "QtQml"; inline constexpr char QtQml_Models[] = "QtQml.Models"; +inline constexpr char QtQml_XmlListModel[] = "QtQml.XmlListModel"; inline constexpr char QtQuick3D[] = "QtQuick3D"; inline constexpr char QtQuick3D_Particles3D[] = "QtQuick3D.Particles3D"; inline constexpr char QtQuick3D_Particles3D_cppnative[] = "QtQuick3D.Particles3D-cppnative"; @@ -131,6 +132,7 @@ inline constexpr char Transition[] = "Transition"; inline constexpr char UIntType[] = "uint"; inline constexpr char View3D[] = "View3D"; inline constexpr char Window[] = "Window"; +inline constexpr char XmlListModelRole[] = "XmlListModelRole"; inline constexpr char color[] = "color"; inline constexpr char date[] = "date"; inline constexpr char font[] = "font"; @@ -176,6 +178,7 @@ class CommonTypeCache CacheType, CacheType, CacheType, + CacheType, CacheType, CacheType, CacheType, diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index 7188d0e814e..83e11f952bc 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -168,7 +168,7 @@ public: return moduleId; } - Utils::SmallString moduleName(ModuleId moduleId) const + Utils::SmallString moduleName(ModuleId moduleId) const override { using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"get module name"_t, @@ -3278,8 +3278,9 @@ private: auto update = [&](const TypeWithDefaultPropertyView &view, const Storage::Synchronization::Type &value) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"reset default properties by update"_t, + NanotraceHR::Tracer tracer{"synchronize default properties by update"_t, projectStorageCategory(), + keyValue("type id", value.typeId), keyValue("value", value), keyValue("view", view)}; @@ -3294,7 +3295,8 @@ private: updateDefaultPropertyIdStatement.write(value.typeId, valueDefaultPropertyId); - tracer.end(keyValue("updated", "yes")); + tracer.end(keyValue("updated", "yes"), + keyValue("default property id", valueDefaultPropertyId)); return Sqlite::UpdateChange::Update; }; @@ -3324,6 +3326,7 @@ private: using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"reset changed default properties by update"_t, projectStorageCategory(), + keyValue("type id", value.typeId), keyValue("value", value), keyValue("view", view)}; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h index edf35f745cb..13a40695bf9 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h @@ -32,6 +32,7 @@ public: virtual void removeObserver(ProjectStorageObserver *observer) = 0; virtual ModuleId moduleId(::Utils::SmallStringView name) const = 0; + virtual Utils::SmallString moduleName(ModuleId moduleId) const = 0; virtual std::optional propertyDeclaration(PropertyDeclarationId propertyDeclarationId) const = 0; virtual TypeId typeId(ModuleId moduleId, diff --git a/src/plugins/qmldesigner/documentmanager.cpp b/src/plugins/qmldesigner/documentmanager.cpp index 75830d19b3d..b94de22c289 100644 --- a/src/plugins/qmldesigner/documentmanager.cpp +++ b/src/plugins/qmldesigner/documentmanager.cpp @@ -230,7 +230,9 @@ void DocumentManager::setCurrentDesignDocument(Core::IEditor *editor) auto found = m_designDocuments.find(editor); if (found == m_designDocuments.end()) { auto &inserted = m_designDocuments[editor] = std::make_unique( - m_projectManager.projectStorageDependencies(), m_externalDependencies); + editor->document()->filePath().toString(), + m_projectManager.projectStorageDependencies(), + m_externalDependencies); m_currentDesignDocument = inserted.get(); m_currentDesignDocument->setEditor(editor); } else { diff --git a/tests/unit/tests/mocks/projectstoragemock.cpp b/tests/unit/tests/mocks/projectstoragemock.cpp index 27e5c152d21..48357ff3e63 100644 --- a/tests/unit/tests/mocks/projectstoragemock.cpp +++ b/tests/unit/tests/mocks/projectstoragemock.cpp @@ -51,6 +51,7 @@ ModuleId ProjectStorageMock::createModule(Utils::SmallStringView moduleName) incrementBasicId(moduleId); ON_CALL(*this, moduleId(Eq(moduleName))).WillByDefault(Return(moduleId)); + ON_CALL(*this, moduleName(Eq(moduleId))).WillByDefault(Return(moduleName)); ON_CALL(*this, fetchModuleIdUnguarded(Eq(moduleName))).WillByDefault(Return(moduleId)); return moduleId; @@ -122,6 +123,14 @@ void ProjectStorageMock::addExportedTypeName(QmlDesigner::TypeId typeId, exportedTypeName[typeId].emplace_back(moduleId, typeName); } +void ProjectStorageMock::addExportedTypeNameBySourceId(QmlDesigner::TypeId typeId, + QmlDesigner::ModuleId moduleId, + Utils::SmallStringView typeName, + QmlDesigner::SourceId sourceId) +{ + exportedTypeNameBySourceId[{typeId, sourceId}].emplace_back(moduleId, typeName); +} + void ProjectStorageMock::removeExportedTypeName(QmlDesigner::TypeId typeId, QmlDesigner::ModuleId moduleId, Utils::SmallStringView typeName) @@ -364,6 +373,10 @@ ProjectStorageMock::ProjectStorageMock() ON_CALL(*this, exportedTypeNames(_)).WillByDefault([&](TypeId id) { return exportedTypeName[id]; }); + + ON_CALL(*this, exportedTypeNames(_, _)).WillByDefault([&](TypeId typeId, SourceId sourceId) { + return exportedTypeNameBySourceId[{typeId, sourceId}]; + }); } void ProjectStorageMock::setupQtQuick() diff --git a/tests/unit/tests/mocks/projectstoragemock.h b/tests/unit/tests/mocks/projectstoragemock.h index ed370bde96f..a68e37c2a15 100644 --- a/tests/unit/tests/mocks/projectstoragemock.h +++ b/tests/unit/tests/mocks/projectstoragemock.h @@ -46,6 +46,11 @@ public: QmlDesigner::ModuleId moduleId, Utils::SmallStringView typeName); + void addExportedTypeNameBySourceId(QmlDesigner::TypeId typeId, + QmlDesigner::ModuleId moduleId, + Utils::SmallStringView typeName, + QmlDesigner::SourceId sourceId); + void removeExportedTypeName(QmlDesigner::TypeId typeId, QmlDesigner::ModuleId moduleId, Utils::SmallStringView typeName); @@ -122,6 +127,7 @@ public: MOCK_METHOD(void, removeObserver, (QmlDesigner::ProjectStorageObserver *), (override)); MOCK_METHOD(QmlDesigner::ModuleId, moduleId, (::Utils::SmallStringView), (const, override)); + MOCK_METHOD(Utils::SmallString, moduleName, (QmlDesigner::ModuleId), (const, override)); MOCK_METHOD(std::optional, propertyDeclaration, @@ -331,6 +337,8 @@ public: QmlDesigner::Storage::Info::CommonTypeCache typeCache{*this}; std::map exportedTypeName; + std::map, QmlDesigner::Storage::Info::ExportedTypeNames> + exportedTypeNameBySourceId; }; class ProjectStorageMockWithQtQtuick : public ProjectStorageMock diff --git a/tests/unit/tests/unittests/model/modelutils-test.cpp b/tests/unit/tests/unittests/model/modelutils-test.cpp index c7a70cfbdb3..5a9e63b60da 100644 --- a/tests/unit/tests/unittests/model/modelutils-test.cpp +++ b/tests/unit/tests/unittests/model/modelutils-test.cpp @@ -141,7 +141,7 @@ TEST_F(ModelUtils, find_lowest_common_ancestor_when_one_of_the_nodes_is_parent) ASSERT_THAT(commonAncestor, parentNode); } -TEST_F(ModelUtils, lowest_common_ancestor_for_uncle_and_nephew_should_return_the_grandFather) +TEST_F(ModelUtils, lowest_common_ancestor_for_uncle_and_nephew_should_return_the_grandfather) { auto grandFatherNode = model.createModelNode("Item"); auto fatherNode = model.createModelNode("Item"); From afd1b3b6e95728f6bc1df76d647979a904234c23 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 3 Apr 2024 12:10:16 +0200 Subject: [PATCH 135/202] QmlDesigner: Lookup the default property in prototypes too The default property can be set too in any prototypes. Look them up there too. Because it is expensive we cache them in the node meta info to make hasDefaultProperty() followed by defaultProperty() cheaper. Change-Id: I3b9ec90fc1bc5f0228dad3b580c335734f03821d Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../designercore/include/nodemetainfo.h | 2 + .../designercore/metainfo/nodemetainfo.cpp | 14 +++- .../projectstorage/projectstorage.h | 56 ++++++++++++++-- .../projectstorage/projectstorageinfotypes.h | 18 ++---- .../projectstorage/projectstorageinterface.h | 1 + tests/unit/tests/mocks/projectstoragemock.cpp | 6 +- tests/unit/tests/mocks/projectstoragemock.h | 4 ++ .../tests/printers/gtest-creator-printing.cpp | 2 +- .../unittests/model/nodelistproperty-test.cpp | 4 +- .../projectstorage/projectstorage-test.cpp | 64 ++++++++++++++++--- 10 files changed, 135 insertions(+), 36 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h index 23110175c1b..648c4be9a82 100644 --- a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h +++ b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h @@ -265,12 +265,14 @@ public: private: const Storage::Info::Type &typeData() const; + PropertyDeclarationId defaultPropertyDeclarationId() const; bool isSubclassOf(const TypeName &type, int majorVersion = -1, int minorVersion = -1) const; private: TypeId m_typeId; NotNullPointer m_projectStorage = {}; mutable std::optional m_typeData; + mutable std::optional m_defaultPropertyId; std::shared_ptr m_privateData; }; diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index 845a1db1e52..adea8baf255 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -1835,7 +1835,7 @@ PropertyName NodeMetaInfo::defaultPropertyName() const { if constexpr (useProjectStorage()) { if (isValid()) { - if (auto name = m_projectStorage->propertyName(typeData().defaultPropertyId)) { + if (auto name = m_projectStorage->propertyName(defaultPropertyDeclarationId())) { return name->toQByteArray(); } } @@ -1851,7 +1851,7 @@ PropertyMetaInfo NodeMetaInfo::defaultProperty() const { if constexpr (useProjectStorage()) { if (isValid()) { - return PropertyMetaInfo(typeData().defaultPropertyId, m_projectStorage); + return PropertyMetaInfo(defaultPropertyDeclarationId(), m_projectStorage); } } else { return property(defaultPropertyName()); @@ -1862,7 +1862,7 @@ PropertyMetaInfo NodeMetaInfo::defaultProperty() const bool NodeMetaInfo::hasDefaultProperty() const { if constexpr (useProjectStorage()) - return isValid() && bool(typeData().defaultPropertyId); + return isValid() && bool(defaultPropertyDeclarationId()); else return !defaultPropertyName().isEmpty(); } @@ -2089,6 +2089,14 @@ const Storage::Info::Type &NodeMetaInfo::typeData() const return *m_typeData; } +PropertyDeclarationId NodeMetaInfo::defaultPropertyDeclarationId() const +{ + if (!m_defaultPropertyId) + m_defaultPropertyId = m_projectStorage->defaultPropertyDeclarationId(m_typeId); + + return *m_defaultPropertyId; +} + bool NodeMetaInfo::isSubclassOf(const TypeName &type, int majorVersion, int minorVersion) const { if (!isValid()) { diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index 83e11f952bc..ad606bcb90c 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -408,6 +408,22 @@ public: return propertyDeclarationId; } + PropertyDeclarationId defaultPropertyDeclarationId(TypeId typeId) const override + { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get default property declaration id"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + auto propertyDeclarationId = Sqlite::withDeferredTransaction(database, [&] { + return fetchDefaultPropertyDeclarationId(typeId); + }); + + tracer.end(keyValue("property declaration id", propertyDeclarationId)); + + return propertyDeclarationId; + } + std::optional propertyDeclaration( PropertyDeclarationId propertyDeclarationId) const override { @@ -2379,17 +2395,42 @@ private: return PropertyDeclarationId{}; } - PropertyDeclarationId fetchPropertyDeclarationId(TypeId baseTypeId, + PropertyDeclarationId fetchPropertyDeclarationId(TypeId typeId, Utils::SmallStringView propertyName) const { auto propertyDeclarationId = selectPropertyDeclarationIdByTypeIdAndNameStatement - .template value(baseTypeId, - propertyName); + .template value(typeId, propertyName); if (propertyDeclarationId) return propertyDeclarationId; - return fetchNextPropertyDeclarationId(baseTypeId, propertyName); + return fetchNextPropertyDeclarationId(typeId, propertyName); + } + + PropertyDeclarationId fetchNextDefaultPropertyDeclarationId(TypeId baseTypeId) const + { + auto range = selectPrototypeAndExtensionIdsStatement.template range(baseTypeId); + + for (TypeId prototype : range) { + auto propertyDeclarationId = selectDefaultPropertyDeclarationIdStatement + .template value(prototype); + + if (propertyDeclarationId) + return propertyDeclarationId; + } + + return PropertyDeclarationId{}; + } + + PropertyDeclarationId fetchDefaultPropertyDeclarationId(TypeId typeId) const + { + auto propertyDeclarationId = selectDefaultPropertyDeclarationIdStatement + .template value(typeId); + + if (propertyDeclarationId) + return propertyDeclarationId; + + return fetchNextDefaultPropertyDeclarationId(typeId); } void synchronizePropertyDeclarationsInsertProperty( @@ -4842,9 +4883,10 @@ public: "UPDATE types SET defaultPropertyId=?2 WHERE typeId=?1", database}; WriteStatement<1> updateDefaultPropertyIdToNullStatement{ "UPDATE types SET defaultPropertyId=NULL WHERE defaultPropertyId=?1", database}; - mutable ReadStatement<4, 1> selectInfoTypeByTypeIdStatement{ - "SELECT defaultPropertyId, sourceId, traits, annotationTraits FROM types WHERE typeId=?", - database}; + mutable ReadStatement<3, 1> selectInfoTypeByTypeIdStatement{ + "SELECT sourceId, traits, annotationTraits FROM types WHERE typeId=?", database}; + mutable ReadStatement<1, 1> selectDefaultPropertyDeclarationIdStatement{ + "SELECT defaultPropertyId FROM types WHERE typeId=?", database}; mutable ReadStatement<1, 1> selectPrototypeIdsForTypeIdInOrderStatement{ "WITH RECURSIVE " " all_prototype_and_extension(typeId, prototypeId) AS (" diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h index 3be6fc4cae4..9f0c134ed39 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h @@ -503,18 +503,13 @@ public: class Type { public: - Type(PropertyDeclarationId defaultPropertyId, - SourceId sourceId, - long long typeTraits, - long long typeAnnotationTraits) - : defaultPropertyId{defaultPropertyId} - , sourceId{sourceId} + Type(SourceId sourceId, long long typeTraits, long long typeAnnotationTraits) + : sourceId{sourceId} , traits{typeTraits, typeAnnotationTraits} {} - Type(PropertyDeclarationId defaultPropertyId, SourceId sourceId, TypeTraits traits) - : defaultPropertyId{defaultPropertyId} - , sourceId{sourceId} + Type(SourceId sourceId, TypeTraits traits) + : sourceId{sourceId} , traits{traits} {} @@ -523,14 +518,11 @@ public: { using NanotraceHR::dictonary; using NanotraceHR::keyValue; - auto dict = dictonary(keyValue("default property id", type.defaultPropertyId), - keyValue("source id", type.sourceId), - keyValue("traits", type.traits)); + auto dict = dictonary(keyValue("source id", type.sourceId), keyValue("traits", type.traits)); convertToString(string, dict); } - PropertyDeclarationId defaultPropertyId; SourceId sourceId; TypeTraits traits; }; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h index 13a40695bf9..a67b6296083 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h @@ -55,6 +55,7 @@ public: virtual PropertyDeclarationId propertyDeclarationId(TypeId typeId, ::Utils::SmallStringView propertyName) const = 0; + virtual PropertyDeclarationId defaultPropertyDeclarationId(TypeId typeId) const = 0; virtual std::optional type(TypeId typeId) const = 0; virtual Utils::PathString typeIconPath(TypeId typeId) const = 0; virtual Storage::Info::TypeHints typeHints(TypeId typeId) const = 0; diff --git a/tests/unit/tests/mocks/projectstoragemock.cpp b/tests/unit/tests/mocks/projectstoragemock.cpp index 48357ff3e63..6d5304879e6 100644 --- a/tests/unit/tests/mocks/projectstoragemock.cpp +++ b/tests/unit/tests/mocks/projectstoragemock.cpp @@ -293,8 +293,10 @@ TypeId ProjectStorageMock::createType(ModuleId moduleId, defaultPropertyTypeId); } - ON_CALL(*this, type(Eq(typeId))) - .WillByDefault(Return(Storage::Info::Type{defaultPropertyDeclarationId, sourceId, typeTraits})); + ON_CALL(*this, type(Eq(typeId))).WillByDefault(Return(Storage::Info::Type{sourceId, typeTraits})); + + ON_CALL(*this, defaultPropertyDeclarationId(Eq(typeId))) + .WillByDefault(Return(defaultPropertyDeclarationId)); ON_CALL(*this, isBasedOn(Eq(typeId), Eq(typeId))).WillByDefault(Return(true)); diff --git a/tests/unit/tests/mocks/projectstoragemock.h b/tests/unit/tests/mocks/projectstoragemock.h index a68e37c2a15..b534814c1d3 100644 --- a/tests/unit/tests/mocks/projectstoragemock.h +++ b/tests/unit/tests/mocks/projectstoragemock.h @@ -187,6 +187,10 @@ public: propertyDeclarationId, (QmlDesigner::TypeId typeId, ::Utils::SmallStringView propertyName), (const, override)); + MOCK_METHOD(QmlDesigner::PropertyDeclarationId, + defaultPropertyDeclarationId, + (QmlDesigner::TypeId typeId), + (const, override)); MOCK_METHOD(std::optional, type, (QmlDesigner::TypeId typeId), diff --git a/tests/unit/tests/printers/gtest-creator-printing.cpp b/tests/unit/tests/printers/gtest-creator-printing.cpp index 1bff6df492c..40f9c8db6da 100644 --- a/tests/unit/tests/printers/gtest-creator-printing.cpp +++ b/tests/unit/tests/printers/gtest-creator-printing.cpp @@ -675,7 +675,7 @@ std::ostream &operator<<(std::ostream &out, const PropertyDeclaration &propertyD std::ostream &operator<<(std::ostream &out, const Type &type) { - return out << "(" << type.defaultPropertyId << ")"; + return out << "(" << type.sourceId << ")"; } std::ostream &operator<<(std::ostream &out, const ExportedTypeName &name) diff --git a/tests/unit/tests/unittests/model/nodelistproperty-test.cpp b/tests/unit/tests/unittests/model/nodelistproperty-test.cpp index 6783bde3e75..aac2e729a29 100644 --- a/tests/unit/tests/unittests/model/nodelistproperty-test.cpp +++ b/tests/unit/tests/unittests/model/nodelistproperty-test.cpp @@ -61,8 +61,8 @@ protected: ++defaultPropertyIdNumber); ON_CALL(projectStorageMock, typeId(Eq(moduleId), Eq(typeName), _)).WillByDefault(Return(typeId)); - ON_CALL(projectStorageMock, type(Eq(typeId))) - .WillByDefault(Return(Info::Type{defaultPropertyId, QmlDesigner::SourceId{}, {}})); + ON_CALL(projectStorageMock, defaultPropertyDeclarationId(Eq(typeId))) + .WillByDefault(Return(defaultPropertyId)); ON_CALL(projectStorageMock, propertyName(Eq(defaultPropertyId))) .WillByDefault(Return(defaultPeopertyName)); } diff --git a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp index 4b3f0a18692..125e8df3aeb 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp @@ -253,17 +253,15 @@ MATCHER(StringsAreSorted, std::string(negation ? "isn't sorted" : "is sorted")) }); } -MATCHER_P3(IsInfoType, - defaultPropertyId, +MATCHER_P2(IsInfoType, sourceId, traits, std::string(negation ? "isn't " : "is ") - + PrintToString(Storage::Info::Type{defaultPropertyId, sourceId, traits})) + + PrintToString(Storage::Info::Type{sourceId, traits})) { const Storage::Info::Type &type = arg; - return type.defaultPropertyId == defaultPropertyId && type.sourceId == sourceId - && type.traits == traits; + return type.sourceId == sourceId && type.traits == traits; } class ProjectStorage : public testing::Test @@ -6683,12 +6681,10 @@ TEST_F(ProjectStorage, get_type) auto package{createSimpleSynchronizationPackage()}; storage.synchronize(package); auto typeId = fetchTypeId(sourceId1, "QQuickItem"); - auto defaultPropertyName = storage.fetchTypeByTypeId(typeId).defaultPropertyName; - auto defaultPropertyId = storage.propertyDeclarationId(typeId, defaultPropertyName); auto type = storage.type(typeId); - ASSERT_THAT(type, Optional(IsInfoType(defaultPropertyId, sourceId1, TypeTraitsKind::Reference))); + ASSERT_THAT(type, Optional(IsInfoType(sourceId1, TypeTraitsKind::Reference))); } TEST_F(ProjectStorage, dont_get_type_for_invalid_id) @@ -6701,6 +6697,58 @@ TEST_F(ProjectStorage, dont_get_type_for_invalid_id) ASSERT_THAT(type, Eq(std::nullopt)); } +TEST_F(ProjectStorage, get_default_property_declarartion_id) +{ + auto package{createSimpleSynchronizationPackage()}; + storage.synchronize(package); + auto typeId = fetchTypeId(sourceId1, "QQuickItem"); + auto defaultPropertyName = storage.fetchTypeByTypeId(typeId).defaultPropertyName; + auto defaultPropertyId = storage.propertyDeclarationId(typeId, defaultPropertyName); + + auto propertyId = storage.defaultPropertyDeclarationId(typeId); + + ASSERT_THAT(propertyId, defaultPropertyId); +} + +TEST_F(ProjectStorage, get_default_property_declarartion_id_in_base_type) +{ + auto package{createSynchronizationPackageWithAliases()}; + storage.synchronize(package); + auto baseTypeId = fetchTypeId(sourceId1, "QQuickItem"); + auto defaultPropertyName = storage.fetchTypeByTypeId(baseTypeId).defaultPropertyName; + auto defaultPropertyId = storage.propertyDeclarationId(baseTypeId, defaultPropertyName); + auto typeId = fetchTypeId(sourceId3, "QAliasItem"); + + auto propertyId = storage.defaultPropertyDeclarationId(typeId); + + ASSERT_THAT(propertyId, defaultPropertyId); +} + +TEST_F(ProjectStorage, do_not_get_default_property_declarartion_id_wrong_type_in_property_chain) +{ + auto package{createSynchronizationPackageWithAliases()}; + package.types[1].defaultPropertyName = "objects"; + storage.synchronize(package); + auto baseTypeId = fetchTypeId(sourceId1, "QQuickItem"); + auto defaultPropertyName = storage.fetchTypeByTypeId(baseTypeId).defaultPropertyName; + auto defaultPropertyId = storage.propertyDeclarationId(baseTypeId, defaultPropertyName); + auto typeId = fetchTypeId(sourceId3, "QAliasItem"); + + auto propertyId = storage.defaultPropertyDeclarationId(typeId); + + ASSERT_THAT(propertyId, defaultPropertyId); +} + +TEST_F(ProjectStorage, get_invalid_default_property_declarartion_id_for_invalid_type) +{ + auto package{createSimpleSynchronizationPackage()}; + storage.synchronize(package); + + auto propertyId = storage.defaultPropertyDeclarationId(TypeId()); + + ASSERT_FALSE(propertyId); +} + TEST_F(ProjectStorage, get_common_type) { auto package{createSimpleSynchronizationPackage()}; From 665d043e7244028ef766c3baf889179a793b2619 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 3 Apr 2024 12:48:37 +0200 Subject: [PATCH 136/202] QmlDesigner: Extract default property in qmltypes parser Change-Id: I0cc9ac71ba764db252e302d11d1cd3e4aff834f5 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- .../designercore/metainfo/nodemetainfo.cpp | 2 +- .../designercore/projectstorage/qmltypesparser.cpp | 4 +++- .../projectstorage/qmltypesparser-test.cpp | 13 +++++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index adea8baf255..0d82a975c22 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -2092,7 +2092,7 @@ const Storage::Info::Type &NodeMetaInfo::typeData() const PropertyDeclarationId NodeMetaInfo::defaultPropertyDeclarationId() const { if (!m_defaultPropertyId) - m_defaultPropertyId = m_projectStorage->defaultPropertyDeclarationId(m_typeId); + m_defaultPropertyId.emplace(m_projectStorage->defaultPropertyDeclarationId(m_typeId)); return *m_defaultPropertyId; } diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp index 8179ff625b8..776e6eb97a8 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp @@ -458,7 +458,9 @@ void addType(Storage::Synchronization::Types &types, createProperties(component.ownProperties(), enumerationTypes, componentNameWithoutNamespace), std::move(functionsDeclarations), std::move(signalDeclarations), - createEnumeration(enumerations)); + createEnumeration(enumerations), + Storage::Synchronization::ChangeLevel::Full, + Utils::SmallString{component.ownDefaultPropertyName()}); tracer.end(keyValue("type", type)); } diff --git a/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp b/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp index 78f8f41d6c4..a6b85404364 100644 --- a/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp @@ -838,4 +838,17 @@ TEST_F(QmlTypesParser, uses_no_custom_parser) ASSERT_THAT(types, ElementsAre(IsTypeTrait(UsesCustomParser(false)))); } +TEST_F(QmlTypesParser, default_property) +{ + QString source{R"(import QtQuick.tooling 1.2 + Module{ + Component { name: "QObject" + defaultProperty: "children" }})"}; + + parser.parse(source, imports, types, projectData); + + ASSERT_THAT(types, + ElementsAre(Field(&Synchronization::Type::defaultPropertyName, Eq("children")))); +} + } // namespace From 316d88bd1280693a9d1b16e4844819394a0292a6 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 3 Apr 2024 12:52:14 +0200 Subject: [PATCH 137/202] QmlDesigner: Extract default property in qml document parser Change-Id: I2b3576312cc2bec0c2e103fd3f342a5fe059b10c Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../projectstorage/qmldocumentparser.cpp | 2 +- .../projectstorage/qmldocumentparser-test.cpp | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp index daa9062a571..27efa8d530d 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp @@ -353,7 +353,7 @@ Storage::Synchronization::Type QmlDocumentParser::parse(const QString &sourceCon m_storage); type.prototype = createImportedTypeName(qmlObject.name(), qualifiedImports); - + type.defaultPropertyName = qmlObject.localDefaultPropertyName(); addImports(imports, qmlFile->imports(), sourceId, directoryPath, m_storage); addPropertyDeclarations(type, qmlObject, qualifiedImports, file); diff --git a/tests/unit/tests/unittests/projectstorage/qmldocumentparser-test.cpp b/tests/unit/tests/unittests/projectstorage/qmldocumentparser-test.cpp index f39dec121c6..affa645330e 100644 --- a/tests/unit/tests/unittests/projectstorage/qmldocumentparser-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/qmldocumentparser-test.cpp @@ -516,4 +516,16 @@ TEST_F(QmlDocumentParser, qualified_list_property) Storage::PropertyDeclarationTraits::IsList))); } +TEST_F(QmlDocumentParser, default_property) +{ + auto type = parser.parse(R"(import Example 2.1 as Example + Item{ + default property list foos + })", + imports, + qmlFileSourceId, + directoryPath); + + ASSERT_THAT(type.defaultPropertyName, Eq("foos")); +} } // namespace From 7883f152999f41b78ed0b2cdde2548aa63c53db9 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 3 Apr 2024 13:34:59 +0200 Subject: [PATCH 138/202] QmlDesigner: Fix component file path Change-Id: I9e5f5dc4906c9c498e4a54f92a7d8a105e0399a1 Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../components/edit3d/bakelightsdatamodel.cpp | 4 +++- .../designercore/include/nodemetainfo.h | 4 +++- .../designercore/model/modelutils.cpp | 16 ++++++++-------- src/plugins/qmldesigner/documentmanager.cpp | 2 +- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp b/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp index 95a260c26fd..a1dfcc8f981 100644 --- a/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp +++ b/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp @@ -15,6 +15,8 @@ #include "qmlobjectnode.h" #include "variantproperty.h" +#include + #include #include @@ -292,7 +294,7 @@ bool BakeLightsDataModel::reset() if (!hasExposedProps && node.metaInfo().isFileComponent() && node.metaInfo().isQtQuick3DNode()) { - const QString compFile = node.metaInfo().componentFileName(); + const QString compFile = ModelUtils::componentFilePath(node); 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 diff --git a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h index 648c4be9a82..ba2e2cda65e 100644 --- a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h +++ b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h @@ -27,9 +27,11 @@ QT_END_NAMESPACE # define DEPRECATED_VERSION_NUMBER \ [[deprecated( \ "In most cases you don't need them anymore because the import is setting them!")]] +# define DEPRECATED_COMPONENT_FILE_NAME [[deprecated("Use sourceId() instead.")]] #else # define DEPRECATED_TYPENAME # define DEPRECATED_VERSION_NUMBER +# define DEPRECATED_COMPONENT_FILE_NAME #endif namespace QmlDesigner { @@ -116,7 +118,7 @@ public: Storage::Info::ItemLibraryEntries itemLibrariesEntries() const; SourceId sourceId() const; - QString componentFileName() const; + DEPRECATED_COMPONENT_FILE_NAME QString componentFileName() const; bool isBasedOn(const NodeMetaInfo &metaInfo) const; bool isBasedOn(const NodeMetaInfo &metaInfo1, const NodeMetaInfo &metaInfo2) const; diff --git a/src/plugins/qmldesigner/designercore/model/modelutils.cpp b/src/plugins/qmldesigner/designercore/model/modelutils.cpp index 3dd7fe6c32a..6c3e1ea50f5 100644 --- a/src/plugins/qmldesigner/designercore/model/modelutils.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelutils.cpp @@ -109,19 +109,19 @@ PropertyMetaInfo metainfo(const ModelNode &node, const PropertyName &propertyNam return node.metaInfo().property(propertyName); } -QString componentFilePath(const PathCacheType &pathCache, const NodeMetaInfo &metaInfo) +QString componentFilePath([[maybe_unused]] const PathCacheType &pathCache, const NodeMetaInfo &metaInfo) { - if constexpr (useProjectStorage()) { - auto typeSourceId = metaInfo.sourceId(); +#ifdef QDS_USE_PROJECTSTORAGE + auto typeSourceId = metaInfo.sourceId(); - if (typeSourceId && metaInfo.isFileComponent()) { - return pathCache.sourcePath(typeSourceId).toQString(); - } - } else { - return metaInfo.componentFileName(); + if (typeSourceId && metaInfo.isFileComponent()) { + return pathCache.sourcePath(typeSourceId).toQString(); } return {}; +#else + return metaInfo.componentFileName(); +#endif } QString componentFilePath(const ModelNode &node) diff --git a/src/plugins/qmldesigner/documentmanager.cpp b/src/plugins/qmldesigner/documentmanager.cpp index b94de22c289..6d4deb527aa 100644 --- a/src/plugins/qmldesigner/documentmanager.cpp +++ b/src/plugins/qmldesigner/documentmanager.cpp @@ -130,7 +130,7 @@ static void openComponentSourcePropertyOfLoader(const ModelNode &modelNode) } Core::EditorManager::openEditor(FilePath::fromString( - componentModelNode.metaInfo().componentFileName()), + ModelUtils::componentFilePath(componentModelNode)), Utils::Id(), Core::EditorManager::DoNotMakeVisible); } From 047b2ef361b8be257d64a0e1f99dec38cc972ba5 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 3 Apr 2024 16:11:57 +0200 Subject: [PATCH 139/202] QmlDesigner: More unique ptr Change-Id: Iabce3eb5eb8832c0edf8864adeb7d7a56d965a3a Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- .../components/itemlibrary/itemlibrarywidget.cpp | 12 ++++++------ .../components/itemlibrary/itemlibrarywidget.h | 11 ++++++----- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp index b710a8226f2..587489bc8ca 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp @@ -118,9 +118,9 @@ void ItemLibraryWidget::resizeEvent(QResizeEvent *event) ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache) : m_itemIconSize(24, 24) - , m_itemLibraryModel(new ItemLibraryModel(this)) - , m_addModuleModel(new ItemLibraryAddImportModel(this)) - , m_itemsWidget(new StudioQuickWidget(this)) + , m_itemLibraryModel(std::make_unique()) + , m_addModuleModel(std::make_unique()) + , m_itemsWidget(Utils::makeUniqueObjectPtr()) , m_imageCache{imageCache} { m_compressionTimer.setInterval(1000); @@ -146,7 +146,7 @@ ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache) auto layout = new QVBoxLayout(this); layout->setContentsMargins({}); layout->setSpacing(0); - layout->addWidget(m_itemsWidget.data()); + layout->addWidget(m_itemsWidget.get()); updateSearch(); @@ -167,8 +167,8 @@ ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache) auto map = m_itemsWidget->registerPropertyMap("ItemLibraryBackend"); - map->setProperties({{"itemLibraryModel", QVariant::fromValue(m_itemLibraryModel.data())}, - {"addModuleModel", QVariant::fromValue(m_addModuleModel.data())}, + map->setProperties({{"itemLibraryModel", QVariant::fromValue(m_itemLibraryModel.get())}, + {"addModuleModel", QVariant::fromValue(m_addModuleModel.get())}, {"itemLibraryIconWidth", m_itemIconSize.width()}, {"itemLibraryIconHeight", m_itemIconSize.height()}, {"rootView", QVariant::fromValue(this)}, diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h index b56532b2185..2940db7a73e 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h @@ -10,9 +10,10 @@ #include -#include -#include #include +#include +#include +#include #include #include @@ -104,10 +105,10 @@ private: #ifndef QDS_USE_PROJECTSTORAGE QPointer m_itemLibraryInfo; #endif - QPointer m_itemLibraryModel; - QPointer m_addModuleModel; + std::unique_ptr m_itemLibraryModel; + std::unique_ptr m_addModuleModel; - QScopedPointer m_itemsWidget; + Utils::UniqueObjectPtr m_itemsWidget; std::unique_ptr m_previewTooltipBackend; QShortcut *m_qmlSourceUpdateShortcut; From b01522f102dea1d37a5f8cfd1d60db7e1e0e68a6 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 9 Apr 2024 19:11:21 +0200 Subject: [PATCH 140/202] QmlDesigner: Skip QQuickItem in QtQuick.Templates Otherwise we have to QQuickItem meta info objects and that creates bugs. Fixes: QDS-12460 Change-Id: Iaf76df6671c4ca73913285b78bf1d6408fbe93be Reviewed-by: Fabian Kosmale Reviewed-by: Reviewed-by: Qt CI Patch Build Bot --- .../projectstorage/qmltypesparser.cpp | 35 ++++++++++++++++++- .../projectstorage/qmltypesparser-test.cpp | 22 ++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp index 776e6eb97a8..10363150dc1 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp @@ -464,6 +464,33 @@ void addType(Storage::Synchronization::Types &types, tracer.end(keyValue("type", type)); } +using namespace Qt::StringLiterals; + +constexpr auto skipLists = std::make_tuple( + std::pair{"QtQuick.Templates-cppnative"sv, std::array{"QQuickItem"_L1}}); + +std::span getSkipList(std::string_view moduleName) +{ + static constexpr std::span emptySkipList; + auto currentSkipList = emptySkipList; + + std::apply( + [&](const auto &entry) { + if (entry.first == moduleName) + currentSkipList = entry.second; + }, + skipLists); + + return currentSkipList; +} + +bool skipType(const QQmlJSExportedScope &object, std::span skipList) +{ + return std::any_of(skipList.begin(), skipList.end(), [&](const QLatin1StringView skip) { + return object.scope->internalName() == skip; + }); +} + void addTypes(Storage::Synchronization::Types &types, const Storage::Synchronization::ProjectData &projectData, const QList &objects, @@ -473,13 +500,19 @@ void addTypes(Storage::Synchronization::Types &types, NanotraceHR::Tracer tracer{"add types"_t, category()}; types.reserve(Utils::usize(objects) + types.size()); - for (const auto &object : objects) + const auto skipList = getSkipList(storage.moduleName(projectData.moduleId)); + + for (const auto &object : objects) { + if (skipType(object, skipList)) + continue; + addType(types, projectData.sourceId, projectData.moduleId, object, storage, componentNameWithoutNamespaces); + } } } // namespace diff --git a/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp b/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp index a6b85404364..a42a560d07d 100644 --- a/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp @@ -851,4 +851,26 @@ TEST_F(QmlTypesParser, default_property) ElementsAre(Field(&Synchronization::Type::defaultPropertyName, Eq("children")))); } +TEST_F(QmlTypesParser, skip_template_item) +{ + ModuleId moduleId = storage.moduleId("QtQuick.Templates-cppnative"); + Synchronization::ProjectData projectData{qmltypesFileSourceId, + qmltypesFileSourceId, + moduleId, + Synchronization::FileType::QmlTypes}; + QString source{R"(import QtQuick.tooling 1.2 + Module{ + Component { name: "QQuickItem"} + Component { name: "QQmlComponent"}})"}; + + parser.parse(source, imports, types, projectData); + + ASSERT_THAT(types, + UnorderedElementsAre(IsType("QQmlComponent", + Synchronization::ImportedType{}, + Synchronization::ImportedType{}, + Storage::TypeTraitsKind::Reference, + qmltypesFileSourceId))); +} + } // namespace From e5c50c6dea2605beeda330715241007070af89cd Mon Sep 17 00:00:00 2001 From: Shrief Gabr Date: Mon, 15 Apr 2024 12:36:24 +0300 Subject: [PATCH 141/202] QmlDesigner: Change default view3D id in Extended 3D projects Task-number: QDS-12351 Change-Id: Ib3ecb8e2f6ca1561819f19001d851b0932e9583f Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../projects/application-extended-3d/Screen01.ui.qml.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/Screen01.ui.qml.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/Screen01.ui.qml.tpl index 2b26fe3f0ce..41d562fb53e 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/Screen01.ui.qml.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/Screen01.ui.qml.tpl @@ -19,7 +19,7 @@ Rectangle { color: Constants.backgroundColor View3D { - id: view3D + id: extendedView3D anchors.fill: parent environment: sceneEnvironment From 401bbae862ae45440580ca7107ac55881aafaf42 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 15 Apr 2024 12:33:51 +0300 Subject: [PATCH 142/202] QmlDesigner: Remove zoom level from affecting edit camera movement When moving the edit camera with keyboard, zoom level no longer is used as multiplier for the movement. Fixes: QDS-12274 Change-Id: I59c903b73fef095e619cccc36868605b47f9ec83 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml | 3 +-- src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp | 5 +++-- src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml index 446d5756838..a76a1cdf08c 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml @@ -158,8 +158,7 @@ Item { function moveCamera(moveVec) { - cameraCtrl._lookAtPoint = _generalHelper.moveCamera(camera, _lookAtPoint, _zoomFactor, - moveVec); + cameraCtrl._lookAtPoint = _generalHelper.moveCamera(camera, _lookAtPoint, moveVec); } function getMoveVectorForKey(key) { diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp index 0fb708c6ff3..4b47179915e 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp @@ -157,7 +157,7 @@ QVector3D GeneralHelper::panCamera(QQuick3DCamera *camera, const QMatrix4x4 star // Moves camera in 3D space and returns new look-at point QVector3D GeneralHelper::moveCamera(QQuick3DCamera *camera, const QVector3D &startLookAt, - float zoomFactor, const QVector3D &moveVector) + const QVector3D &moveVector) { if (moveVector.length() < 0.001f) @@ -171,7 +171,8 @@ QVector3D GeneralHelper::moveCamera(QQuick3DCamera *camera, const QVector3D &sta const QVector3D xDelta = xAxis * moveVector.x(); const QVector3D yDelta = yAxis * moveVector.y(); const QVector3D zDelta = zAxis * moveVector.z(); - const QVector3D delta = (yDelta - xDelta - zDelta) * zoomFactor; + // Delta multiplier for nice default speed in default scene + const QVector3D delta = (yDelta - xDelta - zDelta) * .5f; camera->setPosition(camera->position() + delta); diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h index 3ba6f8b1d9f..781a19dbff5 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h @@ -55,7 +55,7 @@ public: const QVector3D &pressPos, const QVector3D ¤tPos, float zoomFactor); Q_INVOKABLE QVector3D moveCamera(QQuick3DCamera *camera,const QVector3D &startLookAt, - float zoomFactor, const QVector3D &moveVector); + const QVector3D &moveVector); Q_INVOKABLE QVector3D rotateCamera(QQuick3DCamera *camera, const QPointF &angles, const QVector3D &lookAtPoint); From 8694de43c054bb1a3fc524fe1daad57ddd5bcc93 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 15 Apr 2024 15:41:25 +0200 Subject: [PATCH 143/202] QmlDesigner: Fix project storage build Change-Id: Id734a9618103e61778a772eeb460c7eeb031f076 Reviewed-by: Tim Jenssen --- src/plugins/qmldesigner/components/edit3d/edit3dview.cpp | 8 ++++++-- .../qmldesigner/components/edit3d/edit3dwidget.cpp | 9 +++++---- .../components/propertyeditor/qmlmodelnodeproxy.cpp | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index 4a05c1bc5a7..e560ed16ac5 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -356,8 +356,12 @@ void Edit3DView::handleEntriesChanged() append(model()->qtQuick3DOrthographicCameraMetaInfo(), EK_cameras); append(model()->qtQuick3DPerspectiveCameraMetaInfo(), EK_cameras); - auto assetsModule = model()->module(QmlDesignerPlugin::instance()->documentManager() - .generatedComponentUtils().import3dTypePrefix()); + Utils::PathString import3dTypePrefix = QmlDesignerPlugin::instance() + ->documentManager() + .generatedComponentUtils() + .import3dTypePrefix(); + + auto assetsModule = model()->module(import3dTypePrefix); for (const auto &metaInfo : model()->metaInfosForModule(assetsModule)) append(metaInfo, EK_importedModels); diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index 033cc627701..6f1cf2e1837 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -766,10 +766,11 @@ void Edit3DWidget::dropEvent(QDropEvent *dropEvent) QString fileName = QFileInfo(assetPath).baseName(); fileName = fileName.at(0).toUpper() + fileName.mid(1); // capitalize first letter auto model = m_view->model(); - auto metaInfo = model->metaInfo(model->module( - QmlDesignerPlugin::instance()->documentManager() - .generatedComponentUtils().import3dTypePrefix()), - fileName.toUtf8()); + Utils::PathString import3dTypePrefix = QmlDesignerPlugin::instance() + ->documentManager() + .generatedComponentUtils() + .import3dTypePrefix(); + auto metaInfo = model->metaInfo(model->module(import3dTypePrefix), fileName.toUtf8()); if (auto entries = metaInfo.itemLibrariesEntries(); entries.size()) { auto entry = ItemLibraryEntry{entries.front(), *model->projectStorage()}; QmlVisualNode::createQml3DNode(view(), entry, m_canvas->activeScene(), {}, false); diff --git a/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp b/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp index 225e1d6c1bc..b16e7c258f6 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp @@ -182,7 +182,7 @@ void QmlModelNodeProxy::createModelNode(int internalIdParent, view->model()->changeImports({import}, {}); #ifdef QDS_USE_PROJECTSTORAGE - ModelNode newNode = view->createModelNode(type); + ModelNode newNode = view->createModelNode(typeName.toUtf8()); #else NodeMetaInfo metaInfo = modelNode.model()->metaInfo(typeName.toUtf8()); ModelNode newNode = view->createModelNode(metaInfo.typeName(), From 3aec095e50a02ea62b319e52b0bf4cc9f0f81788 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 15 Apr 2024 15:02:10 +0200 Subject: [PATCH 144/202] QmlDesigner: Fix crash Change-Id: I0b2080c19868efd92427f47bd344412e6460e126 Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- .../components/propertyeditor/qmlmodelnodeproxy.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp b/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp index b16e7c258f6..3daa6c4ecce 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp @@ -246,7 +246,7 @@ void QmlModelNodeProxy::handleInstancePropertyChanged(const ModelNode &modelNode const QmlObjectNode qmlObjectNode(modelNode); for (const auto &item : qAsConst(m_subselection)) { - if (item->isRelevantModelNode(modelNode)) { + if (item && item->isRelevantModelNode(modelNode)) { if (!modelNode.hasProperty(propertyName) || modelNode.property(propertyName).isBindingProperty()) { item->setValueFromModel(propertyName, qmlObjectNode.instanceValue(propertyName)); @@ -260,7 +260,7 @@ void QmlModelNodeProxy::handleInstancePropertyChanged(const ModelNode &modelNode void QmlModelNodeProxy::handleBindingPropertyChanged(const BindingProperty &property) { for (const auto &item : qAsConst(m_subselection)) { - if (item->isRelevantModelNode(property.parentModelNode())) { + if (item && item->isRelevantModelNode(property.parentModelNode())) { QmlObjectNode objectNode(item->modelNode()); if (objectNode.modelNode().property(property.name()).isBindingProperty()) item->setValueFromModel(property.name(), objectNode.instanceValue(property.name())); @@ -273,7 +273,7 @@ void QmlModelNodeProxy::handleBindingPropertyChanged(const BindingProperty &prop void QmlModelNodeProxy::handleVariantPropertyChanged(const VariantProperty &property) { for (const auto &item : qAsConst(m_subselection)) { - if (item->isRelevantModelNode(property.parentModelNode())) { + if (item && item->isRelevantModelNode(property.parentModelNode())) { QmlObjectNode objectNode(item->modelNode()); if (objectNode.modelNode().property(property.name()).isBindingProperty()) item->setValueFromModel(property.name(), objectNode.instanceValue(property.name())); @@ -286,7 +286,7 @@ void QmlModelNodeProxy::handleVariantPropertyChanged(const VariantProperty &prop void QmlModelNodeProxy::handlePropertiesRemoved(const AbstractProperty &property) { for (const auto &item : qAsConst(m_subselection)) { - if (item->isRelevantModelNode(property.parentModelNode())) { + if (item && item->isRelevantModelNode(property.parentModelNode())) { QmlObjectNode objectNode(item->modelNode()); item->resetValue(property.name()); item->setValueFromModel(property.name(), objectNode.instanceValue(property.name())); From 4b0f31e823723e4c10d9eb261e3e0fb732a9e712 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 15 Apr 2024 15:03:19 +0200 Subject: [PATCH 145/202] QmlDesigner: Fix crash on shutdown We have to set the correct onwership. For objects from C++ QJSEngine::CppOwnership is the default, unless the object comes from a function like here. Change-Id: Idc35e2b06656da228a55d78e5b41f84795a8aa6e Reviewed-by: Miikka Heikkinen Reviewed-by: Tim Jenssen --- .../qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp b/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp index 3daa6c4ecce..fc39b371375 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp @@ -151,6 +151,8 @@ PropertyEditorSubSelectionWrapper *QmlModelNodeProxy::registerSubSelectionWrappe new PropertyEditorSubSelectionWrapper(node)); m_subselection.append(wrapper); + QJSEngine::setObjectOwnership(wrapper.data(), QJSEngine::CppOwnership); + return wrapper.data(); } From 3c13cf9493c250876bf91bb8f45255b7ef929ec0 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 15 Apr 2024 11:58:58 +0200 Subject: [PATCH 146/202] QmlDesigner: Use the same location on macOS Task-number: QDS-9979 Change-Id: I6a706b5325fd74221523cab4f2be838bdd871592 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: 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 da4ce8ce082..f938c58e04c 100644 --- a/src/plugins/qmldesignerbase/utils/designerpaths.cpp +++ b/src/plugins/qmldesignerbase/utils/designerpaths.cpp @@ -20,9 +20,7 @@ Utils::FilePath defaultExamplesPath() Utils::FilePath defaultBundlesPath() { - QStandardPaths::StandardLocation location = Utils::HostOsInfo::isMacHost() - ? QStandardPaths::HomeLocation - : QStandardPaths::DocumentsLocation; + QStandardPaths::StandardLocation location = QStandardPaths::DocumentsLocation; return Utils::FilePath::fromString(QStandardPaths::writableLocation(location)) .pathAppended("QtDesignStudio/bundles"); From 21f6cbe4e42116d20f52cf35150eb66d5cb29b72 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 15 Apr 2024 17:06:17 +0200 Subject: [PATCH 147/202] QmlDesigner: Adjust paths for new project structure Before we used "content" but in the new project structure we will use "ProjectNameContent" to make the uri unique. First check for "content" to support older project. Keeping only the implementation in DocumentManager::currentResourcePath() Change-Id: Ib6b21f52c078fdb9a2ff31c02855754f83741538 Reviewed-by: Miikka Heikkinen --- .../components/componentcore/modelnodeoperations.cpp | 5 +---- src/plugins/qmldesigner/documentmanager.cpp | 7 +++++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index 8b41cfb6929..66c1229a76e 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -1138,10 +1138,7 @@ static QString getAssetDefaultDirectory(const QString &assetDir, const QString & { QString adjustedDefaultDirectory = defaultDirectory; - Utils::FilePath contentPath = QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath(); - - if (contentPath.pathAppended("content").exists()) - contentPath = contentPath.pathAppended("content"); + Utils::FilePath contentPath = QmlDesignerPlugin::instance()->documentManager().currentResourcePath(); Utils::FilePath assetPath = contentPath.pathAppended(assetDir); diff --git a/src/plugins/qmldesigner/documentmanager.cpp b/src/plugins/qmldesigner/documentmanager.cpp index 6d4deb527aa..92d80680a94 100644 --- a/src/plugins/qmldesigner/documentmanager.cpp +++ b/src/plugins/qmldesigner/documentmanager.cpp @@ -544,6 +544,13 @@ Utils::FilePath DocumentManager::currentResourcePath() if (contentFilePath.exists()) return contentFilePath; + const auto project = ProjectManager::startupProject(); + const QString baseName = project->rootProjectDirectory().baseName() + "Content"; + + contentFilePath = resourcePath.pathAppended(baseName); + if (contentFilePath.exists()) + return contentFilePath; + return resourcePath; } From e8a47b1b8ac9aad17120675fc04a0e2deb7643fe Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Mon, 15 Apr 2024 18:09:42 +0200 Subject: [PATCH 148/202] QmlDesigner: Fix Properties Section for Effects Change-Id: I249723f45b83731232acc9e68a6c4e27b85d6d11 Reviewed-by: Thomas Hartmann --- .../propertyEditorQmlSources/QtQuick/EffectsSection.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml index 769cb679d59..2693fc3ef60 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml @@ -259,7 +259,7 @@ Section { + StudioTheme.Values.actionIndicatorWidth width: implicitWidth typeFilter: "QtQuick.Item" - backendValue: root.effectNodeWrapper.properties.background + backendValue: root.effectNodeWrapper.properties.backgroundLayer } ExpandingSpacer {} From 4ee7b297ced1feb4d93275cbeb2b941f974d313b Mon Sep 17 00:00:00 2001 From: Knud Dollereder Date: Fri, 12 Apr 2024 15:36:28 +0200 Subject: [PATCH 149/202] QmlDesigner: Adapt wizards to the new project structure and removed some unused templates Change-Id: I7e3e4a94ef31cbf5c98a0c1ad26dcaaa340c1e97 Reviewed-by: Miikka Heikkinen --- .../projects/application-3d/wizard.json | 71 +++------------- .../application-extended-3d/wizard.json | 71 +++------------- .../projects/application/wizard.json | 82 +++---------------- .../common/CMakeLists.content.txt.tpl | 14 ---- .../common/CMakeLists.imports.txt.tpl | 4 - .../projects/common/CMakeLists.main.txt.tpl | 56 ------------- .../projects/common/app.qmlproject.tpl | 35 ++++---- .../projects/common/app_environment.h.tpl | 19 ----- .../common/contentmodule.main.qml.tpl | 17 ---- .../import_qml_components_plugins.h.tpl | 19 ----- .../projects/common/import_qml_plugins.h.tpl | 9 -- .../projects/common/insight.tpl | 19 ----- .../projects/common/main.cpp.tpl | 37 --------- .../studio_templates/projects/common/main.qml | 8 -- .../projects/common/qmlcomponents.tpl | 34 -------- .../projects/common/qmlmodules.tpl | 18 ---- .../projects/common/qtquickcontrols2.conf | 23 ------ .../projects/desktop-launcher/wizard.json | 70 +++------------- .../projects/mobile-scroll/wizard.json | 70 +++------------- .../projects/mobile-stack/wizard.json | 72 +++------------- .../projects/mobile-swipe/wizard.json | 72 +++------------- .../shared-plugin/name/Constants.qml.tpl | 2 +- .../name/DirectoryFontLoader.qml.tpl | 2 +- 23 files changed, 105 insertions(+), 719 deletions(-) delete mode 100644 share/qtcreator/qmldesigner/studio_templates/projects/common/CMakeLists.content.txt.tpl delete mode 100644 share/qtcreator/qmldesigner/studio_templates/projects/common/CMakeLists.imports.txt.tpl delete mode 100644 share/qtcreator/qmldesigner/studio_templates/projects/common/CMakeLists.main.txt.tpl delete mode 100644 share/qtcreator/qmldesigner/studio_templates/projects/common/app_environment.h.tpl delete mode 100644 share/qtcreator/qmldesigner/studio_templates/projects/common/contentmodule.main.qml.tpl delete mode 100644 share/qtcreator/qmldesigner/studio_templates/projects/common/import_qml_components_plugins.h.tpl delete mode 100644 share/qtcreator/qmldesigner/studio_templates/projects/common/import_qml_plugins.h.tpl delete mode 100644 share/qtcreator/qmldesigner/studio_templates/projects/common/insight.tpl delete mode 100644 share/qtcreator/qmldesigner/studio_templates/projects/common/main.cpp.tpl delete mode 100644 share/qtcreator/qmldesigner/studio_templates/projects/common/main.qml delete mode 100644 share/qtcreator/qmldesigner/studio_templates/projects/common/qmlcomponents.tpl delete mode 100644 share/qtcreator/qmldesigner/studio_templates/projects/common/qmlmodules.tpl delete mode 100644 share/qtcreator/qmldesigner/studio_templates/projects/common/qtquickcontrols2.conf 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 cb885c55e33..a5d0d7539c0 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json @@ -18,6 +18,8 @@ { "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" }, { "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" }, { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') !== '2.15' }" }, + { "key": "AssetDir", "value": "GeneratedComponents" }, + { "key": "ContentDir", "value": "%{ProjectName}Content" }, { "key": "ImportModuleName", "value": "%{ProjectName}" }, { "key": "UIClassName", "value": "Screen01" }, { "key": "UIClassFileName", "value": "%{JS: Util.fileName('%{UIClassName}', 'ui.qml')}" }, @@ -305,99 +307,50 @@ "target": "%{ProjectDirectory}/%{QmlProjectFileName}", "openAsProject": true }, - { - "source": "../common/CMakeLists.main.txt.tpl", - "target": "%{ProjectDirectory}/CMakeLists.txt" - }, - { - "source": "../common/qmlmodules.tpl", - "target": "%{ProjectDirectory}/qmlmodules" - }, - { - "source": "../common/qmlcomponents.tpl", - "target": "%{ProjectDirectory}/qmlcomponents" - }, - { - "source": "../common/insight.tpl", - "target": "%{ProjectDirectory}/insight" - }, - { - "source": "../common/main.qml", - "target": "%{ProjectDirectory}/main.qml" - }, { "source": "../common/qtquickcontrols2.conf.tpl", "target": "%{ProjectDirectory}/qtquickcontrols2.conf" }, - { - "source": "../common/main.cpp.tpl", - "target": "%{ProjectDirectory}/src/main.cpp" - }, - { - "source": "../common/app_environment.h.tpl", - "target": "%{ProjectDirectory}/src/app_environment.h" - }, - { - "source": "../common/import_qml_plugins.h.tpl", - "target": "%{ProjectDirectory}/src/import_qml_plugins.h" - }, - { - "source": "../common/import_qml_components_plugins.h.tpl", - "target": "%{ProjectDirectory}/src/import_qml_components_plugins.h" - }, - - { - "source": "../common/CMakeLists.content.txt.tpl", - "target": "%{ProjectDirectory}/content/CMakeLists.txt" - }, { "source": "../common/App.qml.tpl", - "target": "%{ProjectDirectory}/content/App.qml" + "target": "%{ProjectDirectory}/%{ContentDir}/App.qml" }, { "source": "Screen01.ui.qml.tpl", - "target": "%{ProjectDirectory}/content/Screen01.ui.qml", + "target": "%{ProjectDirectory}/%{ContentDir}/Screen01.ui.qml", "openInEditor": true }, { "source": "../common/fonts.txt", - "target": "%{ProjectDirectory}/content/fonts/fonts.txt" + "target": "%{ProjectDirectory}/%{ContentDir}/fonts/fonts.txt" }, { "source": "../common/asset_imports.txt", - "target": "%{ProjectDirectory}/asset_imports/asset_imports.txt" - }, - { - "source": "../common/CMakeLists.imports.txt.tpl", - "target": "%{ProjectDirectory}/imports/CMakeLists.txt" - }, - { - "source": "../shared-plugin/name/CMakeLists.importmodule.txt.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/CMakeLists.txt" + "target": "%{ProjectDirectory}/%{AssetDir}/%{AssetDir}.txt" }, { "source": "../shared-plugin/name/importmodule.qmldir.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/qmldir" + "target": "%{ProjectDirectory}/%{ImportModuleName}/qmldir" }, { "source": "../shared-plugin/name/Constants.qml.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/Constants.qml" + "target": "%{ProjectDirectory}/%{ImportModuleName}/Constants.qml" }, { "source": "../shared-plugin/name/DirectoryFontLoader.qml.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/DirectoryFontLoader.qml" + "target": "%{ProjectDirectory}/%{ImportModuleName}/DirectoryFontLoader.qml" }, { "source": "../shared-plugin/name/EventListModel.qml.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/EventListModel.qml" + "target": "%{ProjectDirectory}/%{ImportModuleName}/EventListModel.qml" }, { "source": "../shared-plugin/name/EventListSimulator.qml.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/EventListSimulator.qml" + "target": "%{ProjectDirectory}/%{ImportModuleName}/EventListSimulator.qml" }, { "source": "../shared-plugin/name/designer/plugin.metainfo", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/designer/plugin.metainfo" + "target": "%{ProjectDirectory}/%{ImportModuleName}/designer/plugin.metainfo" } ] } diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/wizard.json index e32ecb8a0bd..5f2e5bfcafd 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/wizard.json @@ -18,6 +18,8 @@ { "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" }, { "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" }, { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') !== '2.15' }" }, + { "key": "AssetDir", "value": "GeneratedComponents" }, + { "key": "ContentDir", "value": "%{ProjectName}Content" }, { "key": "ImportModuleName", "value": "%{ProjectName}" }, { "key": "UIClassName", "value": "Screen01" }, { "key": "UIClassFileName", "value": "%{JS: Util.fileName('%{UIClassName}', 'ui.qml')}" }, @@ -281,99 +283,50 @@ "target": "%{ProjectDirectory}/%{QmlProjectFileName}", "openAsProject": true }, - { - "source": "../common/CMakeLists.main.txt.tpl", - "target": "%{ProjectDirectory}/CMakeLists.txt" - }, - { - "source": "../common/qmlmodules.tpl", - "target": "%{ProjectDirectory}/qmlmodules" - }, - { - "source": "../common/qmlcomponents.tpl", - "target": "%{ProjectDirectory}/qmlcomponents" - }, - { - "source": "../common/insight.tpl", - "target": "%{ProjectDirectory}/insight" - }, - { - "source": "../common/main.qml", - "target": "%{ProjectDirectory}/main.qml" - }, { "source": "../common/qtquickcontrols2.conf.tpl", "target": "%{ProjectDirectory}/qtquickcontrols2.conf" }, - { - "source": "../common/main.cpp.tpl", - "target": "%{ProjectDirectory}/src/main.cpp" - }, - { - "source": "../common/app_environment.h.tpl", - "target": "%{ProjectDirectory}/src/app_environment.h" - }, - { - "source": "../common/import_qml_plugins.h.tpl", - "target": "%{ProjectDirectory}/src/import_qml_plugins.h" - }, - { - "source": "../common/import_qml_components_plugins.h.tpl", - "target": "%{ProjectDirectory}/src/import_qml_components_plugins.h" - }, - - { - "source": "../common/CMakeLists.content.txt.tpl", - "target": "%{ProjectDirectory}/content/CMakeLists.txt" - }, { "source": "../common/App.qml.tpl", - "target": "%{ProjectDirectory}/content/App.qml" + "target": "%{ProjectDirectory}/%{ContentDir}/App.qml" }, { "source": "Screen01.ui.qml.tpl", - "target": "%{ProjectDirectory}/content/Screen01.ui.qml", + "target": "%{ProjectDirectory}/%{ContentDir}/Screen01.ui.qml", "openInEditor": true }, { "source": "../common/fonts.txt", - "target": "%{ProjectDirectory}/content/fonts/fonts.txt" + "target": "%{ProjectDirectory}/%{ContentDir}/fonts/fonts.txt" }, { "source": "../common/asset_imports.txt", - "target": "%{ProjectDirectory}/asset_imports/asset_imports.txt" - }, - { - "source": "../common/CMakeLists.imports.txt.tpl", - "target": "%{ProjectDirectory}/imports/CMakeLists.txt" - }, - { - "source": "../shared-plugin/name/CMakeLists.importmodule.txt.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/CMakeLists.txt" + "target": "%{ProjectDirectory}/%{AssetDir}/%{AssetDir}.txt" }, { "source": "../shared-plugin/name/importmodule.qmldir.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/qmldir" + "target": "%{ProjectDirectory}/%{ImportModuleName}/qmldir" }, { "source": "../shared-plugin/name/Constants.qml.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/Constants.qml" + "target": "%{ProjectDirectory}/%{ImportModuleName}/Constants.qml" }, { "source": "../shared-plugin/name/DirectoryFontLoader.qml.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/DirectoryFontLoader.qml" + "target": "%{ProjectDirectory}/%{ImportModuleName}/DirectoryFontLoader.qml" }, { "source": "../shared-plugin/name/EventListModel.qml.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/EventListModel.qml" + "target": "%{ProjectDirectory}/%{ImportModuleName}/EventListModel.qml" }, { "source": "../shared-plugin/name/EventListSimulator.qml.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/EventListSimulator.qml" + "target": "%{ProjectDirectory}/%{ImportModuleName}/EventListSimulator.qml" }, { "source": "../shared-plugin/name/designer/plugin.metainfo", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/designer/plugin.metainfo" + "target": "%{ProjectDirectory}/%{ImportModuleName}/designer/plugin.metainfo" } ] } diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json index 613770b646c..41fe2df289a 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json @@ -17,6 +17,8 @@ { "key": "ProjectPluginName", "value": "%{ProjectName}plugin" }, { "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" }, { "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" }, + { "key": "AssetDir", "value": "GeneratedComponents" }, + { "key": "ContentDir", "value": "%{ProjectName}Content" }, { "key": "ImportModuleName", "value": "%{ProjectName}" }, { "key": "UIClassName", "value": "Screen01" }, { "key": "UIClassFileName", "value": "%{JS: Util.fileName('%{UIClassName}', 'ui.qml')}" }, @@ -306,109 +308,49 @@ "target": "%{ProjectDirectory}/%{QmlProjectFileName}", "openAsProject": true }, - { - "source": "../common/CMakeLists.main.txt.tpl", - "target": "%{ProjectDirectory}/CMakeLists.txt", - "condition": "%{IsQt6Project}" - }, - { - "source": "../common/qmlmodules.tpl", - "target": "%{ProjectDirectory}/qmlmodules", - "condition": "%{IsQt6Project}" - }, - { - "source": "../common/qmlcomponents.tpl", - "target": "%{ProjectDirectory}/qmlcomponents", - "condition": "%{IsQt6Project}" - }, - { - "source": "../common/insight.tpl", - "target": "%{ProjectDirectory}/insight", - "condition": "%{IsQt6Project}" - }, - { - "source": "../common/main.qml", - "target": "%{ProjectDirectory}/main.qml", - "condition": "%{IsQt6Project}" - }, { "source": "../common/qtquickcontrols2.conf.tpl", "target": "%{ProjectDirectory}/qtquickcontrols2.conf" }, - { - "source": "../common/main.cpp.tpl", - "target": "%{ProjectDirectory}/src/main.cpp", - "condition": "%{IsQt6Project}" - }, - { - "source": "../common/app_environment.h.tpl", - "target": "%{ProjectDirectory}/src/app_environment.h", - "condition": "%{IsQt6Project}" - }, - { - "source": "../common/import_qml_plugins.h.tpl", - "target": "%{ProjectDirectory}/src/import_qml_plugins.h", - "condition": "%{IsQt6Project}" - }, - { - "source": "../common/import_qml_components_plugins.h.tpl", - "target": "%{ProjectDirectory}/src/import_qml_components_plugins.h", - "condition": "%{IsQt6Project}" - }, - { - "source": "../common/CMakeLists.content.txt.tpl", - "target": "%{ProjectDirectory}/content/CMakeLists.txt", - "condition": "%{IsQt6Project}" - }, { "source": "../common/App.qml.tpl", - "target": "%{ProjectDirectory}/content/App.qml" + "target": "%{ProjectDirectory}/%{ContentDir}/App.qml" }, { "source": "Screen01.ui.qml.tpl", - "target": "%{ProjectDirectory}/content/Screen01.ui.qml" + "target": "%{ProjectDirectory}/%{ContentDir}/Screen01.ui.qml" }, { "source": "../common/fonts.txt", - "target": "%{ProjectDirectory}/content/fonts/fonts.txt" + "target": "%{ProjectDirectory}/%{ContentDir}/fonts/fonts.txt" }, { "source": "../common/asset_imports.txt", - "target": "%{ProjectDirectory}/asset_imports/asset_imports.txt" - }, - { - "source": "../common/CMakeLists.imports.txt.tpl", - "target": "%{ProjectDirectory}/imports/CMakeLists.txt", - "condition": "%{IsQt6Project}" - }, - { - "source": "../shared-plugin/name/CMakeLists.importmodule.txt.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/CMakeLists.txt", - "condition": "%{IsQt6Project}" + "target": "%{ProjectDirectory}/%{AssetDir}/Quick3DAssets.txt" }, { "source": "../shared-plugin/name/importmodule.qmldir.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/qmldir" + "target": "%{ProjectDirectory}/%{ImportModuleName}/qmldir" }, { "source": "../shared-plugin/name/Constants.qml.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/Constants.qml" + "target": "%{ProjectDirectory}/%{ImportModuleName}/Constants.qml" }, { "source": "../shared-plugin/name/DirectoryFontLoader.qml.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/DirectoryFontLoader.qml" + "target": "%{ProjectDirectory}/%{ImportModuleName}/DirectoryFontLoader.qml" }, { "source": "../shared-plugin/name/EventListModel.qml.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/EventListModel.qml" + "target": "%{ProjectDirectory}/%{ImportModuleName}/EventListModel.qml" }, { "source": "../shared-plugin/name/EventListSimulator.qml.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/EventListSimulator.qml" + "target": "%{ProjectDirectory}/%{ImportModuleName}/EventListSimulator.qml" }, { "source": "../shared-plugin/name/designer/plugin.metainfo", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/designer/plugin.metainfo" + "target": "%{ProjectDirectory}/%{ImportModuleName}/designer/plugin.metainfo" } ] } diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/common/CMakeLists.content.txt.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/common/CMakeLists.content.txt.tpl deleted file mode 100644 index a5a4360e3f2..00000000000 --- a/share/qtcreator/qmldesigner/studio_templates/projects/common/CMakeLists.content.txt.tpl +++ /dev/null @@ -1,14 +0,0 @@ -### This file is automatically generated by Qt Design Studio. -### Do not change - -qt_add_library(content STATIC) -qt6_add_qml_module(content - URI "content" - VERSION 1.0 - RESOURCE_PREFIX "/qt/qml" - QML_FILES - App.qml - %{UIClassFileName} - RESOURCES - fonts/fonts.txt -) diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/common/CMakeLists.imports.txt.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/common/CMakeLists.imports.txt.tpl deleted file mode 100644 index 418f6d7719d..00000000000 --- a/share/qtcreator/qmldesigner/studio_templates/projects/common/CMakeLists.imports.txt.tpl +++ /dev/null @@ -1,4 +0,0 @@ -### This file is automatically generated by Qt Design Studio. -### Do not change - -add_subdirectory(%{ImportModuleName}) 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 deleted file mode 100644 index eb621ef2193..00000000000 --- a/share/qtcreator/qmldesigner/studio_templates/projects/common/CMakeLists.main.txt.tpl +++ /dev/null @@ -1,56 +0,0 @@ -cmake_minimum_required(VERSION 3.21.1) - -option(LINK_INSIGHT "Link Qt Insight Tracker library" ON) -option(BUILD_QDS_COMPONENTS "Build design studio components" ON) - -project(%{ProjectName}App LANGUAGES CXX) - -set(CMAKE_AUTOMOC ON) - -find_package(Qt6 6.2 REQUIRED COMPONENTS Core Gui Qml Quick) - -if (Qt6_VERSION VERSION_GREATER_EQUAL 6.3) - qt_standard_project_setup() -endif() - -qt_add_executable(%{ProjectName}App src/main.cpp) - -qt_add_resources(%{ProjectName}App "configuration" - PREFIX "/" - FILES - qtquickcontrols2.conf -) - -target_link_libraries(%{ProjectName}App PRIVATE - Qt6::Core - Qt6::Gui - Qt6::Qml - Qt6::Quick -) - -set(QT_QML_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/qml) -set(QML_IMPORT_PATH ${QT_QML_OUTPUT_DIRECTORY} - CACHE STRING "Import paths for Qt Creator's code model" - FORCE -) - -if (BUILD_QDS_COMPONENTS) - include(${CMAKE_CURRENT_SOURCE_DIR}/qmlcomponents) -endif() - -include(${CMAKE_CURRENT_SOURCE_DIR}/qmlmodules) - -if (LINK_INSIGHT) - include(${CMAKE_CURRENT_SOURCE_DIR}/insight) -endif () - -include(GNUInstallDirs) -install(TARGETS %{ProjectName}App - BUNDLE DESTINATION . - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} -) - -# make IDEs aware of the QML import path -set(QML_IMPORT_PATH ${PROJECT_BINARY_DIR}/qml CACHE PATH - "Path to the custom QML components defined by the project") 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 75aeae67142..6d346818e41 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl @@ -5,32 +5,36 @@ import QmlProject 1.1 @endif Project { - mainFile: "content/App.qml" - mainUiFile: "content/Screen01.ui.qml" + mainFile: "%{ContentDir}/App.qml" + mainUiFile: "%{ContentDir}/Screen01.ui.qml" /* Include .qml, .js, and image files from current directory and subdirectories */ QmlFiles { - directory: "content" + directory: "%{ProjectName}" } QmlFiles { - directory: "imports" + directory: "%{ContentDir}" + } + + QmlFiles { + directory: "%{AssetDir}" } JavaScriptFiles { - directory: "content" + directory: "%{ProjectName}" } JavaScriptFiles { - directory: "imports" + directory: "%{ProjectName}" } ImageFiles { - directory: "content" + directory: "%{ContentDir}" } ImageFiles { - directory: "asset_imports" + directory: "%{AssetDir}" } Files { @@ -69,17 +73,12 @@ Project { Files { filter: "*.mesh" - directory: "asset_imports" + directory: "%{AssetDir}" } Files { filter: "*.qad" - directory: "asset_imports" - } - - Files { - filter: "*.qml" - directory: "asset_imports" + directory: "%{AssetDir}" } Environment { @@ -109,7 +108,7 @@ Project { @endif /* List of plugin directories passed to QML runtime */ - importPaths: [ "imports", "asset_imports" ] + importPaths: [ "." ] /* Required for deployment */ targetDirectory: "/opt/%{ProjectName}" @@ -125,10 +124,10 @@ Project { /* 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" ] */ + e.g. files: [ "%{ContentDir}/shaders/*.vert", "*.frag" ] */ ShaderTool { args: "-s --glsl \\\"100 es,120,150\\\" --hlsl 50 --msl 12" - files: [ "content/shaders/*" ] + files: [ "%{ContentDir}/shaders/*" ] } @endif diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/common/app_environment.h.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/common/app_environment.h.tpl deleted file mode 100644 index e1f7ec2e23d..00000000000 --- a/share/qtcreator/qmldesigner/studio_templates/projects/common/app_environment.h.tpl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * This file is automatically generated by Qt Design Studio. - * Do not change. -*/ - -#include - -void set_qt_environment() -{ -@if %{UseVirtualKeyboard} - qputenv("QT_IM_MODULE", "qtvirtualkeyboard"); - qputenv("QT_VIRTUALKEYBOARD_DESKTOP_DISABLE", "1"); -@endif - qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", "1"); - qputenv("QT_ENABLE_HIGHDPI_SCALING", "0"); - qputenv("QT_LOGGING_RULES", "qt.qml.connections=false"); - qputenv("QT_QUICK_CONTROLS_CONF", ":/qtquickcontrols2.conf"); - qputenv("QML_COMPAT_RESOLVE_URLS_ON_ASSIGNMENT", "1"); -} diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/common/contentmodule.main.qml.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/common/contentmodule.main.qml.tpl deleted file mode 100644 index ac0b0b28c02..00000000000 --- a/share/qtcreator/qmldesigner/studio_templates/projects/common/contentmodule.main.qml.tpl +++ /dev/null @@ -1,17 +0,0 @@ -import QtQuick %{QtQuickVersion} -@if !%{IsQt6Project} -import QtQuick.Window %{QtQuickVersion} -@endif -import %{ApplicationImport} - -Window { - width: Constants.width - height: Constants.height - - visible: true - - Screen01 { - width: parent.width - height: parent.height - } -} diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/common/import_qml_components_plugins.h.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/common/import_qml_components_plugins.h.tpl deleted file mode 100644 index 167481d7c74..00000000000 --- a/share/qtcreator/qmldesigner/studio_templates/projects/common/import_qml_components_plugins.h.tpl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * This file is automatically generated by Qt Design Studio. - * Do not change. -*/ - -#include "qqmlextensionplugin.h" - -#ifdef BUILD_QDS_COMPONENTS - -Q_IMPORT_QML_PLUGIN(QtQuick_Studio_ComponentsPlugin) -Q_IMPORT_QML_PLUGIN(QtQuick_Studio_EffectsPlugin) -Q_IMPORT_QML_PLUGIN(QtQuick_Studio_ApplicationPlugin) -Q_IMPORT_QML_PLUGIN(FlowViewPlugin) -Q_IMPORT_QML_PLUGIN(QtQuick_Studio_LogicHelperPlugin) -Q_IMPORT_QML_PLUGIN(QtQuick_Studio_MultiTextPlugin) -Q_IMPORT_QML_PLUGIN(QtQuick_Studio_EventSimulatorPlugin) -Q_IMPORT_QML_PLUGIN(QtQuick_Studio_EventSystemPlugin) - -#endif diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/common/import_qml_plugins.h.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/common/import_qml_plugins.h.tpl deleted file mode 100644 index f9700ff4528..00000000000 --- a/share/qtcreator/qmldesigner/studio_templates/projects/common/import_qml_plugins.h.tpl +++ /dev/null @@ -1,9 +0,0 @@ -/* - * This file is automatically generated by Qt Design Studio. - * Do not change. -*/ - -#include - -Q_IMPORT_QML_PLUGIN(contentPlugin) -Q_IMPORT_QML_PLUGIN(%{ProjectPluginClassName}) diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/common/insight.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/common/insight.tpl deleted file mode 100644 index 8245e31f0d9..00000000000 --- a/share/qtcreator/qmldesigner/studio_templates/projects/common/insight.tpl +++ /dev/null @@ -1,19 +0,0 @@ -### 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/common/main.cpp.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/common/main.cpp.tpl deleted file mode 100644 index 915d08462e2..00000000000 --- a/share/qtcreator/qmldesigner/studio_templates/projects/common/main.cpp.tpl +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only - -#include -#include - -#include "app_environment.h" -#include "import_qml_components_plugins.h" -#include "import_qml_plugins.h" - -int main(int argc, char *argv[]) -{ - set_qt_environment(); - - QGuiApplication app(argc, argv); - - QQmlApplicationEngine engine; - const QUrl url(u"qrc:/qt/qml/Main/main.qml"_qs); - QObject::connect( - &engine, &QQmlApplicationEngine::objectCreated, &app, - [url](QObject *obj, const QUrl &objUrl) { - if (!obj && url == objUrl) - QCoreApplication::exit(-1); - }, - Qt::QueuedConnection); - - engine.addImportPath(QCoreApplication::applicationDirPath() + "/qml"); - engine.addImportPath(":/"); - - engine.load(url); - - if (engine.rootObjects().isEmpty()) { - return -1; - } - - return app.exec(); -} diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/common/main.qml b/share/qtcreator/qmldesigner/studio_templates/projects/common/main.qml deleted file mode 100644 index 2c4f857df3b..00000000000 --- a/share/qtcreator/qmldesigner/studio_templates/projects/common/main.qml +++ /dev/null @@ -1,8 +0,0 @@ -/* This file is generated and only relevant for integrating the project into a Qt 6 and cmake based -C++ project. */ - -import QtQuick -import content - -App { -} diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/common/qmlcomponents.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/common/qmlcomponents.tpl deleted file mode 100644 index a9f20243a69..00000000000 --- a/share/qtcreator/qmldesigner/studio_templates/projects/common/qmlcomponents.tpl +++ /dev/null @@ -1,34 +0,0 @@ -### This file is automatically generated by Qt Design Studio. -### Do not change - -message("Building designer components.") - -set(QT_QML_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/qml") - -include(FetchContent) -FetchContent_Declare( - ds - GIT_TAG qds-4.5 - GIT_REPOSITORY https://code.qt.io/qt-labs/qtquickdesigner-components.git -) - -FetchContent_GetProperties(ds) -FetchContent_Populate(ds) - -target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE - QuickStudioComponentsplugin - QuickStudioEffectsplugin - QuickStudioApplicationplugin - FlowViewplugin - QuickStudioLogicHelperplugin - QuickStudioMultiTextplugin - QuickStudioEventSimulatorplugin - QuickStudioEventSystemplugin - QuickStudioUtilsplugin -) - -add_subdirectory(${ds_SOURCE_DIR} ${ds_BINARY_DIR}) - -target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE - BULD_QDS_COMPONENTS=true -) diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/common/qmlmodules.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/common/qmlmodules.tpl deleted file mode 100644 index 5a22661b5a1..00000000000 --- a/share/qtcreator/qmldesigner/studio_templates/projects/common/qmlmodules.tpl +++ /dev/null @@ -1,18 +0,0 @@ -### This file is automatically generated by Qt Design Studio. -### Do not change - -qt6_add_qml_module(${CMAKE_PROJECT_NAME} - URI "Main" - VERSION 1.0 - RESOURCE_PREFIX "/qt/qml" - NO_PLUGIN - QML_FILES main.qml -) - -add_subdirectory(content) -add_subdirectory(imports) - -target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE - contentplugin - %{ProjectPluginName} -) diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/common/qtquickcontrols2.conf b/share/qtcreator/qmldesigner/studio_templates/projects/common/qtquickcontrols2.conf deleted file mode 100644 index 9c7633fb0a6..00000000000 --- a/share/qtcreator/qmldesigner/studio_templates/projects/common/qtquickcontrols2.conf +++ /dev/null @@ -1,23 +0,0 @@ -; This file can be edited to change the style of the application -; Read "Qt Quick Controls 2 Configuration File" for details: -; http://doc.qt.io/qt-5/qtquickcontrols2-configuration.html - -[Controls] -Style=%{QtQuickControlsStyle} -@if '%{QtQuickControlsStyle}' == 'Universal' - -[Universal] -Theme=%{QtQuickControlsStyleTheme} -;Accent=Steel -;Foreground=Brown -;Background=Steel -@endif -@if '%{QtQuickControlsStyle}' == 'Material' - -[Material] -Theme=%{QtQuickControlsStyleTheme} -;Accent=BlueGrey -;Primary=BlueGray -;Foreground=Brown -;Background=Grey -@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 01a603eb9d8..ddaf5021543 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json @@ -17,6 +17,8 @@ { "key": "ProjectPluginName", "value": "%{ProjectName}plugin" }, { "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" }, { "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" }, + { "key": "AssetDir", "value": "GeneratedComponents" }, + { "key": "ContentDir", "value": "%{ProjectName}Content" }, { "key": "ImportModuleName", "value": "%{ProjectName}" }, { "key": "UIClassName", "value": "Screen01" }, { "key": "UIClassFileName", "value": "%{JS: Util.fileName('%{UIClassName}', 'ui.qml')}" }, @@ -297,98 +299,50 @@ "target": "%{ProjectDirectory}/%{QmlProjectFileName}", "openAsProject": true }, - { - "source": "../common/CMakeLists.main.txt.tpl", - "target": "%{ProjectDirectory}/CMakeLists.txt" - }, - { - "source": "../common/qmlmodules.tpl", - "target": "%{ProjectDirectory}/qmlmodules" - }, - { - "source": "../common/qmlcomponents.tpl", - "target": "%{ProjectDirectory}/qmlcomponents" - }, - { - "source": "../common/insight.tpl", - "target": "%{ProjectDirectory}/insight" - }, - { - "source": "../common/main.qml", - "target": "%{ProjectDirectory}/main.qml" - }, { "source": "../common/qtquickcontrols2.conf.tpl", "target": "%{ProjectDirectory}/qtquickcontrols2.conf" }, - { - "source": "../common/main.cpp.tpl", - "target": "%{ProjectDirectory}/src/main.cpp" - }, - { - "source": "../common/app_environment.h.tpl", - "target": "%{ProjectDirectory}/src/app_environment.h" - }, - { - "source": "../common/import_qml_plugins.h.tpl", - "target": "%{ProjectDirectory}/src/import_qml_plugins.h" - }, - { - "source": "../common/import_qml_components_plugins.h.tpl", - "target": "%{ProjectDirectory}/src/import_qml_components_plugins.h" - }, - { - "source": "../common/CMakeLists.content.txt.tpl", - "target": "%{ProjectDirectory}/content/CMakeLists.txt" - }, { "source": "../common/App.qml.tpl", - "target": "%{ProjectDirectory}/content/App.qml" + "target": "%{ProjectDirectory}/%{ContentDir}/App.qml" }, { "source": "Screen01.ui.qml.tpl", - "target": "%{ProjectDirectory}/content/Screen01.ui.qml", + "target": "%{ProjectDirectory}/%{ContentDir}/Screen01.ui.qml", "openInEditor": true }, { "source": "../common/fonts.txt", - "target": "%{ProjectDirectory}/content/fonts/fonts.txt" + "target": "%{ProjectDirectory}/%{ContentDir}/fonts/fonts.txt" }, { "source": "../common/asset_imports.txt", - "target": "%{ProjectDirectory}/asset_imports/asset_imports.txt" - }, - { - "source": "../common/CMakeLists.imports.txt.tpl", - "target": "%{ProjectDirectory}/imports/CMakeLists.txt" - }, - { - "source": "../shared-plugin/name/CMakeLists.importmodule.txt.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/CMakeLists.txt" + "target": "%{ProjectDirectory}/%{AssetDir}/%{AssetDir}.txt" }, { "source": "../shared-plugin/name/importmodule.qmldir.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/qmldir" + "target": "%{ProjectDirectory}/%{ImportModuleName}/qmldir" }, { "source": "../shared-plugin/name/Constants.qml.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/Constants.qml" + "target": "%{ProjectDirectory}/%{ImportModuleName}/Constants.qml" }, { "source": "../shared-plugin/name/DirectoryFontLoader.qml.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/DirectoryFontLoader.qml" + "target": "%{ProjectDirectory}/%{ImportModuleName}/DirectoryFontLoader.qml" }, { "source": "../shared-plugin/name/EventListModel.qml.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/EventListModel.qml" + "target": "%{ProjectDirectory}/%{ImportModuleName}/EventListModel.qml" }, { "source": "../shared-plugin/name/EventListSimulator.qml.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/EventListSimulator.qml" + "target": "%{ProjectDirectory}/%{ImportModuleName}/EventListSimulator.qml" }, { "source": "../shared-plugin/name/designer/plugin.metainfo", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/designer/plugin.metainfo" + "target": "%{ProjectDirectory}/%{ImportModuleName}/designer/plugin.metainfo" } ] } 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 7ad8d1d9d43..585a73aa907 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json @@ -17,6 +17,8 @@ { "key": "ProjectPluginName", "value": "%{ProjectName}plugin" }, { "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" }, { "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" }, + { "key": "AssetDir", "value": "GeneratedComponents" }, + { "key": "ContentDir", "value": "%{ProjectName}Content" }, { "key": "ImportModuleName", "value": "%{ProjectName}" }, { "key": "UIClassName", "value": "Screen01" }, { "key": "UIClassFileName", "value": "%{JS: Util.fileName('%{UIClassName}', 'ui.qml')}" }, @@ -263,98 +265,50 @@ "target": "%{ProjectDirectory}/%{QmlProjectFileName}", "openAsProject": true }, - { - "source": "../common/CMakeLists.main.txt.tpl", - "target": "%{ProjectDirectory}/CMakeLists.txt" - }, - { - "source": "../common/qmlmodules.tpl", - "target": "%{ProjectDirectory}/qmlmodules" - }, - { - "source": "../common/qmlcomponents.tpl", - "target": "%{ProjectDirectory}/qmlcomponents" - }, - { - "source": "../common/insight.tpl", - "target": "%{ProjectDirectory}/insight" - }, - { - "source": "../common/main.qml", - "target": "%{ProjectDirectory}/main.qml" - }, { "source": "../common/qtquickcontrols2.conf.tpl", "target": "%{ProjectDirectory}/qtquickcontrols2.conf" }, - { - "source": "../common/main.cpp.tpl", - "target": "%{ProjectDirectory}/src/main.cpp" - }, - { - "source": "../common/app_environment.h.tpl", - "target": "%{ProjectDirectory}/src/app_environment.h" - }, - { - "source": "../common/import_qml_plugins.h.tpl", - "target": "%{ProjectDirectory}/src/import_qml_plugins.h" - }, - { - "source": "../common/import_qml_components_plugins.h.tpl", - "target": "%{ProjectDirectory}/src/import_qml_components_plugins.h" - }, - { - "source": "../common/CMakeLists.content.txt.tpl", - "target": "%{ProjectDirectory}/content/CMakeLists.txt" - }, { "source": "../common/App.qml.tpl", - "target": "%{ProjectDirectory}/content/App.qml" + "target": "%{ProjectDirectory}/%{ContentDir}/App.qml" }, { "source": "Screen01.ui.qml.tpl", - "target": "%{ProjectDirectory}/content/Screen01.ui.qml", + "target": "%{ProjectDirectory}/%{ContentDir}/Screen01.ui.qml", "openInEditor": true }, { "source": "../common/fonts.txt", - "target": "%{ProjectDirectory}/content/fonts/fonts.txt" + "target": "%{ProjectDirectory}/%{ContentDir}/fonts/fonts.txt" }, { "source": "../common/asset_imports.txt", - "target": "%{ProjectDirectory}/asset_imports/asset_imports.txt" - }, - { - "source": "../common/CMakeLists.imports.txt.tpl", - "target": "%{ProjectDirectory}/imports/CMakeLists.txt" - }, - { - "source": "../shared-plugin/name/CMakeLists.importmodule.txt.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/CMakeLists.txt" + "target": "%{ProjectDirectory}/%{AssetDir}/%{AssetDir}.txt" }, { "source": "../shared-plugin/name/importmodule.qmldir.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/qmldir" + "target": "%{ProjectDirectory}/%{ImportModuleName}/qmldir" }, { "source": "../shared-plugin/name/Constants.qml.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/Constants.qml" + "target": "%{ProjectDirectory}/%{ImportModuleName}/Constants.qml" }, { "source": "../shared-plugin/name/DirectoryFontLoader.qml.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/DirectoryFontLoader.qml" + "target": "%{ProjectDirectory}/%{ImportModuleName}/DirectoryFontLoader.qml" }, { "source": "../shared-plugin/name/EventListModel.qml.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/EventListModel.qml" + "target": "%{ProjectDirectory}/%{ImportModuleName}/EventListModel.qml" }, { "source": "../shared-plugin/name/EventListSimulator.qml.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/EventListSimulator.qml" + "target": "%{ProjectDirectory}/%{ImportModuleName}/EventListSimulator.qml" }, { "source": "../shared-plugin/name/designer/plugin.metainfo", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/designer/plugin.metainfo" + "target": "%{ProjectDirectory}/%{ImportModuleName}/designer/plugin.metainfo" } ] } 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 0a3aaf26b6e..a44a1429bee 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json @@ -17,6 +17,8 @@ { "key": "ProjectPluginName", "value": "%{ProjectName}plugin" }, { "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" }, { "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" }, + { "key": "AssetDir", "value": "GeneratedComponents" }, + { "key": "ContentDir", "value": "%{ProjectName}Content" }, { "key": "ImportModuleName", "value": "%{ProjectName}" }, { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') !== '2.15' }" }, { "key": "QtQuickVersion", "value": "%{JS: %{TargetQtVersion}.TargetQuickVersion}" }, @@ -260,103 +262,55 @@ "target": "%{ProjectDirectory}/%{QmlProjectFileName}", "openAsProject": true }, - { - "source": "../common/CMakeLists.main.txt.tpl", - "target": "%{ProjectDirectory}/CMakeLists.txt" - }, - { - "source": "../common/qmlmodules.tpl", - "target": "%{ProjectDirectory}/qmlmodules" - }, - { - "source": "../common/qmlcomponents.tpl", - "target": "%{ProjectDirectory}/qmlcomponents" - }, - { - "source": "../common/insight.tpl", - "target": "%{ProjectDirectory}/insight" - }, - { - "source": "../common/main.qml", - "target": "%{ProjectDirectory}/main.qml" - }, { "source": "../common/qtquickcontrols2.conf.tpl", "target": "%{ProjectDirectory}/qtquickcontrols2.conf" }, - { - "source": "../common/main.cpp.tpl", - "target": "%{ProjectDirectory}/src/main.cpp" - }, - { - "source": "../common/app_environment.h.tpl", - "target": "%{ProjectDirectory}/src/app_environment.h" - }, - { - "source": "../common/import_qml_plugins.h.tpl", - "target": "%{ProjectDirectory}/src/import_qml_plugins.h" - }, - { - "source": "../common/import_qml_components_plugins.h.tpl", - "target": "%{ProjectDirectory}/src/import_qml_components_plugins.h" - }, - { - "source": "CMakeLists.content.txt.tpl", - "target": "%{ProjectDirectory}/content/CMakeLists.txt" - }, { "source": "App.qml.tpl", - "target": "%{ProjectDirectory}/content/App.qml" + "target": "%{ProjectDirectory}/%{ContentDir}/App.qml" }, { "source": "Screen01.ui.qml.tpl", - "target": "%{ProjectDirectory}/content/Screen01.ui.qml", + "target": "%{ProjectDirectory}/%{ContentDir}/Screen01.ui.qml", "openInEditor": true }, { "source": "Screen02.ui.qml.tpl", - "target": "%{ProjectDirectory}/content/Screen02.ui.qml", + "target": "%{ProjectDirectory}/%{ContentDir}/Screen02.ui.qml", "openInEditor": true }, { "source": "../common/fonts.txt", - "target": "%{ProjectDirectory}/content/fonts/fonts.txt" + "target": "%{ProjectDirectory}/%{ContentDir}/fonts/fonts.txt" }, { "source": "../common/asset_imports.txt", - "target": "%{ProjectDirectory}/asset_imports/asset_imports.txt" - }, - { - "source": "../common/CMakeLists.imports.txt.tpl", - "target": "%{ProjectDirectory}/imports/CMakeLists.txt" - }, - { - "source": "../shared-plugin/name/CMakeLists.importmodule.txt.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/CMakeLists.txt" + "target": "%{ProjectDirectory}/%{AssetDir}/%{AssetDir}.txt" }, { "source": "../shared-plugin/name/importmodule.qmldir.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/qmldir" + "target": "%{ProjectDirectory}/%{ImportModuleName}/qmldir" }, { "source": "../shared-plugin/name/Constants.qml.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/Constants.qml" + "target": "%{ProjectDirectory}/%{ImportModuleName}/Constants.qml" }, { "source": "../shared-plugin/name/DirectoryFontLoader.qml.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/DirectoryFontLoader.qml" + "target": "%{ProjectDirectory}/%{ImportModuleName}/DirectoryFontLoader.qml" }, { "source": "../shared-plugin/name/EventListModel.qml.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/EventListModel.qml" + "target": "%{ProjectDirectory}/%{ImportModuleName}/EventListModel.qml" }, { "source": "../shared-plugin/name/EventListSimulator.qml.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/EventListSimulator.qml" + "target": "%{ProjectDirectory}/%{ImportModuleName}/EventListSimulator.qml" }, { "source": "../shared-plugin/name/designer/plugin.metainfo", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/designer/plugin.metainfo" + "target": "%{ProjectDirectory}/%{ImportModuleName}/designer/plugin.metainfo" } ] } 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 58c4783f097..b3f70a8b797 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json @@ -17,6 +17,8 @@ { "key": "ProjectPluginName", "value": "%{ProjectName}plugin" }, { "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" }, { "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" }, + { "key": "AssetDir", "value": "GeneratedComponents" }, + { "key": "ContentDir", "value": "%{ProjectName}Content" }, { "key": "ImportModuleName", "value": "%{ProjectName}" }, { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') !== '2.15' }" }, { "key": "QtQuickVersion", "value": "%{JS: %{TargetQtVersion}.TargetQuickVersion}" }, @@ -260,103 +262,55 @@ "target": "%{ProjectDirectory}/%{QmlProjectFileName}", "openAsProject": true }, - { - "source": "../common/CMakeLists.main.txt.tpl", - "target": "%{ProjectDirectory}/CMakeLists.txt" - }, - { - "source": "../common/qmlmodules.tpl", - "target": "%{ProjectDirectory}/qmlmodules" - }, - { - "source": "../common/qmlcomponents.tpl", - "target": "%{ProjectDirectory}/qmlcomponents" - }, - { - "source": "../common/insight.tpl", - "target": "%{ProjectDirectory}/insight" - }, - { - "source": "../common/main.qml", - "target": "%{ProjectDirectory}/main.qml" - }, { "source": "../common/qtquickcontrols2.conf.tpl", "target": "%{ProjectDirectory}/qtquickcontrols2.conf" }, - { - "source": "../common/main.cpp.tpl", - "target": "%{ProjectDirectory}/src/main.cpp" - }, - { - "source": "../common/app_environment.h.tpl", - "target": "%{ProjectDirectory}/src/app_environment.h" - }, - { - "source": "../common/import_qml_plugins.h.tpl", - "target": "%{ProjectDirectory}/src/import_qml_plugins.h" - }, - { - "source": "../common/import_qml_components_plugins.h.tpl", - "target": "%{ProjectDirectory}/src/import_qml_components_plugins.h" - }, - { - "source": "CMakeLists.content.txt.tpl", - "target": "%{ProjectDirectory}/content/CMakeLists.txt" - }, { "source": "App.qml.tpl", - "target": "%{ProjectDirectory}/content/App.qml" + "target": "%{ProjectDirectory}/%{ContentDir}/App.qml" }, { "source": "Screen01.ui.qml.tpl", - "target": "%{ProjectDirectory}/content/Screen01.ui.qml", + "target": "%{ProjectDirectory}/%{ContentDir}/Screen01.ui.qml", "openInEditor": true }, { "source": "Screen02.ui.qml.tpl", - "target": "%{ProjectDirectory}/content/Screen02.ui.qml", + "target": "%{ProjectDirectory}/%{ContentDir}/Screen02.ui.qml", "openInEditor": true }, { "source": "../common/fonts.txt", - "target": "%{ProjectDirectory}/content/fonts/fonts.txt" + "target": "%{ProjectDirectory}/%{ContentDir}/fonts/fonts.txt" }, { "source": "../common/asset_imports.txt", - "target": "%{ProjectDirectory}/asset_imports/asset_imports.txt" - }, - { - "source": "../common/CMakeLists.imports.txt.tpl", - "target": "%{ProjectDirectory}/imports/CMakeLists.txt" - }, - { - "source": "../shared-plugin/name/CMakeLists.importmodule.txt.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/CMakeLists.txt" + "target": "%{ProjectDirectory}/%{AssetDir}/%{AssetDir}.txt" }, { "source": "../shared-plugin/name/importmodule.qmldir.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/qmldir" + "target": "%{ProjectDirectory}/%{ImportModuleName}/qmldir" }, { "source": "../shared-plugin/name/Constants.qml.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/Constants.qml" + "target": "%{ProjectDirectory}/%{ImportModuleName}/Constants.qml" }, { "source": "../shared-plugin/name/DirectoryFontLoader.qml.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/DirectoryFontLoader.qml" + "target": "%{ProjectDirectory}/%{ImportModuleName}/DirectoryFontLoader.qml" }, { "source": "../shared-plugin/name/EventListModel.qml.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/EventListModel.qml" + "target": "%{ProjectDirectory}/%{ImportModuleName}/EventListModel.qml" }, { "source": "../shared-plugin/name/EventListSimulator.qml.tpl", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/EventListSimulator.qml" + "target": "%{ProjectDirectory}/%{ImportModuleName}/EventListSimulator.qml" }, { "source": "../shared-plugin/name/designer/plugin.metainfo", - "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/designer/plugin.metainfo" + "target": "%{ProjectDirectory}/%{ImportModuleName}/designer/plugin.metainfo" } ] } diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/Constants.qml.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/Constants.qml.tpl index cc5da7be55a..35aa48896bd 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/Constants.qml.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/Constants.qml.tpl @@ -26,7 +26,7 @@ QtObject { @if %{IsQt6Project} property StudioApplication application: StudioApplication { - fontPath: Qt.resolvedUrl("../../content/" + relativeFontDirectory) + fontPath: Qt.resolvedUrl("../../%{ContentDir}/" + relativeFontDirectory) } @else property DirectoryFontLoader directoryFontLoader: DirectoryFontLoader { diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/DirectoryFontLoader.qml.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/DirectoryFontLoader.qml.tpl index 677fe054285..56ecc9f2f8f 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/DirectoryFontLoader.qml.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/DirectoryFontLoader.qml.tpl @@ -7,7 +7,7 @@ import Qt.labs.folderlistmodel %{QtQuickVersion} QtObject { id: loader - property url fontDirectory: Qt.resolvedUrl("../../content/" + relativeFontDirectory) + property url fontDirectory: Qt.resolvedUrl("../../%{ContentDir}/" + relativeFontDirectory) property string relativeFontDirectory: "fonts" function loadFont(url) { From 75fc181b34f94231285ca96c20128d72768b9c42 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 16 Apr 2024 11:58:02 +0300 Subject: [PATCH 150/202] QmlDesigner: Adjust imported 3D assets path Path changed from GeneratedComponents/QtQuick3DComponents to GeneratedComponents/QtQuick3D to avoid repeating Components unnecessarily. Fixes: QDS-12502 Change-Id: Ife81acddcd5ade7a23d2388b55416e2688a6828a Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../componentcore/modelnodecontextmenu_helper.cpp | 4 ++-- .../designercore/generatedcomponentutils.cpp | 14 ++++++-------- .../designercore/generatedcomponentutils.h | 2 +- .../designercore/metainfo/subcomponentmanager.cpp | 5 ++--- src/plugins/qmldesigner/qmldesignerconstants.h | 2 +- 5 files changed, 12 insertions(+), 15 deletions(-) diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp index 8257926d720..4cbebd738df 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp @@ -105,8 +105,8 @@ bool selectionIsImported3DAsset(const SelectionContext &selectionState) // Node is not a file component, so we have to check if the current doc itself is fileName = node.model()->fileUrl().toLocalFile(); } - if (fileName.contains(Constants::OLD_QUICK_3D_ASSETS_FOLDER) - || fileName.contains(Constants::QUICK_3D_COMPONENTS_FOLDER)) { + if (QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().isImport3dPath(fileName)) { return true; } } diff --git a/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp b/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp index a86388625a5..5ee5790b534 100644 --- a/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp +++ b/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp @@ -80,7 +80,8 @@ Utils::FilePath GeneratedComponentUtils::import3dBasePath() const bool GeneratedComponentUtils::isImport3dPath(const QString &path) const { return path.contains('/' + QLatin1String(Constants::OLD_QUICK_3D_ASSETS_FOLDER)) - || path.contains('/' + QLatin1String(Constants::QUICK_3D_COMPONENTS_FOLDER)); + || path.contains(QLatin1String(Constants::GENERATED_COMPONENTS_FOLDER) + '/' + + QLatin1String(Constants::QUICK_3D_COMPONENTS_FOLDER)); } bool GeneratedComponentUtils::isComposedEffectPath(const QString &path) const @@ -108,14 +109,11 @@ QString GeneratedComponentUtils::import3dTypePrefix() const return Constants::OLD_QUICK_3D_ASSETS_FOLDER; } -QString GeneratedComponentUtils::import3dSimplifiedTypePrefix() const +QString GeneratedComponentUtils::import3dTypePath() const { - QString basePrefix = generatedComponentTypePrefix(); - - if (basePrefix.endsWith(Constants::QUICK_3D_COMPONENTS_FOLDER)) - return Constants::QUICK_3D_COMPONENTS_FOLDER; - - return Constants::OLD_QUICK_3D_ASSETS_FOLDER; + QString prefix = import3dTypePrefix(); + prefix.replace('.', '/'); + return prefix; } QString GeneratedComponentUtils::componentBundlesTypePrefix() const diff --git a/src/plugins/qmldesigner/designercore/generatedcomponentutils.h b/src/plugins/qmldesigner/designercore/generatedcomponentutils.h index f8eceaed4be..e2e9baf3e75 100644 --- a/src/plugins/qmldesigner/designercore/generatedcomponentutils.h +++ b/src/plugins/qmldesigner/designercore/generatedcomponentutils.h @@ -27,7 +27,7 @@ public: QString generatedComponentTypePrefix() const; QString import3dTypePrefix() const; - QString import3dSimplifiedTypePrefix() const; + QString import3dTypePath() const; QString componentBundlesTypePrefix() const; QString composedEffectsTypePrefix() const; diff --git a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp index feeb9173a05..16d9217f6a6 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp @@ -461,8 +461,7 @@ QStringList SubComponentManager::quick3DAssetPaths() const const auto impPaths = importPaths(); QStringList retPaths; for (const auto &impPath : impPaths) { - QString path3d = m_componentUtils.import3dTypePrefix(); - path3d.replace('.', '/'); + QString path3d = m_componentUtils.import3dTypePath(); const QString assetPath = impPath + '/' + path3d; if (QFileInfo::exists(assetPath)) retPaths << assetPath; @@ -523,7 +522,7 @@ void SubComponentManager::update(const QUrl &filePath, const Imports &imports) // Remove old watched asset paths const QStringList watchPaths = m_watcher.directories(); - const QString &quick3DAssetFolder = m_componentUtils.import3dSimplifiedTypePrefix(); + const QString &quick3DAssetFolder = m_componentUtils.import3dTypePath(); for (const auto &watchPath : watchPaths) { if (watchPath.endsWith(quick3DAssetFolder)) m_watcher.removePath(watchPath); diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index e3772244a62..fb691097f72 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -82,7 +82,7 @@ inline constexpr char COMPONENT_BUNDLES_TYPE[] = "ComponentBundles"; inline constexpr char GENERATED_COMPONENTS_FOLDER[] = "GeneratedComponents"; inline constexpr char COMPONENT_BUNDLES_ASSET_REF_FILE[] = "_asset_ref.json"; inline constexpr char OLD_QUICK_3D_ASSETS_FOLDER[] = "Quick3DAssets"; -inline constexpr char QUICK_3D_COMPONENTS_FOLDER[] = "QtQuick3DComponents"; +inline constexpr char QUICK_3D_COMPONENTS_FOLDER[] = "QtQuick3D"; inline constexpr char QUICK_3D_ASSET_LIBRARY_ICON_SUFFIX[] = "_libicon"; inline constexpr char QUICK_3D_ASSET_IMPORT_DATA_NAME[] = "_importdata.json"; inline constexpr char QUICK_3D_ASSET_IMPORT_DATA_OPTIONS_KEY[] = "import_options"; From 1488548739f8521545d54a301ab1346d9a56a196 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 16 Apr 2024 11:23:19 +0200 Subject: [PATCH 151/202] QmlDesigner: Fix reflection for CheckBox MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ib0268e5a015fb74c6691e7b3c51ca03f1f90ba55 Reviewed-by: Henning GrĂĽndl --- .../imports/HelperWidgets/CheckBox.qml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/CheckBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/CheckBox.qml index 95788a9ec6a..7f1e6f5d461 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/CheckBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/CheckBox.qml @@ -22,17 +22,25 @@ StudioControls.CheckBox { labelColor: colorLogic.textColor + property bool __block: false + ColorLogic { id: colorLogic backendValue: checkBox.backendValue onValueFromBackendChanged: { + checkBox.__block = true if (colorLogic.valueFromBackend !== undefined && checkBox.checked !== colorLogic.valueFromBackend) checkBox.checked = colorLogic.valueFromBackend + checkBox.__block = false } + } onCheckedChanged: { + if (checkBox.__block) + return + if (backendValue.value !== checkBox.checked) backendValue.value = checkBox.checked } From 8ca37a3218c48f5e18812a42d1a2ae7f69971c4c Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 16 Apr 2024 11:30:48 +0200 Subject: [PATCH 152/202] Sqlite: Update to 3.45.3 Change-Id: I8f52308d56619147393238ec04dfedad75d4139f Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- src/libs/3rdparty/sqlite/sqlite3.c | 488 ++++++++++++++++++++++------- src/libs/3rdparty/sqlite/sqlite3.h | 25 +- 2 files changed, 392 insertions(+), 121 deletions(-) diff --git a/src/libs/3rdparty/sqlite/sqlite3.c b/src/libs/3rdparty/sqlite/sqlite3.c index 139ee46a6a9..08c593e55c7 100644 --- a/src/libs/3rdparty/sqlite/sqlite3.c +++ b/src/libs/3rdparty/sqlite/sqlite3.c @@ -1,6 +1,6 @@ /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite -** version 3.45.1. By combining all the individual C code files into this +** version 3.45.3. By combining all the individual C code files into this ** single large file, the entire code can be compiled as a single translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements @@ -18,7 +18,7 @@ ** separate file. This file contains only code for the core SQLite library. ** ** The content in this amalgamation comes from Fossil check-in -** e876e51a0ed5c5b3126f52e532044363a014. +** 8653b758870e6ef0c98d46b3ace27849054a. */ #define SQLITE_CORE 1 #define SQLITE_AMALGAMATION 1 @@ -459,9 +459,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.45.1" -#define SQLITE_VERSION_NUMBER 3045001 -#define SQLITE_SOURCE_ID "2024-01-30 16:01:20 e876e51a0ed5c5b3126f52e532044363a014bc594cfefa87ffb5b82257cc467a" +#define SQLITE_VERSION "3.45.3" +#define SQLITE_VERSION_NUMBER 3045003 +#define SQLITE_SOURCE_ID "2024-04-15 13:34:05 8653b758870e6ef0c98d46b3ace27849054af85da891eb121e9aaa537f1e8355" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -733,6 +733,8 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**); ** the 1st parameter to sqlite3_exec() while sqlite3_exec() is running. **
  • The application must not modify the SQL statement text passed into ** the 2nd parameter of sqlite3_exec() while sqlite3_exec() is running. +**
  • The application must not dereference the arrays or string pointers +** passed as the 3rd and 4th callback parameters after it returns. ** */ SQLITE_API int sqlite3_exec( @@ -2454,6 +2456,22 @@ struct sqlite3_mem_methods { ** configuration setting is never used, then the default maximum is determined ** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that ** compile-time option is not set, then the default maximum is 1073741824. +** +** [[SQLITE_CONFIG_ROWID_IN_VIEW]] +**
    SQLITE_CONFIG_ROWID_IN_VIEW +**
    The SQLITE_CONFIG_ROWID_IN_VIEW option enables or disables the ability +** for VIEWs to have a ROWID. The capability can only be enabled if SQLite is +** compiled with -DSQLITE_ALLOW_ROWID_IN_VIEW, in which case the capability +** defaults to on. This configuration option queries the current setting or +** changes the setting to off or on. The argument is a pointer to an integer. +** If that integer initially holds a value of 1, then the ability for VIEWs to +** have ROWIDs is activated. If the integer initially holds zero, then the +** ability is deactivated. Any other initial value for the integer leaves the +** setting unchanged. After changes, if any, the integer is written with +** a 1 or 0, if the ability for VIEWs to have ROWIDs is on or off. If SQLite +** is compiled without -DSQLITE_ALLOW_ROWID_IN_VIEW (which is the usual and +** recommended case) then the integer is always filled with zero, regardless +** if its initial value. **
  • */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ @@ -2485,6 +2503,7 @@ struct sqlite3_mem_methods { #define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */ #define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */ #define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */ +#define SQLITE_CONFIG_ROWID_IN_VIEW 30 /* int* */ /* ** CAPI3REF: Database Connection Configuration Options @@ -15097,6 +15116,7 @@ SQLITE_PRIVATE u32 sqlite3TreeTrace; ** 0x00010000 Beginning of DELETE/INSERT/UPDATE processing ** 0x00020000 Transform DISTINCT into GROUP BY ** 0x00040000 SELECT tree dump after all code has been generated +** 0x00080000 NOT NULL strength reduction */ /* @@ -18427,6 +18447,15 @@ struct Table { #define HasRowid(X) (((X)->tabFlags & TF_WithoutRowid)==0) #define VisibleRowid(X) (((X)->tabFlags & TF_NoVisibleRowid)==0) +/* Macro is true if the SQLITE_ALLOW_ROWID_IN_VIEW (mis-)feature is +** available. By default, this macro is false +*/ +#ifndef SQLITE_ALLOW_ROWID_IN_VIEW +# define ViewCanHaveRowid 0 +#else +# define ViewCanHaveRowid (sqlite3Config.mNoVisibleRowid==0) +#endif + /* ** Each foreign key constraint is an instance of the following structure. ** @@ -19346,6 +19375,7 @@ struct NameContext { #define NC_InAggFunc 0x020000 /* True if analyzing arguments to an agg func */ #define NC_FromDDL 0x040000 /* SQL text comes from sqlite_schema */ #define NC_NoSelect 0x080000 /* Do not descend into sub-selects */ +#define NC_Where 0x100000 /* Processing WHERE clause of a SELECT */ #define NC_OrderAgg 0x8000000 /* Has an aggregate other than count/min/max */ /* @@ -19369,6 +19399,7 @@ struct Upsert { Expr *pUpsertWhere; /* WHERE clause for the ON CONFLICT UPDATE */ Upsert *pNextUpsert; /* Next ON CONFLICT clause in the list */ u8 isDoUpdate; /* True for DO UPDATE. False for DO NOTHING */ + u8 isDup; /* True if 2nd or later with same pUpsertIdx */ /* Above this point is the parse tree for the ON CONFLICT clauses. ** The next group of fields stores intermediate data. */ void *pToFree; /* Free memory when deleting the Upsert object */ @@ -20139,6 +20170,11 @@ struct Sqlite3Config { #endif #ifndef SQLITE_UNTESTABLE int (*xTestCallback)(int); /* Invoked by sqlite3FaultSim() */ +#endif +#ifdef SQLITE_ALLOW_ROWID_IN_VIEW + u32 mNoVisibleRowid; /* TF_NoVisibleRowid if the ROWID_IN_VIEW + ** feature is disabled. 0 if rowids can + ** occur in views. */ #endif int bLocaltimeFault; /* True to fail localtime() calls */ int (*xAltLocaltime)(const void*,void*); /* Alternative localtime() routine */ @@ -20595,10 +20631,13 @@ SQLITE_PRIVATE void sqlite3MutexWarnOnContention(sqlite3_mutex*); # define EXP754 (((u64)0x7ff)<<52) # define MAN754 ((((u64)1)<<52)-1) # define IsNaN(X) (((X)&EXP754)==EXP754 && ((X)&MAN754)!=0) +# define IsOvfl(X) (((X)&EXP754)==EXP754) SQLITE_PRIVATE int sqlite3IsNaN(double); +SQLITE_PRIVATE int sqlite3IsOverflow(double); #else -# define IsNaN(X) 0 -# define sqlite3IsNaN(X) 0 +# define IsNaN(X) 0 +# define sqlite3IsNaN(X) 0 +# define sqlite3IsOVerflow(X) 0 #endif /* @@ -21444,7 +21483,7 @@ SQLITE_PRIVATE With *sqlite3WithPush(Parse*, With*, u8); SQLITE_PRIVATE Upsert *sqlite3UpsertNew(sqlite3*,ExprList*,Expr*,ExprList*,Expr*,Upsert*); SQLITE_PRIVATE void sqlite3UpsertDelete(sqlite3*,Upsert*); SQLITE_PRIVATE Upsert *sqlite3UpsertDup(sqlite3*,Upsert*); -SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget(Parse*,SrcList*,Upsert*); +SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget(Parse*,SrcList*,Upsert*,Upsert*); SQLITE_PRIVATE void sqlite3UpsertDoUpdate(Parse*,Upsert*,Table*,Index*,int); SQLITE_PRIVATE Upsert *sqlite3UpsertOfIndex(Upsert*,Index*); SQLITE_PRIVATE int sqlite3UpsertNextIsIPK(Upsert*); @@ -21834,6 +21873,9 @@ static const char * const sqlite3azCompileOpt[] = { "ALLOW_COVERING_INDEX_SCAN=" CTIMEOPT_VAL(SQLITE_ALLOW_COVERING_INDEX_SCAN), # endif #endif +#ifdef SQLITE_ALLOW_ROWID_IN_VIEW + "ALLOW_ROWID_IN_VIEW", +#endif #ifdef SQLITE_ALLOW_URI_AUTHORITY "ALLOW_URI_AUTHORITY", #endif @@ -22853,6 +22895,9 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = { #endif #ifndef SQLITE_UNTESTABLE 0, /* xTestCallback */ +#endif +#ifdef SQLITE_ALLOW_ROWID_IN_VIEW + 0, /* mNoVisibleRowid. 0 == allow rowid-in-view */ #endif 0, /* bLocaltimeFault */ 0, /* xAltLocaltime */ @@ -31309,6 +31354,7 @@ SQLITE_API void sqlite3_str_vappendf( if( xtype==etFLOAT ){ iRound = -precision; }else if( xtype==etGENERIC ){ + if( precision==0 ) precision = 1; iRound = precision; }else{ iRound = precision+1; @@ -34640,6 +34686,19 @@ SQLITE_PRIVATE int sqlite3IsNaN(double x){ } #endif /* SQLITE_OMIT_FLOATING_POINT */ +#ifndef SQLITE_OMIT_FLOATING_POINT +/* +** Return true if the floating point value is NaN or +Inf or -Inf. +*/ +SQLITE_PRIVATE int sqlite3IsOverflow(double x){ + int rc; /* The value return */ + u64 y; + memcpy(&y,&x,sizeof(y)); + rc = IsOvfl(y); + return rc; +} +#endif /* SQLITE_OMIT_FLOATING_POINT */ + /* ** Compute a string length that is limited to what can be stored in ** lower 30 bits of a 32-bit signed integer. @@ -35199,6 +35258,9 @@ do_atof_calc: u64 s2; rr[0] = (double)s; s2 = (u64)rr[0]; +#if defined(_MSC_VER) && _MSC_VER<1700 + if( s2==0x8000000000000000LL ){ s2 = 2*(u64)(0.5*rr[0]); } +#endif rr[1] = s>=s2 ? (double)(s - s2) : -(double)(s2 - s); if( e>0 ){ while( e>=100 ){ @@ -35641,7 +35703,7 @@ SQLITE_PRIVATE void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRou assert( p->n>0 ); assert( p->nzBuf) ); p->iDP = p->n + exp; - if( iRound<0 ){ + if( iRound<=0 ){ iRound = p->iDP - iRound; if( iRound==0 && p->zBuf[i+1]>='5' ){ iRound = 1; @@ -53262,6 +53324,14 @@ SQLITE_API unsigned char *sqlite3_serialize( pOut = 0; }else{ sz = sqlite3_column_int64(pStmt, 0)*szPage; + if( sz==0 ){ + sqlite3_reset(pStmt); + sqlite3_exec(db, "BEGIN IMMEDIATE; COMMIT;", 0, 0, 0); + rc = sqlite3_step(pStmt); + if( rc==SQLITE_ROW ){ + sz = sqlite3_column_int64(pStmt, 0)*szPage; + } + } if( piSize ) *piSize = sz; if( mFlags & SQLITE_SERIALIZE_NOCOPY ){ pOut = 0; @@ -63785,7 +63855,7 @@ SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager *pPager){ ** This will be either the rollback journal or the WAL file. */ SQLITE_PRIVATE sqlite3_file *sqlite3PagerJrnlFile(Pager *pPager){ -#if SQLITE_OMIT_WAL +#ifdef SQLITE_OMIT_WAL return pPager->jfd; #else return pPager->pWal ? sqlite3WalFile(pPager->pWal) : pPager->jfd; @@ -77088,7 +77158,10 @@ static int fillInCell( n = nHeader + nPayload; testcase( n==3 ); testcase( n==4 ); - if( n<4 ) n = 4; + if( n<4 ){ + n = 4; + pPayload[nPayload] = 0; + } *pnSize = n; assert( nSrc<=nPayload ); testcase( nSrcpBt->nPreformatSize; - if( szNew<4 ) szNew = 4; + if( szNew<4 ){ + szNew = 4; + newCell[3] = 0; + } if( ISAUTOVACUUM(p->pBt) && szNew>pPage->maxLocal ){ CellInfo info; pPage->xParseCell(pPage, newCell, &info); @@ -79596,7 +79672,7 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( }else if( loc<0 && pPage->nCell>0 ){ assert( pPage->leaf ); idx = ++pCur->ix; - pCur->curFlags &= ~BTCF_ValidNKey; + pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl); }else{ assert( pPage->leaf ); } @@ -79626,7 +79702,7 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( */ if( pPage->nOverflow ){ assert( rc==SQLITE_OK ); - pCur->curFlags &= ~(BTCF_ValidNKey); + pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl); rc = balance(pCur); /* Must make sure nOverflow is reset to zero even if the balance() @@ -88379,6 +88455,23 @@ static void serialGet( pMem->flags = IsNaN(x) ? MEM_Null : MEM_Real; } } +static int serialGet7( + const unsigned char *buf, /* Buffer to deserialize from */ + Mem *pMem /* Memory cell to write value into */ +){ + u64 x = FOUR_BYTE_UINT(buf); + u32 y = FOUR_BYTE_UINT(buf+4); + x = (x<<32) + y; + assert( sizeof(x)==8 && sizeof(pMem->u.r)==8 ); + swapMixedEndianFloat(x); + memcpy(&pMem->u.r, &x, sizeof(x)); + if( IsNaN(x) ){ + pMem->flags = MEM_Null; + return 1; + } + pMem->flags = MEM_Real; + return 0; +} SQLITE_PRIVATE void sqlite3VdbeSerialGet( const unsigned char *buf, /* Buffer to deserialize from */ u32 serial_type, /* Serial type to deserialize */ @@ -89058,7 +89151,7 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip( }else if( serial_type==0 ){ rc = -1; }else if( serial_type==7 ){ - sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1); + serialGet7(&aKey1[d1], &mem1); rc = -sqlite3IntFloatCompare(pRhs->u.i, mem1.u.r); }else{ i64 lhs = vdbeRecordDecodeInt(serial_type, &aKey1[d1]); @@ -89083,14 +89176,18 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip( }else if( serial_type==0 ){ rc = -1; }else{ - sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1); if( serial_type==7 ){ - if( mem1.u.ru.r ){ + if( serialGet7(&aKey1[d1], &mem1) ){ + rc = -1; /* mem1 is a NaN */ + }else if( mem1.u.ru.r ){ rc = -1; }else if( mem1.u.r>pRhs->u.r ){ rc = +1; + }else{ + assert( rc==0 ); } }else{ + sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1); rc = sqlite3IntFloatCompare(mem1.u.i, pRhs->u.r); } } @@ -89160,7 +89257,14 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip( /* RHS is null */ else{ serial_type = aKey1[idx1]; - rc = (serial_type!=0 && serial_type!=10); + if( serial_type==0 + || serial_type==10 + || (serial_type==7 && serialGet7(&aKey1[d1], &mem1)!=0) + ){ + assert( rc==0 ); + }else{ + rc = 1; + } } if( rc!=0 ){ @@ -94858,7 +94962,9 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ } } }else if( affinity==SQLITE_AFF_TEXT && ((flags1 | flags3) & MEM_Str)!=0 ){ - if( (flags1 & MEM_Str)==0 && (flags1&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){ + if( (flags1 & MEM_Str)!=0 ){ + pIn1->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal); + }else if( (flags1&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){ testcase( pIn1->flags & MEM_Int ); testcase( pIn1->flags & MEM_Real ); testcase( pIn1->flags & MEM_IntReal ); @@ -94867,7 +94973,9 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ flags1 = (pIn1->flags & ~MEM_TypeMask) | (flags1 & MEM_TypeMask); if( NEVER(pIn1==pIn3) ) flags3 = flags1 | MEM_Str; } - if( (flags3 & MEM_Str)==0 && (flags3&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){ + if( (flags3 & MEM_Str)!=0 ){ + pIn3->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal); + }else if( (flags3&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){ testcase( pIn3->flags & MEM_Int ); testcase( pIn3->flags & MEM_Real ); testcase( pIn3->flags & MEM_IntReal ); @@ -106212,6 +106320,8 @@ static void resolveAlias( assert( iCol>=0 && iColnExpr ); pOrig = pEList->a[iCol].pExpr; assert( pOrig!=0 ); + assert( !ExprHasProperty(pExpr, EP_Reduced|EP_TokenOnly) ); + if( pExpr->pAggInfo ) return; db = pParse->db; pDup = sqlite3ExprDup(db, pOrig, 0); if( db->mallocFailed ){ @@ -106599,8 +106709,37 @@ static int lookupName( } } if( 0==cnt && VisibleRowid(pTab) ){ + /* pTab is a potential ROWID match. Keep track of it and match + ** the ROWID later if that seems appropriate. (Search for "cntTab" + ** to find related code.) Only allow a ROWID match if there is + ** a single ROWID match candidate. + */ +#ifdef SQLITE_ALLOW_ROWID_IN_VIEW + /* In SQLITE_ALLOW_ROWID_IN_VIEW mode, allow a ROWID match + ** if there is a single VIEW candidate or if there is a single + ** non-VIEW candidate plus multiple VIEW candidates. In other + ** words non-VIEW candidate terms take precedence over VIEWs. + */ + if( cntTab==0 + || (cntTab==1 + && ALWAYS(pMatch!=0) + && ALWAYS(pMatch->pTab!=0) + && (pMatch->pTab->tabFlags & TF_Ephemeral)!=0 + && (pTab->tabFlags & TF_Ephemeral)==0) + ){ + cntTab = 1; + pMatch = pItem; + }else{ + cntTab++; + } +#else + /* The (much more common) non-SQLITE_ALLOW_ROWID_IN_VIEW case is + ** simpler since we require exactly one candidate, which will + ** always be a non-VIEW + */ cntTab++; pMatch = pItem; +#endif } } if( pMatch ){ @@ -106726,13 +106865,13 @@ static int lookupName( ** Perhaps the name is a reference to the ROWID */ if( cnt==0 - && cntTab==1 + && cntTab>=1 && pMatch && (pNC->ncFlags & (NC_IdxExpr|NC_GenCol))==0 && sqlite3IsRowid(zCol) && ALWAYS(VisibleRowid(pMatch->pTab) || pMatch->fg.isNestedFrom) ){ - cnt = 1; + cnt = cntTab; if( pMatch->fg.isNestedFrom==0 ) pExpr->iColumn = -1; pExpr->affExpr = SQLITE_AFF_INTEGER; } @@ -107097,6 +107236,19 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ ** resolved. This prevents "column" from being counted as having been ** referenced, which might prevent a SELECT from being erroneously ** marked as correlated. + ** + ** 2024-03-28: Beware of aggregates. A bare column of aggregated table + ** can still evaluate to NULL even though it is marked as NOT NULL. + ** Example: + ** + ** CREATE TABLE t1(a INT NOT NULL); + ** SELECT a, a IS NULL, a IS NOT NULL, count(*) FROM t1; + ** + ** The "a IS NULL" and "a IS NOT NULL" expressions cannot be optimized + ** here because at the time this case is hit, we do not yet know whether + ** or not t1 is being aggregated. We have to assume the worst and omit + ** the optimization. The only time it is safe to apply this optimization + ** is within the WHERE clause. */ case TK_NOTNULL: case TK_ISNULL: { @@ -107107,19 +107259,36 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ anRef[i] = p->nRef; } sqlite3WalkExpr(pWalker, pExpr->pLeft); - if( 0==sqlite3ExprCanBeNull(pExpr->pLeft) && !IN_RENAME_OBJECT ){ - testcase( ExprHasProperty(pExpr, EP_OuterON) ); - assert( !ExprHasProperty(pExpr, EP_IntValue) ); - 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]; - } - sqlite3ExprDelete(pParse->db, pExpr->pLeft); - pExpr->pLeft = 0; + if( IN_RENAME_OBJECT ) return WRC_Prune; + if( sqlite3ExprCanBeNull(pExpr->pLeft) ){ + /* The expression can be NULL. So the optimization does not apply */ + return WRC_Prune; } + + for(i=0, p=pNC; p; p=p->pNext, i++){ + if( (p->ncFlags & NC_Where)==0 ){ + return WRC_Prune; /* Not in a WHERE clause. Unsafe to optimize. */ + } + } + testcase( ExprHasProperty(pExpr, EP_OuterON) ); + assert( !ExprHasProperty(pExpr, EP_IntValue) ); +#if TREETRACE_ENABLED + if( sqlite3TreeTrace & 0x80000 ){ + sqlite3DebugPrintf( + "NOT NULL strength reduction converts the following to %d:\n", + pExpr->op==TK_NOTNULL + ); + sqlite3ShowExpr(pExpr); + } +#endif /* TREETRACE_ENABLED */ + 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]; + } + sqlite3ExprDelete(pParse->db, pExpr->pLeft); + pExpr->pLeft = 0; return WRC_Prune; } @@ -108019,7 +108188,9 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ } if( sqlite3ResolveExprNames(&sNC, p->pHaving) ) return WRC_Abort; } + sNC.ncFlags |= NC_Where; if( sqlite3ResolveExprNames(&sNC, p->pWhere) ) return WRC_Abort; + sNC.ncFlags &= ~NC_Where; /* Resolve names in table-valued-function arguments */ for(i=0; ipSrc->nSrc; i++){ @@ -108558,9 +108729,10 @@ SQLITE_PRIVATE Expr *sqlite3ExprSkipCollateAndLikely(Expr *pExpr){ assert( pExpr->x.pList->nExpr>0 ); assert( pExpr->op==TK_FUNCTION ); pExpr = pExpr->x.pList->a[0].pExpr; - }else{ - assert( pExpr->op==TK_COLLATE ); + }else if( pExpr->op==TK_COLLATE ){ pExpr = pExpr->pLeft; + }else{ + break; } } return pExpr; @@ -111079,9 +111251,12 @@ SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr *p){ return 0; case TK_COLUMN: assert( ExprUseYTab(p) ); - return ExprHasProperty(p, EP_CanBeNull) || - NEVER(p->y.pTab==0) || /* Reference to column of index on expr */ - (p->iColumn>=0 + return ExprHasProperty(p, EP_CanBeNull) + || NEVER(p->y.pTab==0) /* Reference to column of index on expr */ +#ifdef SQLITE_ALLOW_ROWID_IN_VIEW + || (p->iColumn==XN_ROWID && IsView(p->y.pTab)) +#endif + || (p->iColumn>=0 && p->y.pTab->aCol!=0 /* Possible due to prior error */ && ALWAYS(p->iColumny.pTab->nCol) && p->y.pTab->aCol[p->iColumn].notNull==0); @@ -123572,9 +123747,12 @@ SQLITE_PRIVATE void sqlite3CreateView( ** on a view, even though views do not have rowids. The following flag ** setting fixes this problem. But the fix can be disabled by compiling ** with -DSQLITE_ALLOW_ROWID_IN_VIEW in case there are legacy apps that - ** depend upon the old buggy behavior. */ -#ifndef SQLITE_ALLOW_ROWID_IN_VIEW - p->tabFlags |= TF_NoVisibleRowid; + ** depend upon the old buggy behavior. The ability can also be toggled + ** using sqlite3_config(SQLITE_CONFIG_ROWID_IN_VIEW,...) */ +#ifdef SQLITE_ALLOW_ROWID_IN_VIEW + p->tabFlags |= sqlite3Config.mNoVisibleRowid; /* Optional. Allow by default */ +#else + p->tabFlags |= TF_NoVisibleRowid; /* Never allow rowid in view */ #endif sqlite3TwoPartName(pParse, pName1, pName2, &pName); @@ -128947,13 +129125,13 @@ SQLITE_PRIVATE void sqlite3QuoteValue(StrAccum *pStr, sqlite3_value *pValue){ double r1, r2; const char *zVal; r1 = sqlite3_value_double(pValue); - sqlite3_str_appendf(pStr, "%!.15g", r1); + sqlite3_str_appendf(pStr, "%!0.15g", r1); zVal = sqlite3_str_value(pStr); if( zVal ){ sqlite3AtoF(zVal, &r2, pStr->nChar, SQLITE_UTF8); if( r1!=r2 ){ sqlite3_str_reset(pStr); - sqlite3_str_appendf(pStr, "%!.20e", r1); + sqlite3_str_appendf(pStr, "%!0.20e", r1); } } break; @@ -129255,7 +129433,7 @@ static void replaceFunc( } if( zPattern[0]==0 ){ assert( sqlite3_value_type(argv[1])!=SQLITE_NULL ); - sqlite3_result_value(context, argv[0]); + sqlite3_result_text(context, (const char*)zStr, nStr, SQLITE_TRANSIENT); return; } nPattern = sqlite3_value_bytes(argv[1]); @@ -129738,7 +129916,7 @@ static void sumFinalize(sqlite3_context *context){ if( p->approx ){ if( p->ovrfl ){ sqlite3_result_error(context,"integer overflow",-1); - }else if( !sqlite3IsNaN(p->rErr) ){ + }else if( !sqlite3IsOverflow(p->rErr) ){ sqlite3_result_double(context, p->rSum+p->rErr); }else{ sqlite3_result_double(context, p->rSum); @@ -129755,7 +129933,7 @@ static void avgFinalize(sqlite3_context *context){ double r; if( p->approx ){ r = p->rSum; - if( !sqlite3IsNaN(p->rErr) ) r += p->rErr; + if( !sqlite3IsOverflow(p->rErr) ) r += p->rErr; }else{ r = (double)(p->iSum); } @@ -129769,7 +129947,7 @@ static void totalFinalize(sqlite3_context *context){ if( p ){ if( p->approx ){ r = p->rSum; - if( !sqlite3IsNaN(p->rErr) ) r += p->rErr; + if( !sqlite3IsOverflow(p->rErr) ) r += p->rErr; }else{ r = (double)(p->iSum); } @@ -133175,7 +133353,7 @@ SQLITE_PRIVATE void sqlite3Insert( pNx->iDataCur = iDataCur; pNx->iIdxCur = iIdxCur; if( pNx->pUpsertTarget ){ - if( sqlite3UpsertAnalyzeTarget(pParse, pTabList, pNx) ){ + if( sqlite3UpsertAnalyzeTarget(pParse, pTabList, pNx, pUpsert) ){ goto insert_cleanup; } } @@ -135067,7 +135245,10 @@ static int xferOptimization( } } #ifndef SQLITE_OMIT_CHECK - if( pDest->pCheck && sqlite3ExprListCompare(pSrc->pCheck,pDest->pCheck,-1) ){ + if( pDest->pCheck + && (db->mDbFlags & DBFLAG_Vacuum)==0 + && sqlite3ExprListCompare(pSrc->pCheck,pDest->pCheck,-1) + ){ return 0; /* Tables have different CHECK constraints. Ticket #2252 */ } #endif @@ -139474,31 +139655,7 @@ SQLITE_PRIVATE void sqlite3Pragma( int mxCol; /* Maximum non-virtual column number */ if( pObjTab && pObjTab!=pTab ) continue; - if( !IsOrdinaryTable(pTab) ){ -#ifndef SQLITE_OMIT_VIRTUALTABLE - sqlite3_vtab *pVTab; - int a1; - if( !IsVirtual(pTab) ) continue; - if( pTab->nCol<=0 ){ - const char *zMod = pTab->u.vtab.azArg[0]; - if( sqlite3HashFind(&db->aModule, zMod)==0 ) continue; - } - sqlite3ViewGetColumnNames(pParse, pTab); - if( pTab->u.vtab.p==0 ) continue; - pVTab = pTab->u.vtab.p->pVtab; - if( NEVER(pVTab==0) ) continue; - if( NEVER(pVTab->pModule==0) ) continue; - if( pVTab->pModule->iVersion<4 ) continue; - if( pVTab->pModule->xIntegrity==0 ) continue; - sqlite3VdbeAddOp3(v, OP_VCheck, i, 3, isQuick); - pTab->nTabRef++; - sqlite3VdbeAppendP4(v, pTab, P4_TABLEREF); - a1 = sqlite3VdbeAddOp1(v, OP_IsNull, 3); VdbeCoverage(v); - integrityCheckResultRow(v); - sqlite3VdbeJumpHere(v, a1); -#endif - continue; - } + if( !IsOrdinaryTable(pTab) ) continue; if( isQuick || HasRowid(pTab) ){ pPk = 0; r2 = 0; @@ -139633,6 +139790,7 @@ SQLITE_PRIVATE void sqlite3Pragma( ** 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); + sqlite3ColumnDefault(v, pTab, j, 3); jmp3 = sqlite3VdbeAddOp2(v, OP_NotNull, 3, labelOk); VdbeCoverage(v); } @@ -139823,6 +139981,38 @@ SQLITE_PRIVATE void sqlite3Pragma( } } } + +#ifndef SQLITE_OMIT_VIRTUALTABLE + /* Second pass to invoke the xIntegrity method on all virtual + ** tables. + */ + for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ + Table *pTab = sqliteHashData(x); + sqlite3_vtab *pVTab; + int a1; + if( pObjTab && pObjTab!=pTab ) continue; + if( IsOrdinaryTable(pTab) ) continue; + if( !IsVirtual(pTab) ) continue; + if( pTab->nCol<=0 ){ + const char *zMod = pTab->u.vtab.azArg[0]; + if( sqlite3HashFind(&db->aModule, zMod)==0 ) continue; + } + sqlite3ViewGetColumnNames(pParse, pTab); + if( pTab->u.vtab.p==0 ) continue; + pVTab = pTab->u.vtab.p->pVtab; + if( NEVER(pVTab==0) ) continue; + if( NEVER(pVTab->pModule==0) ) continue; + if( pVTab->pModule->iVersion<4 ) continue; + if( pVTab->pModule->xIntegrity==0 ) continue; + sqlite3VdbeAddOp3(v, OP_VCheck, i, 3, isQuick); + pTab->nTabRef++; + sqlite3VdbeAppendP4(v, pTab, P4_TABLEREF); + a1 = sqlite3VdbeAddOp1(v, OP_IsNull, 3); VdbeCoverage(v); + integrityCheckResultRow(v); + sqlite3VdbeJumpHere(v, a1); + continue; + } +#endif } { static const int iLn = VDBE_OFFSET_LINENO(2); @@ -140459,7 +140649,11 @@ static int pragmaVtabBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ j = seen[0]-1; pIdxInfo->aConstraintUsage[j].argvIndex = 1; pIdxInfo->aConstraintUsage[j].omit = 1; - if( seen[1]==0 ) return SQLITE_OK; + if( seen[1]==0 ){ + pIdxInfo->estimatedCost = (double)1000; + pIdxInfo->estimatedRows = 1000; + return SQLITE_OK; + } pIdxInfo->estimatedCost = (double)20; pIdxInfo->estimatedRows = 20; j = seen[1]-1; @@ -143686,11 +143880,7 @@ static const char *columnTypeImpl( ** data for the result-set column of the sub-select. */ if( iColpEList->nExpr -#ifdef SQLITE_ALLOW_ROWID_IN_VIEW - && iCol>=0 -#else - && ALWAYS(iCol>=0) -#endif + && (!ViewCanHaveRowid || iCol>=0) ){ /* If iCol is less than zero, then the expression requests the ** rowid of the sub-select or view. This expression is legal (see @@ -146865,6 +147055,10 @@ static int pushDownWindowCheck(Parse *pParse, Select *pSubq, Expr *pExpr){ ** ** (11) The subquery is not a VALUES clause ** +** (12) The WHERE clause is not "rowid ISNULL" or the equivalent. This +** case only comes up if SQLite is compiled using +** SQLITE_ALLOW_ROWID_IN_VIEW. +** ** Return 0 if no changes are made and non-zero if one or more WHERE clause ** terms are duplicated into the subquery. */ @@ -146975,6 +147169,18 @@ static int pushDownWhereTerms( } #endif +#ifdef SQLITE_ALLOW_ROWID_IN_VIEW + if( ViewCanHaveRowid && (pWhere->op==TK_ISNULL || pWhere->op==TK_NOTNULL) ){ + Expr *pLeft = pWhere->pLeft; + if( ALWAYS(pLeft) + && pLeft->op==TK_COLUMN + && pLeft->iColumn < 0 + ){ + return 0; /* Restriction (12) */ + } + } +#endif + if( sqlite3ExprIsSingleTableConstraint(pWhere, pSrcList, iSrc) ){ nChng++; pSubq->selFlags |= SF_PushDown; @@ -147602,12 +147808,14 @@ SQLITE_PRIVATE int sqlite3ExpandSubquery(Parse *pParse, SrcItem *pFrom){ while( pSel->pPrior ){ pSel = pSel->pPrior; } sqlite3ColumnsFromExprList(pParse, pSel->pEList,&pTab->nCol,&pTab->aCol); pTab->iPKey = -1; + pTab->eTabType = TABTYP_VIEW; pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) ); #ifndef SQLITE_ALLOW_ROWID_IN_VIEW /* The usual case - do not allow ROWID on a subquery */ pTab->tabFlags |= TF_Ephemeral | TF_NoVisibleRowid; #else - pTab->tabFlags |= TF_Ephemeral; /* Legacy compatibility mode */ + /* Legacy compatibility mode */ + pTab->tabFlags |= TF_Ephemeral | sqlite3Config.mNoVisibleRowid; #endif return pParse->nErr ? SQLITE_ERROR : SQLITE_OK; } @@ -147875,7 +148083,7 @@ static int selectExpander(Walker *pWalker, Select *p){ pNestedFrom = pFrom->pSelect->pEList; assert( pNestedFrom!=0 ); assert( pNestedFrom->nExpr==pTab->nCol ); - assert( VisibleRowid(pTab)==0 ); + assert( VisibleRowid(pTab)==0 || ViewCanHaveRowid ); }else{ if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){ continue; @@ -147907,7 +148115,8 @@ static int selectExpander(Walker *pWalker, Select *p){ pUsing = 0; } - nAdd = pTab->nCol + (VisibleRowid(pTab) && (selFlags&SF_NestedFrom)); + nAdd = pTab->nCol; + if( VisibleRowid(pTab) && (selFlags & SF_NestedFrom)!=0 ) nAdd++; for(j=0; ja[pNew->nExpr-1]; assert( pX->zEName==0 ); if( (selFlags & SF_NestedFrom)!=0 && !IN_RENAME_OBJECT ){ - if( pNestedFrom ){ + if( pNestedFrom && (!ViewCanHaveRowid || jnExpr) ){ + assert( jnExpr ); pX->zEName = sqlite3DbStrDup(db, pNestedFrom->a[j].zEName); testcase( pX->zEName==0 ); }else{ @@ -152923,6 +153133,9 @@ SQLITE_PRIVATE void sqlite3Update( } } if( chngRowid==0 && pPk==0 ){ +#ifdef SQLITE_ALLOW_ROWID_IN_VIEW + if( isView ) sqlite3VdbeAddOp2(v, OP_Null, 0, regOldRowid); +#endif sqlite3VdbeAddOp2(v, OP_Copy, regOldRowid, regNewRowid); } } @@ -153460,7 +153673,8 @@ SQLITE_PRIVATE Upsert *sqlite3UpsertNew( SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget( Parse *pParse, /* The parsing context */ SrcList *pTabList, /* Table into which we are inserting */ - Upsert *pUpsert /* The ON CONFLICT clauses */ + Upsert *pUpsert, /* The ON CONFLICT clauses */ + Upsert *pAll /* Complete list of all ON CONFLICT clauses */ ){ Table *pTab; /* That table into which we are inserting */ int rc; /* Result code */ @@ -153563,6 +153777,14 @@ SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget( continue; } pUpsert->pUpsertIdx = pIdx; + if( sqlite3UpsertOfIndex(pAll,pIdx)!=pUpsert ){ + /* Really this should be an error. The isDup ON CONFLICT clause will + ** never fire. But this problem was not discovered until three years + ** after multi-CONFLICT upsert was added, and so we silently ignore + ** the problem to prevent breaking applications that might actually + ** have redundant ON CONFLICT clauses. */ + pUpsert->isDup = 1; + } break; } if( pUpsert->pUpsertIdx==0 ){ @@ -153589,9 +153811,13 @@ SQLITE_PRIVATE int sqlite3UpsertNextIsIPK(Upsert *pUpsert){ Upsert *pNext; if( NEVER(pUpsert==0) ) return 0; pNext = pUpsert->pNextUpsert; - if( pNext==0 ) return 1; - if( pNext->pUpsertTarget==0 ) return 1; - if( pNext->pUpsertIdx==0 ) return 1; + while( 1 /*exit-by-return*/ ){ + if( pNext==0 ) return 1; + if( pNext->pUpsertTarget==0 ) return 1; + if( pNext->pUpsertIdx==0 ) return 1; + if( !pNext->isDup ) return 0; + pNext = pNext->pNextUpsert; + } return 0; } @@ -166619,16 +166845,10 @@ static SQLITE_NOINLINE void whereAddIndexedExpr( for(i=0; inColumn; i++){ Expr *pExpr; int j = pIdx->aiColumn[i]; - int bMaybeNullRow; if( j==XN_EXPR ){ pExpr = pIdx->aColExpr->a[i].pExpr; - testcase( pTabItem->fg.jointype & JT_LEFT ); - testcase( pTabItem->fg.jointype & JT_RIGHT ); - testcase( pTabItem->fg.jointype & JT_LTORJ ); - bMaybeNullRow = (pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0; }else if( j>=0 && (pTab->aCol[j].colFlags & COLFLAG_VIRTUAL)!=0 ){ pExpr = sqlite3ColumnExpr(pTab, &pTab->aCol[j]); - bMaybeNullRow = 0; }else{ continue; } @@ -166660,7 +166880,7 @@ static SQLITE_NOINLINE void whereAddIndexedExpr( p->iDataCur = pTabItem->iCursor; p->iIdxCur = iIdxCur; p->iIdxCol = i; - p->bMaybeNullRow = bMaybeNullRow; + p->bMaybeNullRow = (pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0; if( sqlite3IndexAffinityStr(pParse->db, pIdx) ){ p->aff = pIdx->zColAff[i]; } @@ -178865,6 +179085,18 @@ SQLITE_API int sqlite3_config(int op, ...){ } #endif /* SQLITE_OMIT_DESERIALIZE */ + case SQLITE_CONFIG_ROWID_IN_VIEW: { + int *pVal = va_arg(ap,int*); +#ifdef SQLITE_ALLOW_ROWID_IN_VIEW + if( 0==*pVal ) sqlite3GlobalConfig.mNoVisibleRowid = TF_NoVisibleRowid; + if( 1==*pVal ) sqlite3GlobalConfig.mNoVisibleRowid = 0; + *pVal = (sqlite3GlobalConfig.mNoVisibleRowid==0); +#else + *pVal = 0; +#endif + break; + } + default: { rc = SQLITE_ERROR; break; @@ -204785,6 +205017,7 @@ json_parse_restart: case '[': { /* Parse array */ iThis = pParse->nBlob; + assert( i<=(u32)pParse->nJson ); jsonBlobAppendNode(pParse, JSONB_ARRAY, pParse->nJson - i, 0); iStart = pParse->nBlob; if( pParse->oom ) return -1; @@ -205183,6 +205416,10 @@ static void jsonReturnStringAsBlob(JsonString *pStr){ JsonParse px; memset(&px, 0, sizeof(px)); jsonStringTerminate(pStr); + if( pStr->eErr ){ + sqlite3_result_error_nomem(pStr->pCtx); + return; + } px.zJson = pStr->zBuf; px.nJson = pStr->nUsed; px.db = sqlite3_context_db_handle(pStr->pCtx); @@ -206508,8 +206745,9 @@ rebuild_from_cache: } p->zJson = (char*)sqlite3_value_text(pArg); p->nJson = sqlite3_value_bytes(pArg); + if( db->mallocFailed ) goto json_pfa_oom; if( p->nJson==0 ) goto json_pfa_malformed; - if( NEVER(p->zJson==0) ) goto json_pfa_oom; + assert( p->zJson!=0 ); if( jsonConvertTextToBlob(p, (flgs & JSON_KEEPERROR) ? 0 : ctx) ){ if( flgs & JSON_KEEPERROR ){ p->nErr = 1; @@ -206675,10 +206913,10 @@ static void jsonDebugPrintBlob( if( sz==0 && x<=JSONB_FALSE ){ sqlite3_str_append(pOut, "\n", 1); }else{ - u32 i; + u32 j; sqlite3_str_appendall(pOut, ": \""); - for(i=iStart+n; iaBlob[i]; + for(j=iStart+n; jaBlob[j]; if( c<0x20 || c>=0x7f ) c = '.'; sqlite3_str_append(pOut, (char*)&c, 1); } @@ -208086,6 +208324,9 @@ static int jsonEachColumn( case JEACH_VALUE: { u32 i = jsonSkipLabel(p); jsonReturnFromBlob(&p->sParse, i, ctx, 1); + if( (p->sParse.aBlob[i] & 0x0f)>=JSONB_ARRAY ){ + sqlite3_result_subtype(ctx, JSON_SUBTYPE); + } break; } case JEACH_TYPE: { @@ -208132,9 +208373,9 @@ static int jsonEachColumn( case JEACH_JSON: { if( p->sParse.zJson==0 ){ sqlite3_result_blob(ctx, p->sParse.aBlob, p->sParse.nBlob, - SQLITE_STATIC); + SQLITE_TRANSIENT); }else{ - sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC); + sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_TRANSIENT); } break; } @@ -209160,11 +209401,9 @@ static RtreeNode *nodeNew(Rtree *pRtree, RtreeNode *pParent){ ** Clear the Rtree.pNodeBlob object */ static void nodeBlobReset(Rtree *pRtree){ - if( pRtree->pNodeBlob && pRtree->inWrTrans==0 && pRtree->nCursor==0 ){ - sqlite3_blob *pBlob = pRtree->pNodeBlob; - pRtree->pNodeBlob = 0; - sqlite3_blob_close(pBlob); - } + sqlite3_blob *pBlob = pRtree->pNodeBlob; + pRtree->pNodeBlob = 0; + sqlite3_blob_close(pBlob); } /* @@ -209208,7 +209447,6 @@ static int nodeAcquire( &pRtree->pNodeBlob); } if( rc ){ - nodeBlobReset(pRtree); *ppNode = 0; /* If unable to open an sqlite3_blob on the desired row, that can only ** be because the shadow tables hold erroneous data. */ @@ -209268,6 +209506,7 @@ static int nodeAcquire( } *ppNode = pNode; }else{ + nodeBlobReset(pRtree); if( pNode ){ pRtree->nNodeRef--; sqlite3_free(pNode); @@ -209412,6 +209651,7 @@ static void nodeGetCoord( int iCoord, /* Which coordinate to extract */ RtreeCoord *pCoord /* OUT: Space to write result to */ ){ + assert( iCellzData[12 + pRtree->nBytesPerCell*iCell + 4*iCoord], pCoord); } @@ -209601,7 +209841,9 @@ static int rtreeClose(sqlite3_vtab_cursor *cur){ sqlite3_finalize(pCsr->pReadAux); sqlite3_free(pCsr); pRtree->nCursor--; - nodeBlobReset(pRtree); + if( pRtree->nCursor==0 && pRtree->inWrTrans==0 ){ + nodeBlobReset(pRtree); + } return SQLITE_OK; } @@ -210186,7 +210428,11 @@ static int rtreeRowid(sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *pRowid){ int rc = SQLITE_OK; RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc); if( rc==SQLITE_OK && ALWAYS(p) ){ - *pRowid = nodeGetRowid(RTREE_OF_CURSOR(pCsr), pNode, p->iCell); + if( p->iCell>=NCELL(pNode) ){ + rc = SQLITE_ABORT; + }else{ + *pRowid = nodeGetRowid(RTREE_OF_CURSOR(pCsr), pNode, p->iCell); + } } return rc; } @@ -210204,6 +210450,7 @@ static int rtreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ if( rc ) return rc; if( NEVER(p==0) ) return SQLITE_OK; + if( p->iCell>=NCELL(pNode) ) return SQLITE_ABORT; if( i==0 ){ sqlite3_result_int64(ctx, nodeGetRowid(pRtree, pNode, p->iCell)); }else if( i<=pRtree->nDim2 ){ @@ -211685,8 +211932,7 @@ constraint: */ static int rtreeBeginTransaction(sqlite3_vtab *pVtab){ Rtree *pRtree = (Rtree *)pVtab; - assert( pRtree->inWrTrans==0 ); - pRtree->inWrTrans++; + pRtree->inWrTrans = 1; return SQLITE_OK; } @@ -211700,6 +211946,9 @@ static int rtreeEndTransaction(sqlite3_vtab *pVtab){ nodeBlobReset(pRtree); return SQLITE_OK; } +static int rtreeRollback(sqlite3_vtab *pVtab){ + return rtreeEndTransaction(pVtab); +} /* ** The xRename method for rtree module virtual tables. @@ -211818,7 +212067,7 @@ static sqlite3_module rtreeModule = { rtreeBeginTransaction, /* xBegin - begin transaction */ rtreeEndTransaction, /* xSync - sync transaction */ rtreeEndTransaction, /* xCommit - commit transaction */ - rtreeEndTransaction, /* xRollback - rollback transaction */ + rtreeRollback, /* xRollback - rollback transaction */ 0, /* xFindFunction - function overloading */ rtreeRename, /* xRename - rename the table */ rtreeSavepoint, /* xSavepoint */ @@ -245377,23 +245626,26 @@ static void fts5IterSetOutputsTokendata(Fts5Iter *pIter){ static void fts5TokendataIterNext(Fts5Iter *pIter, int bFrom, i64 iFrom){ int ii; Fts5TokenDataIter *pT = pIter->pTokenDataIter; + Fts5Index *pIndex = pIter->pIndex; for(ii=0; iinIter; ii++){ Fts5Iter *p = pT->apIter[ii]; if( p->base.bEof==0 && (p->base.iRowid==pIter->base.iRowid || (bFrom && p->base.iRowidpIndex, p, bFrom, iFrom); + fts5MultiIterNext(pIndex, p, bFrom, iFrom); while( bFrom && p->base.bEof==0 && p->base.iRowidpIndex->rc==SQLITE_OK + && pIndex->rc==SQLITE_OK ){ - fts5MultiIterNext(p->pIndex, p, 0, 0); + fts5MultiIterNext(pIndex, p, 0, 0); } } } - fts5IterSetOutputsTokendata(pIter); + if( pIndex->rc==SQLITE_OK ){ + fts5IterSetOutputsTokendata(pIter); + } } /* @@ -250547,7 +250799,7 @@ static void fts5SourceIdFunc( ){ assert( nArg==0 ); UNUSED_PARAM2(nArg, apUnused); - sqlite3_result_text(pCtx, "fts5: 2024-01-30 16:01:20 e876e51a0ed5c5b3126f52e532044363a014bc594cfefa87ffb5b82257cc467a", -1, SQLITE_TRANSIENT); + sqlite3_result_text(pCtx, "fts5: 2024-04-15 13:34:05 8653b758870e6ef0c98d46b3ace27849054af85da891eb121e9aaa537f1e8355", -1, SQLITE_TRANSIENT); } /* diff --git a/src/libs/3rdparty/sqlite/sqlite3.h b/src/libs/3rdparty/sqlite/sqlite3.h index 4fdfde004eb..2618b37a7b8 100644 --- a/src/libs/3rdparty/sqlite/sqlite3.h +++ b/src/libs/3rdparty/sqlite/sqlite3.h @@ -146,9 +146,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.45.1" -#define SQLITE_VERSION_NUMBER 3045001 -#define SQLITE_SOURCE_ID "2024-01-30 16:01:20 e876e51a0ed5c5b3126f52e532044363a014bc594cfefa87ffb5b82257cc467a" +#define SQLITE_VERSION "3.45.3" +#define SQLITE_VERSION_NUMBER 3045003 +#define SQLITE_SOURCE_ID "2024-04-15 13:34:05 8653b758870e6ef0c98d46b3ace27849054af85da891eb121e9aaa537f1e8355" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -420,6 +420,8 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**); ** the 1st parameter to sqlite3_exec() while sqlite3_exec() is running. **
  • The application must not modify the SQL statement text passed into ** the 2nd parameter of sqlite3_exec() while sqlite3_exec() is running. +**
  • The application must not dereference the arrays or string pointers +** passed as the 3rd and 4th callback parameters after it returns. ** */ SQLITE_API int sqlite3_exec( @@ -2141,6 +2143,22 @@ struct sqlite3_mem_methods { ** configuration setting is never used, then the default maximum is determined ** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that ** compile-time option is not set, then the default maximum is 1073741824. +** +** [[SQLITE_CONFIG_ROWID_IN_VIEW]] +**
    SQLITE_CONFIG_ROWID_IN_VIEW +**
    The SQLITE_CONFIG_ROWID_IN_VIEW option enables or disables the ability +** for VIEWs to have a ROWID. The capability can only be enabled if SQLite is +** compiled with -DSQLITE_ALLOW_ROWID_IN_VIEW, in which case the capability +** defaults to on. This configuration option queries the current setting or +** changes the setting to off or on. The argument is a pointer to an integer. +** If that integer initially holds a value of 1, then the ability for VIEWs to +** have ROWIDs is activated. If the integer initially holds zero, then the +** ability is deactivated. Any other initial value for the integer leaves the +** setting unchanged. After changes, if any, the integer is written with +** a 1 or 0, if the ability for VIEWs to have ROWIDs is on or off. If SQLite +** is compiled without -DSQLITE_ALLOW_ROWID_IN_VIEW (which is the usual and +** recommended case) then the integer is always filled with zero, regardless +** if its initial value. ** */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ @@ -2172,6 +2190,7 @@ struct sqlite3_mem_methods { #define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */ #define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */ #define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */ +#define SQLITE_CONFIG_ROWID_IN_VIEW 30 /* int* */ /* ** CAPI3REF: Database Connection Configuration Options From d385a2350b8ae2a7e62dc53237d4dd6eb0ab9831 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 16 Apr 2024 13:26:33 +0300 Subject: [PATCH 153/202] QmlDesigner: Reset camera speeds when project changes Fixes: QDS-12297 Change-Id: Ia86f2290cdc94e637b7c12f48b379c429338ff0e Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- src/plugins/qmldesigner/components/edit3d/edit3dview.cpp | 8 ++++++++ src/plugins/qmldesigner/components/edit3d/edit3dview.h | 1 + 2 files changed, 9 insertions(+) diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index e560ed16ac5..a20904b2e5d 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -279,6 +279,14 @@ void Edit3DView::modelAttached(Model *model) { AbstractView::modelAttached(model); + QString currProjectPath = QmlDesigner::DocumentManager::currentProjectDirPath().toString(); + if (m_currProjectPath != currProjectPath) { + // Opening a new project -> reset camera speeds + m_currProjectPath = currProjectPath; + m_previousCameraSpeed = -1.; + m_previousCameraMultiplier = -1.; + } + syncSnapAuxPropsToSettings(); rootModelNode().setAuxiliaryData(edit3dGridColorProperty, diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h index 781b26d8d8d..fad87aae1f1 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h @@ -192,6 +192,7 @@ private: double m_previousCameraSpeed = -1.; double m_previousCameraMultiplier = -1.; + QString m_currProjectPath; friend class Edit3DAction; }; From 3f597a5935b49115a6969d65b340359691e611a4 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Tue, 16 Apr 2024 14:02:25 +0300 Subject: [PATCH 154/202] QmlDesigner: Modify the model editor files path Task-number: QDS-12505 Change-Id: Ib35ba7ab8ce3c6a980770bd73385d1afab630a2d Reviewed-by: Miikka Heikkinen --- .../collectioneditor/collectioneditorutils.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp index f1e4e2c207d..29b833cc2ce 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp @@ -96,13 +96,17 @@ Utils::FilePath dataStoreDir() if (!currentProject) return {}; - return currentProject->projectDirectory().pathAppended("/imports/" - + currentProject->displayName()); + FilePath oldImportDirectory = currentProject->projectDirectory().pathAppended( + "imports/" + currentProject->displayName()); + if (oldImportDirectory.exists()) + return oldImportDirectory; + + return currentProject->projectDirectory().pathAppended(currentProject->displayName()); } inline Utils::FilePath collectionPath(const QString &filePath) { - return dataStoreDir().pathAppended("/" + filePath); + return dataStoreDir().pathAppended(filePath); } inline Utils::FilePath qmlDirFilePath() From b5aac705ea7aa68fce74dd452c2b7c1b5eb0b5f0 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 11 Apr 2024 16:00:51 +0200 Subject: [PATCH 155/202] QmlDesigner: Indicate if a file is modified MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QDS-12361 Change-Id: I7a6c5f112b0f193f5bb975a331e2f0d44d19ddc2 Reviewed-by: Henning GrĂĽndl --- share/qtcreator/qmldesigner/toolbar/Main.qml | 20 ++++++++++++++++ .../components/toolbar/toolbarbackend.cpp | 23 +++++++++++++++++++ .../components/toolbar/toolbarbackend.h | 4 ++++ 3 files changed, 47 insertions(+) diff --git a/share/qtcreator/qmldesigner/toolbar/Main.qml b/share/qtcreator/qmldesigner/toolbar/Main.qml index 7235028df68..88a9bd744a2 100644 --- a/share/qtcreator/qmldesigner/toolbar/Main.qml +++ b/share/qtcreator/qmldesigner/toolbar/Main.qml @@ -190,6 +190,26 @@ Rectangle { onActivated: backend.openFileByIndex(index) } + Text { + parent:currentFile.contentItem + visible: backend.isDocumentDirty + + anchors.right: parent.right + anchors.rightMargin: parent.width - metric.textWidth - 18 + color: StudioTheme.Values.themeTextColor + text: StudioTheme.Constants.wildcard + font.family: StudioTheme.Constants.iconFont.family + font.pixelSize: StudioTheme.Values.smallIconFont + anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenterOffset: -4 + + FontMetrics { + id: metric + font: currentFile.font + property int textWidth: metric.boundingRect(currentFile.currentText).width + } + } + ToolbarButton { id: backButton anchors.verticalCenter: parent.verticalCenter diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp index e9df928c96c..cb84183f927 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp +++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp @@ -21,6 +21,9 @@ #include #include #include + +#include + #include #include #include @@ -302,6 +305,20 @@ ToolBarBackend::ToolBarBackend(QObject *parent) this, &ToolBarBackend::documentIndexChanged); + connect(Core::EditorManager::instance(), &Core::EditorManager::currentEditorChanged, this, [this]() { + static QMetaObject::Connection *lastConnection = nullptr; + delete lastConnection; + + if (auto textDocument = qobject_cast( + Core::EditorManager::currentDocument())) { + connect(textDocument->document(), + &QTextDocument::modificationChanged, + this, + &ToolBarBackend::isDocumentDirtyChanged); + emit isDocumentDirtyChanged(); + } + }); + connect(Core::EditorManager::instance(), &Core::EditorManager::currentEditorChanged, this, @@ -740,6 +757,12 @@ bool ToolBarBackend::isSharingEnabled() return QmlDesigner::checkEnterpriseLicense(); } +bool ToolBarBackend::isDocumentDirty() const +{ + return Core::EditorManager::currentDocument() + && Core::EditorManager::currentDocument()->isModified(); +} + void ToolBarBackend::launchGlobalAnnotations() { QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_TOOLBAR_EDIT_GLOBAL_ANNOTATION); diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h index 5d0b0e712a4..02bdae17170 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h +++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h @@ -97,6 +97,7 @@ class ToolBarBackend : public QObject Q_PROPERTY(bool isMCUs READ isMCUs NOTIFY isMCUsChanged) Q_PROPERTY(bool projectOpened READ projectOpened NOTIFY projectOpenedChanged) Q_PROPERTY(bool isSharingEnabled READ isSharingEnabled NOTIFY isSharingEnabledChanged) + Q_PROPERTY(bool isDocumentDirty READ isDocumentDirty NOTIFY isDocumentDirtyChanged) public: ToolBarBackend(QObject *parent = nullptr); @@ -147,6 +148,8 @@ public: bool isSharingEnabled(); + bool isDocumentDirty() const; + static void launchGlobalAnnotations(); signals: @@ -167,6 +170,7 @@ signals: void isMCUsChanged(); void projectOpenedChanged(); void isSharingEnabledChanged(); + void isDocumentDirtyChanged(); private: void setupWorkspaces(); From e55b5d0a0b388e5069f3ba573d81846c37ca5ac7 Mon Sep 17 00:00:00 2001 From: Knud Dollereder Date: Mon, 15 Apr 2024 16:39:10 +0200 Subject: [PATCH 156/202] QmlProjectManager: Write cmake-generator issues into the issues panel write qtquickcontrols file if it does not exist and minor cleanup of the cmake generator. Change-Id: I9b7523f32e5a9b41904c02a398a6f924623f949a Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- .../cmakegen/boilerplate.qrc | 1 + .../cmakegen/cmakegenerator.cpp | 24 +++++++++---------- .../cmakegen/cmakegenerator.h | 3 ++- .../cmakegen/cmakewriter.cpp | 4 ++-- .../cmakegen/cmakewriterv0.cpp | 15 +++++++----- .../templates/qtquickcontrols2_conf.tpl | 7 ++++++ 6 files changed, 33 insertions(+), 21 deletions(-) create mode 100644 src/plugins/qmlprojectmanager/cmakegen/templates/qtquickcontrols2_conf.tpl diff --git a/src/plugins/qmlprojectmanager/cmakegen/boilerplate.qrc b/src/plugins/qmlprojectmanager/cmakegen/boilerplate.qrc index b997114b2f0..a48666d883e 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/boilerplate.qrc +++ b/src/plugins/qmlprojectmanager/cmakegen/boilerplate.qrc @@ -9,6 +9,7 @@ templates/qmlcomponents.tpl templates/environment_h.tpl templates/import_qml_components_h.tpl + templates/qtquickcontrols2_conf.tpl diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp index abd812832c7..6a3f4afda03 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp +++ b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp @@ -9,6 +9,7 @@ #include "projectexplorer/projectmanager.h" #include "projectexplorer/projectnodes.h" +#include "projectexplorer/taskhub.h" #include "utils/filenamevalidatinglineedit.h" @@ -60,10 +61,11 @@ void CMakeGenerator::createMenuAction(QObject *parent) }); } -void CMakeGenerator::logIssue(const QString &text) +void CMakeGenerator::logIssue(ProjectExplorer::Task::TaskType type, const QString &text, const Utils::FilePath &file) { - // TODO: Use Issues panel as soon as it is usable in DS. - qDebug() << text; + ProjectExplorer::BuildSystemTask task(type, text, file); + ProjectExplorer::TaskHub::addTask(task); + ProjectExplorer::TaskHub::requestPopup(); } void CMakeGenerator::updateMenuAction() @@ -173,8 +175,8 @@ void CMakeGenerator::update(const QSet &added, const QSet &rem if (auto module = findModuleFor(node)) dirtyModules.insert(module); } else { - QString text("Failed to find Folder for file: %1"); - logIssue(text.arg(add)); + QString text("Failed to find Folder for file"); + logIssue(ProjectExplorer::Task::Error, text, path); } } @@ -338,10 +340,8 @@ bool CMakeGenerator::findFile(const NodePtr &node, const Utils::FilePath &file) void CMakeGenerator::insertFile(NodePtr &node, const Utils::FilePath &path) const { QString error; - if (!Utils::FileNameValidatingLineEdit::validateFileName(path.fileName(), false, &error)) { - QString text(path.path() + error); - logIssue(error); - } + if (!Utils::FileNameValidatingLineEdit::validateFileName(path.fileName(), false, &error)) + logIssue(ProjectExplorer::Task::Error, error, path); if (path.fileName() == "qmldir") { readQmlDir(path, node); @@ -485,9 +485,9 @@ void CMakeGenerator::compareWithFileSystem(const NodePtr &node) const files.push_back(next); } - const QString text("File %1 is not part of the project"); - for (const auto& file : files) - logIssue(text.arg(file.path())); + const QString text("File is not part of the project"); + for (const auto &file : files) + logIssue(ProjectExplorer::Task::Warning, text, file); } } // namespace GenerateCmake diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h index e9e87da1412..fd7b7dfd5be 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h +++ b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h @@ -5,6 +5,7 @@ #include "cmakewriter.h" #include "utils/filepath.h" +#include "projectexplorer/task.h" #include @@ -25,7 +26,7 @@ class CMakeGenerator : public QObject public: static void createMenuAction(QObject *parent); - static void logIssue(const QString &text); + static void logIssue(ProjectExplorer::Task::TaskType type, const QString &text, const Utils::FilePath &file); CMakeGenerator(QmlBuildSystem *bs, QObject *parent = nullptr); diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakewriter.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakewriter.cpp index 65d4a8c6924..80ea56dfb9d 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/cmakewriter.cpp +++ b/src/plugins/qmlprojectmanager/cmakegen/cmakewriter.cpp @@ -240,8 +240,8 @@ void CMakeWriter::writeFile(const Utils::FilePath &path, const QString &content) QTextStream stream(&fileHandle); stream << content; } else { - QString text("Failed to write file: %1"); - CMakeGenerator::logIssue(text.arg(path.path())); + QString text("Failed to write"); + CMakeGenerator::logIssue(ProjectExplorer::Task::Error, text, path); } fileHandle.close(); } diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv0.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv0.cpp index 71b6dbc4b0c..feac91f5873 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv0.cpp +++ b/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv0.cpp @@ -21,13 +21,10 @@ CMakeWriterV0::CMakeWriterV0(CMakeGenerator *parent) bool CMakeWriterV0::isPlugin(const NodePtr &node) const { - if (CMakeWriter::isPlugin(node)) - return true; - if (node->type == Node::Type::App) return !node->files.empty() || !node->singletons.empty() || !node->resources.empty(); - return false; + return CMakeWriter::isPlugin(node); } void CMakeWriterV0::transformNode(NodePtr &node) const @@ -41,8 +38,8 @@ void CMakeWriterV0::transformNode(NodePtr &node) const } else if (node->type == Node::Type::App) { Utils::FilePath path = node->dir.pathAppended("main.qml"); if (!path.exists()) { - QString text("Expected File %1 not found."); - CMakeGenerator::logIssue(text.arg(path.path())); + QString text("Expected File not found."); + CMakeGenerator::logIssue(ProjectExplorer::Task::Error, text, path); return; } if (!parent()->findFile(path)) @@ -54,6 +51,12 @@ void CMakeWriterV0::writeRootCMakeFile(const NodePtr &node) const { QTC_ASSERT(parent(), return); + const Utils::FilePath quickControlsPath = node->dir.pathAppended("qtquickcontrols2.conf"); + if (!quickControlsPath.exists()) { + const QString quickControlsTemplate = readTemplate(":/templates/qtquickcontrols_conf"); + writeFile(quickControlsPath, quickControlsTemplate); + } + const Utils::FilePath insightPath = node->dir.pathAppended("insight"); if (!insightPath.exists()) { const QString insightTemplate = readTemplate(":/templates/insight"); diff --git a/src/plugins/qmlprojectmanager/cmakegen/templates/qtquickcontrols2_conf.tpl b/src/plugins/qmlprojectmanager/cmakegen/templates/qtquickcontrols2_conf.tpl new file mode 100644 index 00000000000..eb1f825a388 --- /dev/null +++ b/src/plugins/qmlprojectmanager/cmakegen/templates/qtquickcontrols2_conf.tpl @@ -0,0 +1,7 @@ +; This file can be edited to change the style of the application +; Read "Qt Quick Controls 2 Configuration File" for details: +; http://doc.qt.io/qt-5/qtquickcontrols2-configuration.html + +[Controls] +Style=Basic + From 8154e4c3d067ac72fc256da8429d9e5d09c3e006 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 16 Apr 2024 15:47:54 +0300 Subject: [PATCH 157/202] QmlDesigner: Fix override cursor on config dialog close in 3D view Only restore override cursor on dialog destruction if it was actually hidden by current dialog. Fixes: QDS-12300 Change-Id: I8789e060244089c7d83e43926f841012602b27a5 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../components/edit3d/cameraspeedconfiguration.cpp | 10 +++++++--- .../components/edit3d/cameraspeedconfiguration.h | 1 + .../components/edit3d/snapconfiguration.cpp | 10 +++++++--- .../qmldesigner/components/edit3d/snapconfiguration.h | 1 + 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.cpp b/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.cpp index 76560ac1926..f5a74ee8645 100644 --- a/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.cpp +++ b/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.cpp @@ -66,9 +66,11 @@ void CameraSpeedConfiguration::resetDefaults() void CameraSpeedConfiguration::hideCursor() { - if (QGuiApplication::overrideCursor()) + if (m_cursorHidden) return; + m_cursorHidden = true; + QGuiApplication::setOverrideCursor(QCursor(Qt::BlankCursor)); if (QWindow *w = QGuiApplication::focusWindow()) @@ -77,9 +79,11 @@ void CameraSpeedConfiguration::hideCursor() void CameraSpeedConfiguration::restoreCursor() { - if (!QGuiApplication::overrideCursor()) + if (!m_cursorHidden) return; + m_cursorHidden = false; + QGuiApplication::restoreOverrideCursor(); if (QWindow *w = QGuiApplication::focusWindow()) @@ -88,7 +92,7 @@ void CameraSpeedConfiguration::restoreCursor() void CameraSpeedConfiguration::holdCursorInPlace() { - if (!QGuiApplication::overrideCursor()) + if (!m_cursorHidden) return; if (QWindow *w = QGuiApplication::focusWindow()) diff --git a/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.h b/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.h index 55256890cbb..fef06efc499 100644 --- a/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.h +++ b/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.h @@ -71,6 +71,7 @@ private: double m_multiplier = 0.; bool m_changes = false; QPoint m_lastPos; + bool m_cursorHidden = false; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp index 26c5fe0eb28..8890ea89641 100644 --- a/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp +++ b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp @@ -87,9 +87,11 @@ void SnapConfiguration::resetDefaults() void SnapConfiguration::hideCursor() { - if (QGuiApplication::overrideCursor()) + if (m_cursorHidden) return; + m_cursorHidden = true; + QGuiApplication::setOverrideCursor(QCursor(Qt::BlankCursor)); if (QWindow *w = QGuiApplication::focusWindow()) @@ -98,9 +100,11 @@ void SnapConfiguration::hideCursor() void SnapConfiguration::restoreCursor() { - if (!QGuiApplication::overrideCursor()) + if (!m_cursorHidden) return; + m_cursorHidden = false; + QGuiApplication::restoreOverrideCursor(); if (QWindow *w = QGuiApplication::focusWindow()) @@ -109,7 +113,7 @@ void SnapConfiguration::restoreCursor() void SnapConfiguration::holdCursorInPlace() { - if (!QGuiApplication::overrideCursor()) + if (!m_cursorHidden) return; if (QWindow *w = QGuiApplication::focusWindow()) diff --git a/src/plugins/qmldesigner/components/edit3d/snapconfiguration.h b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.h index 729e6ce7d10..ae401f60f9a 100644 --- a/src/plugins/qmldesigner/components/edit3d/snapconfiguration.h +++ b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.h @@ -103,6 +103,7 @@ private: double m_scaleInterval = 0.; bool m_changes = false; QPoint m_lastPos; + bool m_cursorHidden = false; }; } // namespace QmlDesigner From 1f4d1a5820d3d5aba2f32394fb3d9aac55aada5b Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 4 Apr 2024 10:36:13 +0200 Subject: [PATCH 158/202] QmlDesigner: Use different changelog path for QDS * Do not deploy Qt Creator changelog for Qt Design Studio * Set minimum size on combo box Change-Id: Ic069c76b5a02963972ec199553ce9e27c820f75a Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- CMakeLists.txt | 4 +++- src/plugins/coreplugin/icore.cpp | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e56479a98b9..d88f2491144 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -170,7 +170,9 @@ endif() add_subdirectory(src) add_subdirectory(share) -add_subdirectory(dist) +if (NOT BUILD_DESIGNSTUDIO) + add_subdirectory(dist) +endif() if (WITH_TESTS) add_subdirectory(tests) diff --git a/src/plugins/coreplugin/icore.cpp b/src/plugins/coreplugin/icore.cpp index 8a727494c2d..84548d91cf4 100644 --- a/src/plugins/coreplugin/icore.cpp +++ b/src/plugins/coreplugin/icore.cpp @@ -2578,6 +2578,7 @@ void ICorePrivate::changeLog() }); auto versionCombo = new QComboBox; + versionCombo->setMinimumWidth(80); for (const VersionFilePair &f : versionedFiles) versionCombo->addItem(f.first.toString()); dialog = new LogDialog(ICore::dialogParent()); From ece3a60cfad87928342902021f94d0b1fd869801 Mon Sep 17 00:00:00 2001 From: Shrief Gabr Date: Tue, 16 Apr 2024 15:59:11 +0300 Subject: [PATCH 159/202] QmlDesigner: Prevent QDS crash during asset import in debug mode Task-number: QDS-12436 Change-Id: I5ecac614faff58a314ec2886730e4d69f717885b Reviewed-by: Miikka Heikkinen --- .../components/itemlibrary/itemlibraryassetimportdialog.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp index b8a75851f62..2c690726029 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp @@ -702,8 +702,10 @@ QGridLayout *ItemLibraryAssetImportDialog::createOptionsGrid( // and move the remaining member to ungrouped options // Note: <= 2 instead of < 2 because each group has group label member if (i != 0 && groupWidgets.size() <= 2) { - widgets[0].prepend(groupWidgets[1]); - groupWidgets[0].first->hide(); // hide group label + if (groupWidgets.size() == 2) + widgets[0].prepend(groupWidgets[1]); + if (groupWidgets.size() >= 1) + groupWidgets[0].first->hide(); // hide group label groupWidgets.clear(); } } From 4591293fd9103a57d611b64b9e2daa976cad594e Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Tue, 9 Apr 2024 17:50:11 +0300 Subject: [PATCH 160/202] QmlDesigner: Add user texture bundle Fixes: QDS-12390 Change-Id: I512a8748bbb6a282589f05293507c110162e7f1d Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Miikka Heikkinen --- .../ContentLibraryUserView.qml | 33 ++++++++++++++----- .../contentlibrary/contentlibrarymaterial.h | 2 ++ .../contentlibrary/contentlibrarytexture.cpp | 12 +++---- .../contentlibrary/contentlibrarytexture.h | 6 ++-- .../contentlibrarytexturescategory.cpp | 4 +-- .../contentlibrarytexturesmodel.h | 2 +- .../contentlibraryusermodel.cpp | 32 +++++++++++++++++- .../contentlibrary/contentlibraryusermodel.h | 4 ++- .../materialbrowsertexturesmodel.cpp | 2 +- .../instances/nodeinstanceview.cpp | 2 +- src/plugins/qmldesigner/utils/imageutils.cpp | 12 +++++-- src/plugins/qmldesigner/utils/imageutils.h | 5 +-- 12 files changed, 88 insertions(+), 28 deletions(-) diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml index d3d1dbad92d..47d3c6ce773 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 import QtQuick +import Qt.labs.qmlmodels import HelperWidgets as HelperWidgets import StudioControls as StudioControls import StudioTheme as StudioTheme @@ -99,17 +100,33 @@ HelperWidgets.ScrollView { id: repeater model: categoryItems - delegate: ContentLibraryMaterial { - width: root.cellWidth - height: root.cellHeight + delegate: DelegateChooser { + role: "itemType" - importerRunning: ContentLibraryBackend.userModel.importerRunning + DelegateChoice { + roleValue: "material" + ContentLibraryMaterial { + width: root.cellWidth + height: root.cellHeight - onShowContextMenu: ctxMenu.popupMenu(modelData) - onAddToProject: ContentLibraryBackend.userModel.addToProject(modelData) + importerRunning: ContentLibraryBackend.userModel.importerRunning - onVisibleChanged: { - section.numVisibleItem += visible ? 1 : -1 + onShowContextMenu: ctxMenu.popupMenu(modelData) + onAddToProject: ContentLibraryBackend.userModel.addToProject(modelData) + + onVisibleChanged: { + section.numVisibleItem += visible ? 1 : -1 + } + } + } + DelegateChoice { + roleValue: "texture" + delegate: ContentLibraryTexture { + width: root.cellWidth + height: root.cellHeight + + // onShowContextMenu: ctxMenu.popupMenu(modelData) // TODO + } } } diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h index 26b5adc468b..55af2accbd3 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h @@ -21,6 +21,7 @@ class ContentLibraryMaterial : public QObject Q_PROPERTY(QString bundleMaterialBaseWebUrl MEMBER m_baseWebUrl CONSTANT) Q_PROPERTY(QString bundleMaterialParentPath READ parentDirPath CONSTANT) Q_PROPERTY(QStringList bundleMaterialFiles READ allFiles CONSTANT) + Q_PROPERTY(QString itemType MEMBER m_itemType CONSTANT) public: ContentLibraryMaterial(QObject *parent, @@ -65,6 +66,7 @@ private: QString m_downloadPath; QString m_baseWebUrl; QStringList m_allFiles; + const QString m_itemType = "material"; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp index c2fec6e0f73..80dd7e816f8 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp @@ -12,10 +12,10 @@ namespace QmlDesigner { ContentLibraryTexture::ContentLibraryTexture(QObject *parent, const QFileInfo &iconFileInfo, - const QString &dirPath, const QString &key, - const QString &textureUrl, const QString &iconUrl, - const QString &suffix, const QSize &dimensions, - const qint64 sizeInBytes, bool hasUpdate, bool isNew) + const QString &dirPath, const QString &suffix, + const QSize &dimensions, const qint64 sizeInBytes, + const QString &key, const QString &textureUrl, + const QString &iconUrl, bool hasUpdate, bool isNew) : QObject(parent) , m_iconPath(iconFileInfo.filePath()) , m_dirPath(dirPath) @@ -82,10 +82,10 @@ QString ContentLibraryTexture::resolveToolTipText() QString imageInfo; if (!m_isDownloaded && m_sizeInBytes > 0 && !m_dimensions.isNull()) { - imageInfo = ImageUtils::imageInfo(m_dimensions, m_sizeInBytes); + imageInfo = ImageUtils::imageInfoString(m_dimensions, m_sizeInBytes); } else { QString fullDownloadPath = m_dirPath + '/' + fileName; - imageInfo = ImageUtils::imageInfo(fullDownloadPath); + imageInfo = ImageUtils::imageInfoString(fullDownloadPath); } return QString("%1\n%2").arg(fileName, imageInfo); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h index 48f2314e9c4..8f7197bc72e 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h @@ -24,12 +24,13 @@ class ContentLibraryTexture : public QObject Q_PROPERTY(bool textureHasUpdate WRITE setHasUpdate READ hasUpdate NOTIFY hasUpdateChanged) Q_PROPERTY(bool textureIsNew MEMBER m_isNew CONSTANT) Q_PROPERTY(QString textureKey MEMBER m_textureKey CONSTANT) + Q_PROPERTY(QString itemType MEMBER m_itemType CONSTANT) public: ContentLibraryTexture(QObject *parent, const QFileInfo &iconFileInfo, const QString &dirPath, - const QString &key, const QString &textureUrl, const QString &iconUrl, const QString &suffix, const QSize &dimensions, const qint64 sizeInBytes, - bool hasUpdate = false, bool isNew = false); + const QString &key = {}, const QString &textureUrl = {}, + const QString &iconUrl = {}, bool hasUpdate = false, bool isNew = false); Q_INVOKABLE bool isDownloaded() const; Q_INVOKABLE void setDownloaded(); @@ -71,6 +72,7 @@ private: bool m_visible = true; bool m_hasUpdate = false; bool m_isNew = false; + const QString m_itemType = "texture"; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.cpp index 7fb92c66ac5..0cafe8d138c 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.cpp @@ -21,8 +21,8 @@ void ContentLibraryTexturesCategory::addTexture(const QFileInfo &texIcon, const bool hasUpdate, bool isNew) { m_categoryTextures.append(new ContentLibraryTexture( - this, texIcon, downloadPath, key, webTextureUrl, iconUrl, - suffix, dimensions, sizeInBytes, hasUpdate, isNew)); + this, texIcon, downloadPath, suffix, dimensions, sizeInBytes, + key, webTextureUrl, iconUrl, hasUpdate, isNew)); } bool ContentLibraryTexturesCategory::filter(const QString &searchText) diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.h index b5237b2868b..94e223a2519 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.h @@ -37,7 +37,7 @@ public: void setHasSceneEnv(bool b); void resetModel(); - void loadTextureBundle(const QString &m_textureBundleUrl, const QString &bundlePath, + void loadTextureBundle(const QString &textureBundleUrl, const QString &bundlePath, const QVariantMap &metaData); signals: diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp index 98659221e41..f62ef95fccc 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp @@ -6,9 +6,11 @@ #include "contentlibrarybundleimporter.h" #include "contentlibrarymaterial.h" #include "contentlibrarymaterialscategory.h" +#include "contentlibrarytexture.h" #include "contentlibrarywidget.h" #include +#include #include #include @@ -16,6 +18,7 @@ #include #include +#include #include #include #include @@ -28,9 +31,10 @@ ContentLibraryUserModel::ContentLibraryUserModel(ContentLibraryWidget *parent) : QAbstractListModel(parent) , m_widget(parent) { - m_userCategories = {tr("Materials")/*, tr("Textures"), tr("3D"), tr("Effects"), tr("2D components")*/}; // TODO + m_userCategories = {tr("Materials"), tr("Textures")/*, tr("3D"), tr("Effects"), tr("2D components")*/}; // TODO loadMaterialBundle(); + loadTextureBundle(); } int ContentLibraryUserModel::rowCount(const QModelIndex &) const @@ -262,6 +266,32 @@ void ContentLibraryUserModel::loadMaterialBundle() emit matBundleExistsChanged(); } +void ContentLibraryUserModel::loadTextureBundle() +{ + if (!m_userTextures.isEmpty()) + return; + + QDir bundleDir{Paths::bundlesPathSetting() + "/User/textures"}; + bundleDir.mkpath("."); + bundleDir.mkdir("icons"); + + const QFileInfoList fileInfos = bundleDir.entryInfoList(QDir::Files); + for (const QFileInfo &fileInfo : fileInfos) { + auto iconFileInfo = QFileInfo(fileInfo.path().append("/icons/").append(fileInfo.fileName())); + QPair info = ImageUtils::imageInfo(fileInfo.path()); + QString dirPath = fileInfo.path(); + QString suffix = '.' + fileInfo.suffix(); + QSize imgDims = info.first; + qint64 imgFileSize = info.second; + + auto tex = new ContentLibraryTexture(this, iconFileInfo, dirPath, suffix, imgDims, imgFileSize); + m_userTextures.append(tex); + } + + int texSectionIdx = 1; + emit dataChanged(index(texSectionIdx, 0), index(texSectionIdx, 0)); +} + bool ContentLibraryUserModel::hasRequiredQuick3DImport() const { return m_widget->hasQuick3DImport() && m_quick3dMajorVersion == 6 && m_quick3dMinorVersion >= 3; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h index e703ffea7d6..2dde73e5de4 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h @@ -3,7 +3,7 @@ #pragma once -#include "nodemetainfo.h" +#include "modelfwd.h" #include #include @@ -16,6 +16,7 @@ class ContentLibraryEffect; class ContentLibraryMaterial; class ContentLibraryTexture; class ContentLibraryWidget; +class NodeMetaInfo; namespace Internal { class ContentLibraryBundleImporter; @@ -94,6 +95,7 @@ signals: private: void loadMaterialBundle(); + void loadTextureBundle(); bool isValidIndex(int idx) const; void createImporter(const QString &bundlePath, const QString &bundleId, const QStringList &sharedFiles); diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp index ec95f3e5d3d..918956a04c0 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp @@ -63,7 +63,7 @@ QVariant MaterialBrowserTexturesModel::data(const QModelIndex &index, int role) return tr("Texture has no source image."); ModelNode texNode = m_textureList.at(index.row()); - QString info = ImageUtils::imageInfo(source); + QString info = ImageUtils::imageInfoString(source); if (info.isEmpty()) return tr("Texture has no data."); diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index 26f2687c233..1b965db66a7 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -1946,7 +1946,7 @@ QVariant NodeInstanceView::previewImageDataForImageNode(const ModelNode &modelNo imageData.pixmap = originalPixmap.scaled(dim, dim, Qt::KeepAspectRatio); imageData.pixmap.setDevicePixelRatio(ratio); imageData.time = modified; - imageData.info = ImageUtils::imageInfo(imageSource); + imageData.info = ImageUtils::imageInfoString(imageSource); m_imageDataMap.insert(imageData.id, imageData); } } diff --git a/src/plugins/qmldesigner/utils/imageutils.cpp b/src/plugins/qmldesigner/utils/imageutils.cpp index 8fa3131cd32..42df6184b94 100644 --- a/src/plugins/qmldesigner/utils/imageutils.cpp +++ b/src/plugins/qmldesigner/utils/imageutils.cpp @@ -11,7 +11,7 @@ namespace QmlDesigner { -QString ImageUtils::imageInfo(const QSize &dimensions, qint64 sizeInBytes) +QString ImageUtils::imageInfoString(const QSize &dimensions, qint64 sizeInBytes) { return QLatin1String("%1 x %2\n%3") .arg(QString::number(dimensions.width()), @@ -20,7 +20,7 @@ QString ImageUtils::imageInfo(const QSize &dimensions, qint64 sizeInBytes) sizeInBytes, 2, QLocale::DataSizeTraditionalFormat)); } -QString QmlDesigner::ImageUtils::imageInfo(const QString &path) +QPair QmlDesigner::ImageUtils::imageInfo(const QString &path) { QFileInfo info(path); if (!info.exists()) @@ -52,7 +52,13 @@ QString QmlDesigner::ImageUtils::imageInfo(const QString &path) if (width <= 0 || height <= 0) return {}; - return imageInfo(QSize(width, height), info.size()); + return {QSize(width, height), info.size()}; +} + +QString ImageUtils::imageInfoString(const QString &path) +{ + QPair info = imageInfo(path); + return imageInfoString(info.first, info.second); } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/utils/imageutils.h b/src/plugins/qmldesigner/utils/imageutils.h index a4036614a3c..dae64423bbb 100644 --- a/src/plugins/qmldesigner/utils/imageutils.h +++ b/src/plugins/qmldesigner/utils/imageutils.h @@ -12,8 +12,9 @@ class ImageUtils public: ImageUtils(); - static QString imageInfo(const QSize &dimensions, qint64 sizeInBytes); - static QString imageInfo(const QString &path); + static QPair imageInfo(const QString &path); + static QString imageInfoString(const QString &path); + static QString imageInfoString(const QSize &dimensions, qint64 sizeInBytes); }; } // namespace QmlDesigner From 3e42b144badeb6cb1aedf9077489745c9363b20b Mon Sep 17 00:00:00 2001 From: Knud Dollereder Date: Tue, 16 Apr 2024 15:08:02 +0200 Subject: [PATCH 161/202] QmlDesigner: Fix curveditor playhead update issues Change-Id: I04b3502395f0b0b25bca785acf220f69ba3428bf Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp | 2 +- .../qmldesigner/components/curveeditor/curveeditortoolbar.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp b/src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp index e84623e26cd..36ce3373e59 100644 --- a/src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp +++ b/src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp @@ -77,8 +77,8 @@ CurveEditor::CurveEditor(CurveEditorModel *model, QWidget *parent) connect(m_toolbar, &CurveEditorToolBar::currentFrameChanged, [this, model](int frame) { model->setCurrentFrame(frame); + m_view->setCurrentFrame(frame, false); updateStatusLine(); - m_view->viewport()->update(); }); connect(m_toolbar, &CurveEditorToolBar::zoomChanged, [this](double zoom) { diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.cpp b/src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.cpp index 72410ffb078..8da87ce1194 100644 --- a/src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.cpp +++ b/src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.cpp @@ -129,6 +129,8 @@ CurveEditorToolBar::CurveEditorToolBar(CurveEditorModel *model, QWidget* parent) m_currentSpin->setFrame(false); connect(m_currentSpin, &QSpinBox::valueChanged, this, &CurveEditorToolBar::currentFrameChanged); + connect(model, &CurveEditorModel::commitCurrentFrame, + this, [this](int frame) { m_currentSpin->setValue(frame); }); addSpacer(); From b17cd52f84a7d734357f3e5df32400182ec3a328 Mon Sep 17 00:00:00 2001 From: Knud Dollereder Date: Tue, 16 Apr 2024 16:05:03 +0200 Subject: [PATCH 162/202] QmlProjectManager: Generate file to define the default build path when opening the root CMakeLists.txt with QtCreator Change-Id: Ia448dab1b3161471fe2bb7870ffb79e68dd579fc Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- .../qmlprojectmanager/cmakegen/boilerplate.qrc | 1 + .../qmlprojectmanager/cmakegen/cmakewriterv1.cpp | 4 ++++ .../cmakegen/templates/cmakelists_txt_shared.tpl | 16 ++++++++++++++++ 3 files changed, 21 insertions(+) create mode 100644 src/plugins/qmlprojectmanager/cmakegen/templates/cmakelists_txt_shared.tpl diff --git a/src/plugins/qmlprojectmanager/cmakegen/boilerplate.qrc b/src/plugins/qmlprojectmanager/cmakegen/boilerplate.qrc index a48666d883e..b4eb027e830 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/boilerplate.qrc +++ b/src/plugins/qmlprojectmanager/cmakegen/boilerplate.qrc @@ -10,6 +10,7 @@ templates/environment_h.tpl templates/import_qml_components_h.tpl templates/qtquickcontrols2_conf.tpl + templates/cmakelists_txt_shared.tpl diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv1.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv1.cpp index d78abd227d7..096ceb5d19e 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv1.cpp +++ b/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv1.cpp @@ -70,6 +70,10 @@ void CMakeWriterV1::writeRootCMakeFile(const NodePtr &node) const const QString fileContent = fileTemplate.arg(appName, fileSection); writeFile(file, fileContent); + const Utils::FilePath sharedFile = node->dir.pathAppended("CMakeLists.txt.shared"); + const QString sharedTemplate = readTemplate(":/templates/cmake_shared"); + writeFile(sharedFile, sharedTemplate); + const Utils::FilePath userFile = node->dir.pathAppended("qds.cmake"); QString userFileContent(DO_NOT_EDIT_FILE); userFileContent.append(makeSubdirectoriesBlock(node)); diff --git a/src/plugins/qmlprojectmanager/cmakegen/templates/cmakelists_txt_shared.tpl b/src/plugins/qmlprojectmanager/cmakegen/templates/cmakelists_txt_shared.tpl new file mode 100644 index 00000000000..3db7152bffa --- /dev/null +++ b/src/plugins/qmlprojectmanager/cmakegen/templates/cmakelists_txt_shared.tpl @@ -0,0 +1,16 @@ + + + + + ProjectExplorer.Project.PluginSettings + + + QTC_DEFAULT_BUILD_DIRECTORY_TEMPLATE=../%{Asciify:%{Project:Name}-%{BuildConfig:Name}} + + + + + Version + 22 + + From 7bef3c773a340b8c86a1ad36480cb0571c5aaf22 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Tue, 16 Apr 2024 12:38:38 +0200 Subject: [PATCH 163/202] QmlDesigner: fix build on macOS Change-Id: I7814429f1b1ce04d37c202e3f47f40ab39ad430a Reviewed-by: Marco Bubke Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- .../designercore/projectstorage/qmltypesparser.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp index 10363150dc1..104338e514b 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp @@ -9,6 +9,8 @@ #include +#include + #ifdef QDS_BUILD_QMLPARSER #include #include @@ -469,9 +471,9 @@ using namespace Qt::StringLiterals; constexpr auto skipLists = std::make_tuple( std::pair{"QtQuick.Templates-cppnative"sv, std::array{"QQuickItem"_L1}}); -std::span getSkipList(std::string_view moduleName) +Utils::span getSkipList(std::string_view moduleName) { - static constexpr std::span emptySkipList; + static constexpr Utils::span emptySkipList; auto currentSkipList = emptySkipList; std::apply( @@ -484,7 +486,7 @@ std::span getSkipList(std::string_view moduleName) return currentSkipList; } -bool skipType(const QQmlJSExportedScope &object, std::span skipList) +bool skipType(const QQmlJSExportedScope &object, Utils::span skipList) { return std::any_of(skipList.begin(), skipList.end(), [&](const QLatin1StringView skip) { return object.scope->internalName() == skip; From b33970167aa27bd540c02616d88da46124f6786b Mon Sep 17 00:00:00 2001 From: Teea Poldsam Date: Mon, 15 Apr 2024 12:41:53 +0300 Subject: [PATCH 164/202] Doc: Update Adding flow views Fixes: QDS-11404 Change-Id: Id0cfc1a956051e31393fe57f2b64cc6ceeebc3c1 Reviewed-by: Johanna Vanhatapio --- .../images/studio-flow-view-properties.png | Bin 9780 -> 0 bytes .../images/studio-flow-view-properties.webp | Bin 0 -> 8724 bytes .../src/qtdesignstudio-app-flows.qdoc | 40 ++++++++---------- 3 files changed, 17 insertions(+), 23 deletions(-) delete mode 100644 doc/qtdesignstudio/images/studio-flow-view-properties.png create mode 100644 doc/qtdesignstudio/images/studio-flow-view-properties.webp diff --git a/doc/qtdesignstudio/images/studio-flow-view-properties.png b/doc/qtdesignstudio/images/studio-flow-view-properties.png deleted file mode 100644 index 95fe403b76c4d65616607adc5c0648e57ea2d676..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9780 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%0D`+Y~o37=E@ChHxWmA9zV?R(_J zK0&m4!Iycxrh4dq)z9bc!pz)^?%lSWE7kE)Nqp10f-4lf5OK#puu;2Ljc=c=f zv|s(H>%6umuic_wo7Zu3n=2(kor+qD8s>!CQKZ7L~u-UjP5~`|iWO zk?p?sRb<`2xI_xCuytO_8dI-z{^={-;$E||ZwGvG1!}*B|2aH6 z{EnZKtoc}z=y&^)@zs-ae)&vQ-0}HM>i0~si5sjht-LVxbT@x6M|Sb{33;xnLPvY5 zcjZ2G>{`37WS;2d!_Rj}FWKzx)}1%+C6s^)hGjd;LEGTet20B7Zt+ zYToIiPF5bhrr754Ax#e}Y95XCgqFnUZecnBhPqH_2q~}kanKGR_xm$MT zK0cjk;Z?WS#ME^B%6fTXow(@!FP+Y@7CG~b`!g4Pk60Y~a$AhflID`#CRL%aNq(>Y zhHCnLd9>+nYpC++{^A`f&gpdwmnOz`ADeEq`OnR7-}+?K#q}mTJ?mt97Tt0!&GG8u zP2Tsml(PWIe0rEOYq+>~&gvbUWMRvz1xFU?(( z?Nc7+S-xtY<>k;~-WwJRR>TUOSIU_e)~kCbID6Mq;jV|jC-LZeKk7Sk$NZAv;#Jn` zWa59u4=2 ztUiO*ZJ?U*lA{%=F`lYkt1lxVHB?Ld0|B6?NkG5OVT=M?mi>>z4rphk*#r`~(`SK>E z9d5HKr!}>;rhk5%{OP^2Zs~GPmR-&lUN(78Ka!bfbJ_F<&zzm{OE&xMSe((ZE#~jl z6Td$_XqHjGWIe6#NsPs^H3D;X{@?O($IspM_uS@*dR6LGHg9(@Q^{>?B1=nb$KdF?t^ei)Z|EH7BPhI=;=g*|u$)``fN(erj{!aPj znn_mT(Jf-HFU8K4J^5Vd{>Bn^2ci9vV&T^jW%CZ0DzHRcUyV$&D*Ng{oMqhI> z3nOm^&whEhbC>P<905+H7V%xvmuc0!H2J>cPET;*#ikV|CyxYWuX)!oy>wS~<+7JC zIUzoAi$5)&mr@<|;QQvkGr!G}wzT{!9PU*6_^9rw#7w`ivb3h{&tJW`y4S@r?uAQ; z|Cv2=vNTWob=e0i|Nq(9;hmCmui=B{rdWSef&Oby%zm@S4ow5ZbhFM zC>*N3aOrt3`QCQC`qk2)%;P_<9G-ot>&Icai>|VpKfPY`=8cGGL-u9I#G{LapYGFc z=AGm{G3nhZ^Sh51DH?13_S+?qy{l7A>TbHL)!yd2;7-%mc%%Psl2ema4mT8AeSXY) zYTf0sER(;hL)EXx@R|odcGXKct9EqTSrcv(kqLLZL3P}xQ~yjCNmNhOKF(FTZk^#| zEAgo0EzyTNcJ96~v!q~iiJi#T7o{1OUxbERy=|HH;N~)qZ>!B5OmFzV`SJ1IulAY$ zZp~bM`uhHpZqF@~zHq%Ps{z%##m8Lv*Uf(Ky+THvAKYu#feLt+~ZjTY>gYy9KU&qH&zEltmgyZQ0!VekDq=e6D0mro15w3c7Pa*_I} zSq`^eHO)U&JePOEIY@tP`aQNawG|sTv!`V>zgQn*7`nv?y_wattBG!H+@`q$XVN+Z{lS0EdkOgc{hB1*~#q<3Oo1mMzq`; zo`ZAK^>_Pk?r4iyZd{~4bH>XRt1gs1U6!*;^_W*UN)?#%Bu-_zxco8!j@3%*ywj&& zvc6w($j3OhY*FSy^Q#xP7nS{(^DXG{tdggvrW*b%TT*;`5990e*Uf(ZYpS~Q_te(S zVbj;wPqtmsTw-1I#cis`(znisV@qFMe0}3cv-G7?E_Q|4FFE)mYd@b2)V^;!tL>V8 z-;>WBZ0G(kEJ@BS+z@;R-j|%beR|&sYionRm;1}6zEAjd_~kAEV_DV{>s%ju&yD<- zQ(f)eH<^37XR#>1?^V}LIysego#x9#Q|}$izA`aZuSd5m<(Jilh`5)zn~Odw8fl|- zKWj8x-tC;Y+`P5M-E8*DPiZf6%<>QX`pU8Cwd+y++D8H6e6ydQNuKp$?cEIDpuMwK zOyIlp_STEg(oV~@-@})hTvS&5kgwk#=A}#hIiv_d zl?yiUX}*=39l~R(%D&ue%beh|i)6&^?wqH6Ivf`h}>>ZVFRpPqJe*ThP7I*PiaZAaBJoP2kk=JLuTQlihNNw@5)z73h z6gmX%R+;B|!)C2i^p54%N=~bb9R98<88_{S?983{OOo9}%VR9}ef!IF)Oh_;Ltk5~ zJ0%;ck6qhx{fKOcz3u6au64_N!n+vjQ=1fIm)z!jc|q@;Hmv4_R?48R<^(=)O)jtm zR$rPkazOj0e;lAyDVYEEuQ}tbx{8M%4mn!2TsSVh#2IbKZsGN|{HGu8YR6f4d=K>4 zzG1_M_`PCZ4%L46Uh?}pxW2YvxW26tx%vmSArxfcRwGx{vQ=kiGpu{K-bdl?adp|5 zd8HE`XQX?ouRU*Qx^Pm7dfoX;b>E#K8uC_a+;i=p;`KD5wrA_Y?oA>lIySTAb&LYtMzX8rnAdGv6%R6TZzi^Lx!oX56=8u z-h2EkugPU_FE-FV5k8iuaChItzPUF)uQ<@^QE=krbn#VoOG71pPxZG8-!|>}^XYfw zqQk=2XTLi;^L=G}q-v?&HV)luOLtpYPdcyqF83v0+PCwb&AvC%Q#YG3nrsdKv*OMg z0kg}>@Aha+aa^#WFKx;-uk0@WDx;7#$(z5`)5Jrscx0{CQ`s@|rHy6r`X!Uz&3w4) zGb?N4!JO%!PHwyT+{-gRus&bsp>o~p=*fMnjJ>SXV?x5B)iuRii(g1DyrhzghQJvC2I6h6lF?`%50%D%R<$m#Pmxw*4Bka!Yc} ztB1ast~U1{KYHP^^ZlvnY0z%z{c2;)-Ab`NrgvS0d*5wVl1(-iU)Fei%H&o5C;d#9 z*Ope6++FUMUbn~bX6Y@z+;#O^vtF#c6=kXft|*PWa%X3XWvD3b7C-sK_1%-Hu{kb= z-?YN}*3NmdKE{p%T)|zw_xtRnNcFNm75RUeJ7-?mc|mINUGbh7ySe9jp1Ioo;(p5F z%@cQ5A2_g;|Jiz<68H7ub3)U7uP9FqKKwj3e7(J8iv7&}f`@KY8~c_y?%MzV`O|Lk z{cFEGivKSc>bYH1TrBaD=gqIH?(N*Y{=eJxL>K2ah6sVTh~nL`*Q6PwBlR6zsSXZifQAPt8c5c z&Ocpa=ki}|-8nZWE06Ra7f)LXi7l7h+t$9yZT+WLKQ<(!XG1H%{6Z!5{EUTWCu-iG zUGn}DxaYb2eCw}*bdGm6)i-Nrx!yVYy2S0Bs<5$asOYVAcc%DmKN9iM>$un3xqC$u z6J3^OFPhYQr~ApLM~6jp(_a1FySHT9$?xJNPQE`3}0aGllH7o|Cm4)SKED&03+oWIX9>#4QdYi8scx=m}Gq-1a*>-7StiD#=OX2oi1vhNc8RnVHL=TPgq*nL-72%{owM0}myZ|jdVoepoOVqv?R|Mp>!sgS^YmSl!uMx*uYEbq zU!(24x6vM*&g!yf)px_sta#O@ef;{q_yrZuwwf$k*{PSKM=fRzj!V z{hi7Q>J+~Hwy&wA`mZm;^$+#U(9Yr#aOHx+L8}#b>V*%O{+Y^&r1IPU*$lb%dkQKZ zD#$v$c+7iA5oNNM<>iI!!`q8~yc5-zS@K(B$?x~-^B<&J+yAKE_4l`0dr5^btY$mR zk7O#OHi6WKsI`)_{EQON0BmXW|6AJy?_QbVbcg$Q#qZlZFBR@uTUDJbS=}t-ZtZQA z`PgL1?b?@XglA1L_VS&)xIcfseVVhHz|Eb`o2S}M$=$Q*ooevo)<0`kC|B2*8!Cm} zciHslxBSEXkL&Ggz{bse=)V5%#x-vQuG(AVOV5}ldeHRJnazkAEpw%{ufhe{CO`3* zT`GRuk7uqsnEv3*U!}RwYUAUy}6-5&mY}&dFN<0`>?f4ZSJF^i;daKU92Fr zRLtKyd$gu}iZNT#Tq56B^5)DucUC=@CTG9>HBT89{0KRB;e{D2 zf2wXjK5gpNgUi~^-*LaRvNcmMYK?b!X_?A=?>nL|uWf676K7%+_saKTVDmY|@ae8+ zerh!bcWMir+!|E4Y5m(>C28~2xzDv(ud!u@^$j0Rui05!^(Jg##GLt$(p^#Nw|tha z$q#b;<|sJUeEmJkTiy2U&f`J)ag*!fC%rm+V~cUg_3X9V6OV-F8@I|&oh^DOt?I~J z@m3=R*(JtbUOZ)MIX?aCw|Hw&K{Xxe386<@Ws3{HcxkB z+SktKo0lFFGPL{pVc8a?e;eFO0*|lxvrEI; z+LiUet&)Ia6L3ds&OG|5^FE`ET#*tM?6_t=@jq zZSO=^y_B9f#kD!H`Z-m9Ij>m{9#RX)7e#0G8ctYeHVxsL>>6L{L`JqH<^vMI+n{FU62hJlmOlGa!aBQ>zoM!6 zG3)=e6~^s7dnY{Jr=?`VJKa8#hiRAlH~tc7hmFmCl#O4mEFr%-TE_BtZ0KRVrRDox zw#)K$j=#pvR zNtaE1CG6*I=ImKE^_*Je;*xFB*H`aPToazZ8GlDHWd40@9mUJb9ZF{-YS2%5OMY+E zeSBi#f&~kb1vSIx|FNn4+VOFf6y`AOtE#|{-t#T4v>twD`FX}NEtg|g{ARXavz@!{ z(%pTfhdS>3f%XV*Ut5hfHFH&g zy&)^*XCdG8loy~;!!#~<#V+1`2KsT^X?t%@ziS*K*~VFvG-UMc%UP-%&_5H5P$pk`z#s1ArqPcPV)Ig pEW%jW?_~97z6Qra{(b-Xx%d<|G)~!N%)r3F;OXk;vd$@?2>=TjhQ$iSe#xY3v)^x?*T^*fa_f}6s93Gi)vFv?vm80SLbXD|W#v|0(JEc&rFu2@Zjk3L)tC3ab8%gH zWw=?^c=EfwK~onjiEh(!wwZdi@EG^Y;(ckyO2QJkYG)|5Wjykn8)w{l@6eiqB4>qq zBySsTUi4CQ$78Q{vj-hB6+h)1@J_hl8vLw4$@#<#tM6LRb|@LAohdtVX2Q+x1nac2 zYSwdVuO2_VFvsvtv5Zm69_IOpXM6=q?F6K@A9xejcJthvN=6aORIhmB$8uInAHTh| z&HUuH-01BltGYTGVmBtZS9$i;$=Lt0IQMxrXX6DC0oS{4n_B1I3o_5YzHM#hw%psZ z9Ga>Nr@wO$)t_|vt@-QabEmLq1YDPW_2$AOsk^`UJ9|}JC%rD1X&Gu7&z7M4M0Ng; zN6}p>4c)Oi8Gp~dwH8=^=GHphFWV&F9jS@D6nf*Z)yG&VBNdCSe>h7^UtN&b!xAbdy8@PUzUUt6B^DEy}`F>nCm~_)ecovuaG3~1yQT3&nPfyvsd+RsN z>hAo@zfGPBulaV^N8o<(+J9{m(`5x8pEZ$G@<^L^{})fe-ACW*h^YBytswOoBz zy3Ya2t(UKsDkyRQMsIMD;*gk8Q4VG0@U9=J)?c^xy8ALtulI=FUtC2 z|1ln~tdm_^C-eKrz2r}=S8Us6GO~$q{yZ$>WcqZ$u^EkwuWJl96mL3QIOl9hc<`Y= zllx_!U)7niU0$|tSyk#*{qHX(Jl?Ty>!edr({nuT@LgRr$*n8WZA*(pK;NmfXKVQfkH(W4V_1)3qQv24st}W8Lf4gHt_)JwLn|GoYw%ihV z)Asw;?9T8l>kl(nhI#qgrAcxdG`NfVrMVJ-2YgRk_>?{?Ac_v0qoG0$3gGh&N; zro4U3IX9&@=T6V~t)6djF5|D2X1Mt_AB{B{;tq}Jx99GWy76++l&xXOF$;?Q3wKV= zbT^fh3>IIS*E#QaxZl?M(dr9}p6}8-`CiDXZpaDl6cl5044oh_Tm~u-_`cuf6IGa?lM&oJbMfq_X>)zH|?Ge3J^F5~dzLn73$?7+q zf~Vj4cJF6Fp7rbpt->wyOKv4--1xS2#U=S=_3h>T+ay?ynf+coRpf!SOH1Z5k@!7DSIbh* zdX^>^mD=CGx#`{mi{pN)cI?!hH8fu$Bx|D@I1Jm)#JdS>wVW2@%95Uzg6 zQ`x$I-(zbRmPaa88#LHYH+=E8D=<a|wyO43=#!w7EwdVgr9LfM%HIA}^0;9_yvng6Gs%hKg-UNCXYro7n=GL&A(U7h zrnaeMuU_0V%XNoucUE0m7Z`XkTseew1OI}J2icsNnU7d#hFcaVR_}P^XQDb=t90wW zV;NlDy(D(S9o?FN2(4?NbY{v_g zdb|ay_DfGq@+tpt?s#Dm^JD>|zit=^>`j*V-|k1HiVOh^hj)FhZHJXibHB{e~&y9T~KJpE-VQ(Pu;Ff7*4&6K;I z>d74@JXiZs#%2x&o2$E$PhQHpuzAYXcT$sXY^x~ZRGcTNK3RZCG4b#56<%#qZhw>2 z6=yo9bI*E0HX}pLJB_8S%dQDn-3#Yh5u7E=8v1X-V+m1{lFD<%`ri90KJ8oZIAHSm zvW1xys?jTRL|U?@n!OULR!TVegn4E0sTyYuImv02Q9b?>C;6|NcG62YK4EVPpBV#V z(VhaCgV#8X&e`=ZvHWm5-q1*F*R4l$uNuEEC=D`wCZe{+Wxk$t=KUK_%BQb?)4}Do zv1$*eXG|pTw;Ni|5C2nI;607cZSIv$)>+L{-&XZa>kjD5E^?~g7QKDJC;3%he&-&# zUr?(X|LS%4^(V@$2R|wwud}<&zR#yt`-)Kb)K9aF&rc5a&op9oQe8CZitSEmrdyha z{+-L2tkAH~UUUndU-J0DiyJl}Jeoeon zx8TZ^6uSfADKCEoU7mS#pV?%7wqjWUgLICCj4{8rE&K4>`375-=i}sZW~R0)pEERW zzbf+;jmW&$wkCe(Vg6M$Hgo$QXp1pK+SO(X%Ld-CvVN=QBz^yhzQlHpi02>d-Hu5* zF*E$Q6Z*;5WNTl+F{Wo*j_nbhwS%EP$SRMcypl&FuzRnr{_I7{2O94knYK;3Zl!#A z-~RiP`LDnI>%7D4Ub>k4Z<+ntTJ>%F@&azmJ$>73e;Mbyipr17{5kz9X=%<6=Wgj) zF0n0klE0hY4;zuy``!OG`HM6kIFV+4`O43|J3hTWXl9|b!1K%#?rATqEcT0sna15c zRQrKvuX>a~%F0xs`X9Z|wr==;YHgjwHt#(LKd&v&?4N!1(!s>asjuf%nFL2XwSC&b z%Dr17ZOxTkCEi*Y-DTI^^q6+s?`O^pj`!_ay`d<6i=4>&fEz}kUSW!Uy+<=B@smzJu8Gm}b z@4e3@p_?yj_9Q>fo&gn-^4Ji6)s}dkzkx(&hVj`pG z8%Em3-ck*h=g&KO`@)S>;|CixSakc#|MLp7G6Y0O|9awk@UhvxiLGxss zDM+!4PraKuC1NSZ#D!*thmHC7oLVc_71gX$aPQU|e`yBBq*wp{&l3M-UU*2eY^CZI z=Tz2)bzL(9=RaOs(Oi5bhTEUwQR%;I#6YYztQJ>3g5$KL6kU81r}b3thGDhO6>rh_2>hR6ecF zl6-U48ns*H!jTpSf>d3$D9n6yXM@5PE{D}Miwa%mT+o@Z&f9~V$yYesLv_B%5fit| zlRQG_$p~a*U0-?V-X1%yWz`~=7M+cF?z6U1?WVx_0Ly+(^|Y@U1}D{|_?;(3Y;W_q zCcM_VLrHu2c{#3~T$~#BkNM_)l*>k`C{_n@hz#`N7U(WB}>;LI_IU;#`1a_az2H; zmHVx;G192jA^x3}*8G)IPEO@H?sVR)NmnFhwy*8`V(r@=nSxi9efz#U?b*6$L-@(p zX}3x%m#mS@5YOB2rHg|>uGjf%cU0!{Y5r54lXSV(XtuhjMrm^_%F@~MqW;B&S;=XN zQGRPgJVI6T+gA^m!F?>PVd`m*Mf@t;)BN3yq< z-P{`R$nm^*Ff4^jf7<1;RVjTwVqB5Y%lD}M^nS*<@Lp=?I;_2`}#AdjyHP$aaLdBkyr6OkwTYQ+FA+#ooWMtn~fHWkHI@-!mSm9A9#IBImW5h)Fa0 zdak$5sxQ1Z`+3dx`!&nYcpMke6cutlv$7;FxOIj-)4ufezO!b1*Q%@y|0X8#O@k%) zdi*;Ei<&tb)_Q;Z$Yt^W#+SmBIi`;n-F*J8ZvUL?3u;%rtSK_M&Qj54AAjNdgFL%M z9V;(ND_vl^@w8ducA@{=|2|c2W}-O>P%iA<2G~ z{iy2I`Q~M}A5OMZ`=zkJQ}A24-&|L(qlJx^{Tn`4?@TFC`|^23eQAA1{{Ej19NEF2 z7R&Bb?{K&8(%<-Da(MLS4bE#V%D4V%lQ+}3X^`(La=F}_2C+xAA|_y(Q0Y=uF+d?!EkvxrHxFa^~0?bL`l}nm#}N zT=oUKuRL|{rO!T)Joq#-*8JT!rFlSbFu>;tUWweB(?Y9$8#$+)@*aUz2KF+*UW7t`JDaV zYksC*H~X>ncY4CgN%r-}a&mod+lg*%mR|k1();0}m#!!EOuWyu?s9z}C|z}LO5|J1 zg1OzbURpAx53T&O_r+W^Ja_-=Wm#7HDU5Gqe}!tRUeW)bYWPxd--YPMx~Ij8RQ4zM z+^Odaus!PU^!$pj6bs|$N%h>tH~tG*GaUH0^uKEGzhD2qnyk8zRJpC+?1Spcn@6;& z4!^qN(_Ohf^cSytx8~jx5qID24Y@m^D|1DN`NE0XPLWv){$BRHs($H$?QMf?_tSpZ zE!CD@a!hCayhSJfneA2BS0lH6HSfLm?y3G!YhBk&dHNyb^)`2L{#BuIy&~G3f$J|c zM}FVavpzc5>tt5#YPLm9QYE})XLTE`UT0SzHZo8tZ1a*YK?b~fy`6K9di%{7dXWoVsh#Jk&{K};)?S!w>HhI0D@JQ1swm@0nK zko>WS`NEs*CRc}tmpjh95Pr$9YgUrv?p!{zp1Az`9P@Y=pP!lk$=-g)$GLwL_>)DP zj#bqK*nB!(b9$cidtUbiZlRS2mA@ukwb$7tZ{2R+`{_@D!`el1+?>nbT;zSU(LHR@ zLiOiA?j&`7Pl(SrYqa2Ww|Mxmin*AJ087v=4FJ(Ge8A;nIOD-~<`gux$$y$4vwcj-su>O5@-pF{bw4n9$35zatTv=eC zc(rkv>pnk^jYk%X@HtPNTk6WeFz;sGn$l(0-HPijteYCRzx?U)UBaw|;R+6Q5v!!N zU!R)%O1|WP z>%UwMZErfVx-BASP2rdG%fy_IE(_h4b)r1Zvt0aGVcmr--V04?-%mM{;LJ6Z=l*%+ z^Y&`7Ct{Odntt36p*efg`ALxrHWUlK=Xz!p_E&cK&3uljH@a-DP5jIHU}Z?9l0j*< zTEYyScm2FZHEJ;`P2%5s%zs|&dviJ2|FQ7Hn9F~5wtZWBzIxO54||tKtj*kc{%ypP zXY1yDy7Nig|M_FVmAh_goa6uW^?*^Je4zmk(~H%tr|;i;qkQ;tznJkx!*6Hy?{4}2 z%woq+hWL$p*59^0802yH`O*0oFN>c(C9L)N+#Okl+HJynS4}zOd3jzyu63dBl|yEE zOP6zR`8hNF{=21&GgRMbKD!?OQMA%I*sDQJ%%JOYv#h+*?ihgkH#1JZSu8{P{w6*P+6lv#^T0% zqN@_x7Md5%{j8q0Y2|L7e8;xr)7P$9*1l^IDBhB79R4?XWx0CmBtxCLy0m$TzM|!e zAC)>;Zm#(MAmsJQy>b3}Pb*85x0}wqf4?<%)@wCho%`nLouX^FCh*Sn`_>pZM}7() zpWQC!RooAxZwTGj{JMzwrNWfp!u4v~m(I8{qxxH8;_u}-SEbjbC9Hg0{GLBH{hb`c zt-Z3D^S7>XjnYhBxsRJ$pZ$N;#$CFrGczZCw%)&Thv1i$VXNHlY}$GHdF8U{SCjAO ze_7Jl+WKZ@Fjsvk=fX7#>~9a-F1E1uNSwoFVaOm;xK8;T>w-`%VWY0)9xNBE5?hxp z&U((YR&A?7$c`{ZE4NvWcIYD+DAx*g73M7o|wZG5iZ+Q*u>iIIj~bvYm%PKh>2ZnxST1 zNHB-55(9_n)F~q8SRQ;{W8&k%{q-gDjU@sGE%uSj*W=H;?rN~Jtlz@+x$t28OxEM8 z_j=Z~?QlPxx#&sJvl%be2iI-*dFjN@gAKRuteSs7DJb{7l`MaPCGVO2AEpL7P1QNZ zcKZm|+Jo1BUaj@)_Ply$@7seMlM*dH1&{s%lZC$yOK?}SLtMx2JI|RzCHKg zuI;P$Y};fKvhwwY8&_x6t1p@ptNi(?*2Kqqf7srrSLb@~tJ51Qxw&p*bn)L`3}%v_ z%2q8{cyjLQ&*4i;zqY^nUY(h+@HP7nHTz#r|1Z+>y?*qtNwSo=y@sTkch>5%kJmrD zKjnI9ZQto^sT4kAQ@C<*!fiX9_5Lpy*)_$%>+(9hav&zoPKJPuA$7GCn$(7iN? zdHH#tpRbRbT|D^mioAaAdAIkIC*AbsGHSWFbjjq62al;&>=io|-T!Ix#@o>wx74TC zS}fk68rt!8-U{JMbF^HP9;K&$tJKI=pFU}ED#*>E%jR-Tjl6wW)O4fmZRQp&#YHR& zS8a2>(>{C6aVzsJqBC3{%*=Jq3ZJywnqh(Iq1W2+Ru-E-DJtrAg*-jUd)3T(*)<-_s;q5RFe_RwQJoS*?Z4fYHAFwzX)IUY;8y<_l188gBD$% zx%NQUnmhB}HXS%;b8o-xbw2H&&1EmPJ6@O0+Sw|$^YN=ocPxK4K94#T&Aj9F!kwR% z2+xXO*n9elP5&W z(#`dM`1cn5b9xc3D#~~IqEqIJtv=^}J~3yEUL~Syl>bS@S@mi$-#5k``$e>*kL>f% zI6nK{$MdaC*_#(uJ!hXiT{i2=p9QL#ZQ}nHcw2e0YrZ|Z`*Wvvv85gJwqvP9g@R=j z3;#s9KR&lLpa0xGjq3ku{XLEjwu-eTIybw{rzI<&`TAh4$=$y0-yOO4y!^!#5@sCz zf1`8vEESn|)3Rnf{Ochqy=M2)1#(+-_$=PozrQ1Yru5a#a?bw$UV0IV~ytK z!>7X!E)PG=-}g{)*EQ|dkl8oy2?V%3q`=5JtUV3pr#Oum0-pB8se|R`QhX0RW(6frJ_nH%qFgY&XvF+Rcr!w=`6@<;- zXsQ)`YWvw|%r!@aj?UTpXL5zX`~C-4re*#Ni8Vc%9K7{4;|IUe)%kTBDlhK%d2j=h zY4vyYnXEPIpFO?!?!an2-PpO_npYu5~Wc&HI*B;eZ$5*^r>6GNY zO?dWM^|qP84?DwFl>YuX<=azFu~`p~8Mh?VzblKe5}lcF_QTG_(`II>|9TQEFD`!e zS^1wGuS;Jt-+0pD-Z`nO=-nC{_6Hx{Y)pNZ_LbefcH;71&1)~-d=&EEwOYvfgM<0y zLtCd_-)8r>Z2!_U8&9jgPNUZg>V6)4=Jxm*JAdfIWd-f-*Q->o`vsrg_H$1@!*#~i zy*ry0YFFQX+ZJ>y>yIg?V`!GXzOQbDXw%90yV=&ZPD|fdmb2b}X4IQ?L2@m!dMhLM zY31^Yr%kcDBdoLBK4iM~^CRvV6Hnb(C6{?iHGW=2Nml10)un6GUx{8iSXWq^ApS3E zx?O{>-+Zp6C0F8jH)N-3%m4D*=r8+a&$5Us8?7Q7R@}NXbK-r`t=ktLi=Nu*6`)z^ zrsjWro!!raX&#TCu~)XKdw=n{RsD+b+aK#t`EOEXeD1U93P?XyG^5S`I)&Cvo>x`yI8%~Z043vIe&o^c7tLI%c&wab zUw;2?>(V=}={d&k|N3`zxxd^0RkGdW>_+!5x!EcECOqEt+REQ&J-eFBmHW#;Ik%edxKyZiUOb3JS8^e0|X6nkDJyz_cP z$qUK9c{5%8taEE^EIB4Po`1N-&}HX*-542#_m#&@eJ{`Std76mb6e_z#2nkH?Uhwc zm5dwyOM3jg;&0k-v#pJxwX|98?`F}$=TG(O!&c?XA5c5!B7I{g!-I8|EWh^LXJBws z%$;yzd1|d!z=4h$W^)UX_kPVeZF&I}_0c7XhkiX}pFQ{ci`h&6JZL-nTw`77yzHZ& zcJA~2_nq<1i~{)s&zEyV{4V;I8*t{cWVDGO?)&Li&VSCldp1knU0M8s#m$(Xt~IyRe_Z{yp58_3D^|NKJe?BgQ#bd&@;`i(0uUcB`hEd0Geb*=FwX{lGf) z@r>uC3mnWgeVeV3yq#gkq=k0rQ$^Qsx_z^m|2(GHca6%!pb)*(-Rav+9C;rF)}2~2 zSL&(Diz@f2nX(@*R(WjPlrVGQUL_W(fRqQOyN^$=e){+1ao$sX2@MK42`eXUi`^T~ zbzA(yybmt3Cn(6iS)IMPpIa|*%~oBzh+5BO0`s>=TX$y6N$`$NuAXG(&;3Bq<7UG}JEr{1+IGs63ca80hqcL_C52kNBcq(#9<)OsMvz!f5_%s7e zmoz88HI2$}{ml4`dD+`j{gO3~sq6mReEM@TDa&O~Ufs{Vtj_80?p4fwzF9DQ+l0rn z=2R$KbyuEeSjTHHStUAZ*V1d;=b9JBTs~kjJNf6E){^wd?VnaBUSE8*kzGz>e&nBu zV`*PQ&&{7>CpRr;Mtq#2g#CdNvz~9(+`#M0I)i85*>$t-oN7+KYd_ zZ-TZ+nLR)G)o73D{-7%*kK{CSX6mSJ+MAn}oP6S+c4(!4m_YnnA=~fA6o0SKHvXK| z8}lghQuL$#^qi1vmF4|ys#4FUhjX;)y_q}F_Ply) \uicontrol {New File} > @@ -76,24 +79,18 @@ \image studio-flow-view-create.png "Create Flow View wizard template" - You only need to select the \uicontrol {Use Event Simulator} check box if - you want to add an event simulator to the flow view. The event simulator - needs the project to be imported to the flow view, so you also need - to select the \uicontrol {Use Application Import} check box. You need the + If you want to add an event simulator to the flow view, select the + \uicontrol {Use Event Simulator} checkbox. In this case, select also the + \uicontrol {Use Application Import} checkbox to import the project to the flow view + as the event simulator requires it to work correctly. You need the import also for access to the project \c Constants.qml file that contains - global settings for the project. + global settings for the project. For more information, see \l {Simulating Events}. - The flow view properties enable you to adjust the appearance of all - the items in the flow: action areas, transition lines, decisions, and - wildcards. You can change the global settings for all items by editing - flow view properties, or you can select an individual action area or - transition line and change the appearance of just that component, including - the color, line thickness, dotted or solid lines, and even the curve of - the line. This enables you to add extra semantics to the design - of the flow diagram itself. - - You can \l{Adding Flow Items}{add flow items} to the flow view to design - the UI. + You can adjust the appearance of all the items in the flow: action areas, + transition lines, decisions, and wildcards. Change the global settings for all items + by editing the flow view properties. To add additional semantics to the flow diagram + design, select an individual action area or transition line and change the appearance + of just that component. \section1 Flow View Properties @@ -102,12 +99,12 @@ \l Visibility sections in the \l Properties view. Specify flow view properties in the \uicontrol {Flow View} section. - \image studio-flow-view-properties.png "Flow View component properties" + \image studio-flow-view-properties.webp "Flow View component properties" To specify the \uicontrol {Flow Item} that is currently visible in the flow view, set its index in the \uicontrol {Current index} field. - You can use the \l{Picking Colors}{color picker} to set colors for: + Use the \l{Picking Colors}{color picker} to set colors for: \list \li Transition lines @@ -133,9 +130,6 @@ area or transition line, see \l{Flow Action Area Properties} and \l{Flow Transition Properties}. - In the \uicontrol Layout tab, you can use \l{Setting Anchors and Margins} - {anchors} to position the component. - In the \uicontrol Advanced section, you can manage the more \l{Specifying Developer Properties}{advanced properties} of components. From 6f7e38d64d97ca3967f6e2e2bd4638e0e77e0fee Mon Sep 17 00:00:00 2001 From: Mats Honkamaa Date: Fri, 12 Apr 2024 09:50:54 +0300 Subject: [PATCH 165/202] Doc: Document fresnel example Task-number: QDS-12367 Change-Id: I528138d68fda435cc8ccd2b80c4e26a57d9779aa Reviewed-by: Johanna Vanhatapio Reviewed-by: Mahmoud Badri --- .../examples/doc/FresnelExample.qdoc | 90 ++++++++++++++++++ .../examples/doc/images/fresnel-angle.webp | Bin 0 -> 7182 bytes .../examples/doc/images/fresnel-example.webp | Bin 0 -> 62494 bytes 3 files changed, 90 insertions(+) create mode 100644 doc/qtdesignstudio/examples/doc/FresnelExample.qdoc create mode 100644 doc/qtdesignstudio/examples/doc/images/fresnel-angle.webp create mode 100644 doc/qtdesignstudio/examples/doc/images/fresnel-example.webp diff --git a/doc/qtdesignstudio/examples/doc/FresnelExample.qdoc b/doc/qtdesignstudio/examples/doc/FresnelExample.qdoc new file mode 100644 index 00000000000..8dbc8bcfbff --- /dev/null +++ b/doc/qtdesignstudio/examples/doc/FresnelExample.qdoc @@ -0,0 +1,90 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page fresnel-effect-example.html + \ingroup studioexamples + + \title Fresnel Example + \brief Illustrates how to work with the fresnel effect. + + \image fresnel-example.webp + + The \e{Fresnel} example illustrates how to add and adjust a fresnel effect on + a 3D model. + + The fresnel effect affects how materials reflect light at different viewing angles. Imagine the + water on a lake. If you look down at the water from straight above, you can see through the + water, but if you look from a lower angle, the reflections are stronger. + + \image fresnel-angle.webp + + \section1 Running the Example + + To run the example in \QDS, go to the \uicontrol Welcome screen and select the example + from the \uicontrol Examples tab. + + \section1 The 3D Scene + + The example project consists of a basic 3D scene with the following components: + + \list + \li A 3D model. + \li A directional light. + \li An HDR image used to light the scene (image-based lighting). + \endlist + + \section1 The Material + + The material on the 3D model in this example is a principled material with a + clearcoat. + + \section2 Clearcoat + + A clearcoat is an additional specular layer applied to the surface of a material. The + clearcoating is transparent and doesn't add any color to the material, but it affects + how light interacts with the material. + + You adjust clearcoat properties independently from the base material. + + \section2 Fresnel Properties + + The following properties affect how the fresnel effect renders. These properties are + available both for the base material and the clearcoat layer. Adjusting the settings for the + clearcoat has a bigger visual effect. + + \table + \header + \li Property + \li Description + \row + \li Fresnel power + \li Increasing the fresnel power decreases the head-on reflections (steep viewing angle) + while maintaining the reflections seen from more shallow viewing angles. + \row + \li Enable scale and bias + \li Takes the scale and bias properties into account. + \row + \li Scale + \li Determines the rate of change in reflection intensity as the viewing angle varies. A + large scale value results in a gentler transition between weak and strong reflections, while + a smaller scale creates a more abrupt shift in reflection intensity. + \row + \li Bias + \li Controls the offset for the fresnel power property and determines how quickly the + reflection transitions from weak to strong as the viewing + angle changes. A larger bias value shifts the transition point toward steeper angles. + \endtable + + \section3 Adjusting the Fresnel Settings + + To adjust the settings: + + \list 1 + \li In \uicontrol {Material Browser}, double-click \e {Monkey Material}. + \li In \uicontrol {Material Editor}, find the properties under + \uicontrol {Clearcoat} and \uicontrol {Fresnel} respectively. + \endlist + + \note You see the changes live as you edit them in the \uicontrol 2D view. +*/ diff --git a/doc/qtdesignstudio/examples/doc/images/fresnel-angle.webp b/doc/qtdesignstudio/examples/doc/images/fresnel-angle.webp new file mode 100644 index 0000000000000000000000000000000000000000..2fe11380f05c246a9f9598a68173552e080d5bce GIT binary patch literal 7182 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/examples/doc/images/fresnel-example.webp b/doc/qtdesignstudio/examples/doc/images/fresnel-example.webp new file mode 100644 index 0000000000000000000000000000000000000000..75436595aac72be2192584424d23ec32475b68a9 GIT binary patch literal 62494 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 literal 0 HcmV?d00001 From f006cfb233f03c85f32530ec287d3608764d2666 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Thu, 11 Apr 2024 12:59:18 +0300 Subject: [PATCH 166/202] QmlDesigner: Warn the user if the project is not imported When the project is not importd in the design document, DataStore cannot be found by the project manager. User should add it to make the Model Editor work. So we will warn the user in the Model Editor view. Task-number: QDS-12119 Change-Id: I313e5553e8b0a0ef3b97c50d61db80c0a8d382f8 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- .../CollectionDetailsView.qml | 30 +++++- .../collectioneditor/collectionview.cpp | 101 ++++++++++-------- .../collectioneditor/collectionview.h | 8 +- .../collectioneditor/collectionwidget.cpp | 23 ++++ .../collectioneditor/collectionwidget.h | 11 +- 5 files changed, 123 insertions(+), 50 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml index e3f1a589595..e40a3c94577 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml @@ -498,10 +498,38 @@ Rectangle { } } + ColumnLayout { + id: importsProblem + + visible: !topRow.visible && rootView.dataStoreExists && !rootView.projectImportExists + width: parent.width + anchors.verticalCenter: parent.verticalCenter + clip: true + + Text { + text: qsTr("Import the project to your design document to make the Model Editor enabled.") + Layout.alignment: Qt.AlignCenter + Layout.maximumWidth: parent.width + leftPadding: StudioTheme.Values.collectionItemTextPadding + rightPadding: StudioTheme.Values.collectionItemTextPadding + color: StudioTheme.Values.themeTextColor + font.pixelSize: StudioTheme.Values.mediumFontSize + wrapMode: Text.Wrap + } + + HelperWidgets.Button { + text: qsTr("Enable DataStore (This will add the required import)") + Layout.alignment: Qt.AlignCenter + onClicked: rootView.addProjectImport() + leftPadding: StudioTheme.Values.collectionItemTextPadding + rightPadding: StudioTheme.Values.collectionItemTextPadding + } + } + Text { anchors.centerIn: parent text: qsTr("There are no models in this project.\nAdd or import a model.") - visible: !topRow.visible + visible: !topRow.visible && !importsProblem.visible color: StudioTheme.Values.themeTextColor font.pixelSize: StudioTheme.Values.mediumFontSize } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp index b47fb6a51fe..0c9a2eed94d 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp @@ -35,6 +35,12 @@ bool isStudioCollectionModel(const QmlDesigner::ModelNode &node) return node.metaInfo().isQtQuickStudioUtilsJsonListModel(); } +inline bool isProjectImport(const QmlDesigner::Import &import) +{ + ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectManager::startupProject(); + return currentProject && import.toString() == currentProject->displayName(); +} + inline void setVariantPropertyValue(const QmlDesigner::ModelNode &node, const QmlDesigner::PropertyName &propertyName, const QVariant &value) @@ -98,7 +104,7 @@ QmlDesigner::WidgetInfo CollectionView::widgetInfo() connect(listModel, &CollectionListModel::modelReset, this, [this] { CollectionListModel *listModel = m_widget->listModel().data(); - if (listModel->sourceNode() == m_dataStore->modelNode()) + if (listModel->sourceNode() == dataStoreNode()) m_dataStore->setCollectionNames(listModel->collections()); }); @@ -140,19 +146,14 @@ QmlDesigner::WidgetInfo CollectionView::widgetInfo() void CollectionView::modelAttached(Model *model) { AbstractView::modelAttached(model); + m_widget->setProjectImportExists(Utils::anyOf(model->imports(), isProjectImport)); resetDataStoreNode(); } void CollectionView::modelAboutToBeDetached([[maybe_unused]] Model *model) { - m_libraryInfoIsUpdated = false; - m_reloadCounter = 0; - m_rewriterAmended = false; - m_dataStoreTypeFound = false; - disconnect(m_documentUpdateConnection); - QTC_ASSERT(m_delayedTasks.isEmpty(), m_delayedTasks.clear()); - if (m_widget) - m_widget->listModel()->setDataStoreNode(); + unloadDataStore(); + m_widget->setProjectImportExists(false); } void CollectionView::selectedNodesChanged(const QList &selectedNodeList, @@ -177,6 +178,17 @@ void CollectionView::selectedNodesChanged(const QList &selectedNodeLi m_widget->setTargetNodeSelected(singleSelectedHasModelProperty); } +void CollectionView::importsChanged(const Imports &addedImports, const Imports &removedImports) +{ + if (Utils::anyOf(addedImports, isProjectImport)) { + m_widget->setProjectImportExists(true); + resetDataStoreNode(); + } else if (Utils::anyOf(removedImports, isProjectImport)) { + m_widget->setProjectImportExists(false); + unloadDataStore(); + } +} + void CollectionView::customNotification(const AbstractView *, const QString &identifier, const QList &nodeList, @@ -223,6 +235,22 @@ void CollectionView::addResource(const QUrl &url, const QString &name) }); } +void CollectionView::addProjectImport() +{ + if (!m_widget) + return; + + ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectManager::startupProject(); + if (!currentProject) + return; + + executeInTransaction(__FUNCTION__, [&] { + Import import = Import::createLibraryImport(currentProject->displayName()); + if (!model()->hasImport(import, true, true)) + model()->changeImports({import}, {}); + }); +} + void CollectionView::assignCollectionToNode(const QString &collectionName, const ModelNode &node) { if (!m_widget) @@ -314,7 +342,8 @@ void CollectionView::resetDataStoreNode() m_dataStore->reloadModel(); - ModelNode dataStore = m_dataStore->modelNode(); + ModelNode dataStore = dataStoreNode(); + m_widget->setDataStoreExists(dataStore.isValid()); if (!dataStore || m_widget->listModel()->sourceNode() == dataStore) return; @@ -355,28 +384,11 @@ void CollectionView::ensureDataStoreExists() { bool filesJustCreated = false; bool filesExist = CollectionEditorUtils::ensureDataStoreExists(filesJustCreated); - if (filesExist) { - if (filesJustCreated) { - // Force code model reset to notice changes to existing module - auto modelManager = QmlJS::ModelManagerInterface::instance(); - if (modelManager) { - m_libraryInfoIsUpdated = false; - - m_expectedDocumentUpdates.clear(); - m_expectedDocumentUpdates << CollectionEditorUtils::dataStoreQmlFilePath() - << CollectionEditorUtils::dataStoreJsonFilePath(); - - m_documentUpdateConnection = connect(modelManager, - &QmlJS::ModelManagerInterface::documentUpdated, - this, - &CollectionView::onDocumentUpdated); - - modelManager->resetCodeModel(); - } - resetDataStoreNode(); - } else { - m_libraryInfoIsUpdated = true; - } + if (filesExist && filesJustCreated) { + // Force code model reset to notice changes to existing module + if (auto modelManager = QmlJS::ModelManagerInterface::instance()) + modelManager->resetCodeModel(); + resetDataStoreNode(); } } @@ -396,6 +408,18 @@ NodeMetaInfo CollectionView::jsonCollectionMetaInfo() const return model()->metaInfo(CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME); } +void CollectionView::unloadDataStore() +{ + m_reloadCounter = 0; + m_rewriterAmended = false; + m_dataStoreTypeFound = false; + QTC_ASSERT(m_delayedTasks.isEmpty(), m_delayedTasks.clear()); + if (m_widget) { + m_widget->setDataStoreExists(dataStoreNode().isValid()); + m_widget->listModel()->setDataStoreNode(); + } +} + void CollectionView::ensureStudioModelImport() { executeInTransaction(__FUNCTION__, [&] { @@ -420,23 +444,12 @@ void CollectionView::onItemLibraryNodeCreated(const ModelNode &node) } } -void CollectionView::onDocumentUpdated(const QSharedPointer &doc) -{ - if (m_expectedDocumentUpdates.contains(doc->fileName())) - m_expectedDocumentUpdates.remove(doc->fileName()); - - if (m_expectedDocumentUpdates.isEmpty()) { - disconnect(m_documentUpdateConnection); - m_libraryInfoIsUpdated = true; - } -} - void CollectionView::addTask(QSharedPointer task) { ensureDataStoreExists(); if (m_dataStoreTypeFound) task->process(); - else if (m_dataStore->modelNode()) + else if (dataStoreNode()) m_delayedTasks << task; } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h index d8be8b7055c..3de3bd7ae6d 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h @@ -41,6 +41,8 @@ public: void selectedNodesChanged(const QList &selectedNodeList, const QList &lastSelectedNodeList) override; + void importsChanged(const Imports &addedImports, const Imports &removedImports) override; + void customNotification(const AbstractView *view, const QString &identifier, const QList &nodeList, @@ -48,6 +50,7 @@ public: void addResource(const QUrl &url, const QString &name); + void addProjectImport(); void assignCollectionToNode(const QString &collectionName, const ModelNode &node); void assignCollectionToSelectedNode(const QString &collectionName); void addNewCollection(const QString &collectionName, const QJsonObject &localCollection); @@ -65,17 +68,14 @@ private: friend class CollectionTask; NodeMetaInfo jsonCollectionMetaInfo() const; + void unloadDataStore(); void ensureStudioModelImport(); void onItemLibraryNodeCreated(const ModelNode &node); - void onDocumentUpdated(const QSharedPointer &doc); void addTask(QSharedPointer task); std::unique_ptr m_dataStore; Utils::UniqueObjectPtr m_widget; - QSet m_expectedDocumentUpdates; QList> m_delayedTasks; - QMetaObject::Connection m_documentUpdateConnection; - bool m_libraryInfoIsUpdated = false; bool m_dataStoreTypeFound = false; bool m_rewriterAmended = false; int m_reloadCounter = 0; diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp index 7ecd54174aa..dd706145cf2 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp @@ -251,6 +251,11 @@ bool CollectionWidget::importFile(const QString &collectionName, return false; } +void CollectionWidget::addProjectImport() +{ + m_view->addProjectImport(); +} + void CollectionWidget::addCollectionToDataStore(const QString &collectionName) { m_view->addNewCollection(collectionName, CollectionEditorUtils::defaultCollection()); @@ -289,6 +294,24 @@ void CollectionWidget::setTargetNodeSelected(bool selected) emit targetNodeSelectedChanged(m_targetNodeSelected); } +void CollectionWidget::setProjectImportExists(bool exists) +{ + if (m_projectImportExists == exists) + return; + + m_projectImportExists = exists; + emit projectImportExistsChanged(m_projectImportExists); +} + +void CollectionWidget::setDataStoreExists(bool exists) +{ + if (m_dataStoreExists == exists) + return; + + m_dataStoreExists = exists; + emit dataStoreExistsChanged(m_dataStoreExists); +} + void CollectionWidget::deleteSelectedCollection() { QMetaObject::invokeMethod(m_quickWidget->quickWidget()->rootObject(), "deleteSelectedCollection"); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h index f06edd23231..13c3566c78e 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h @@ -22,6 +22,8 @@ class CollectionWidget : public QFrame Q_OBJECT Q_PROPERTY(bool targetNodeSelected MEMBER m_targetNodeSelected NOTIFY targetNodeSelectedChanged) + Q_PROPERTY(bool projectImportExists MEMBER m_projectImportExists NOTIFY projectImportExistsChanged) + Q_PROPERTY(bool dataStoreExists MEMBER m_dataStoreExists NOTIFY dataStoreExistsChanged) public: CollectionWidget(CollectionView *view); @@ -33,7 +35,7 @@ public: void reloadQmlSource(); - virtual QSize minimumSizeHint() const; + QSize minimumSizeHint() const override; Q_INVOKABLE bool loadJsonFile(const QUrl &url, const QString &collectionName = {}); Q_INVOKABLE bool loadCsvFile(const QUrl &url, const QString &collectionName = {}); @@ -45,6 +47,7 @@ public: const QUrl &url, const bool &firstRowIsHeader = true); + Q_INVOKABLE void addProjectImport(); Q_INVOKABLE void addCollectionToDataStore(const QString &collectionName); Q_INVOKABLE void assignCollectionToSelectedNode(const QString collectionName); Q_INVOKABLE void openCollection(const QString &collectionName); @@ -52,11 +55,15 @@ public: void warn(const QString &title, const QString &body); void setTargetNodeSelected(bool selected); + void setProjectImportExists(bool exists); + void setDataStoreExists(bool exists); void deleteSelectedCollection(); signals: void targetNodeSelectedChanged(bool); + void projectImportExistsChanged(bool); + void dataStoreExistsChanged(bool); private: QString generateUniqueCollectionName(const ModelNode &node, const QString &name); @@ -67,6 +74,8 @@ private: std::unique_ptr m_collectionDetailsSortFilterModel; QScopedPointer m_quickWidget; bool m_targetNodeSelected = false; + bool m_projectImportExists = false; + bool m_dataStoreExists = false; }; } // namespace QmlDesigner From 90c4826c2937be4685a78349f76aaedfc33964dd Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Wed, 17 Apr 2024 15:20:56 +0200 Subject: [PATCH 167/202] QmlDesigner: remove annoying output Change-Id: I7f74ece071923208764b767a70436145888fbb54 Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../effectComposerQmlSources/EffectComposerPreview.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerPreview.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerPreview.qml index aefffcc6bcc..c0c47f73661 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerPreview.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerPreview.qml @@ -382,7 +382,6 @@ Column { Connections { target: effectComposerModel function onShadersBaked() { - console.log("Shaders Baked!") updateTimer.restart() } } From c709e6d5552c734e795b61090288a81abc6321ed Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 17 Apr 2024 16:04:37 +0200 Subject: [PATCH 168/202] QmlJS: Improve/simplify error message Task-number: QDS-12149 Change-Id: Idaa9bb2624aa3591cafd3ea6c8b62b9dea7cb61a Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Pranta Ghosh Dastider --- src/libs/qmljs/qmljsstaticanalysismessage.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libs/qmljs/qmljsstaticanalysismessage.cpp b/src/libs/qmljs/qmljsstaticanalysismessage.cpp index 5b15e694281..432b97aba72 100644 --- a/src/libs/qmljs/qmljsstaticanalysismessage.cpp +++ b/src/libs/qmljs/qmljsstaticanalysismessage.cpp @@ -189,8 +189,9 @@ StaticAnalysisMessages::StaticAnalysisMessages() Tr::tr("Maximum string value length is %1."), 1); newMsg(ErrInvalidArrayValueLength, Error, Tr::tr("%1 elements expected in array value."), 1); - newMsg(WarnImperativeCodeNotEditableInVisualDesigner, Warning, - Tr::tr("Imperative code is not supported in Qt Design Studio.")); + newMsg(WarnImperativeCodeNotEditableInVisualDesigner, + Warning, + Tr::tr("JavaScript can break the visual tooling in Qt Design Studio.")); newMsg(WarnUnsupportedTypeInVisualDesigner, Warning, Tr::tr("This type (%1) is not supported in Qt Design Studio."), 1); newMsg(WarnReferenceToParentItemNotSupportedByVisualDesigner, Warning, From ad408553af7d3e40dacb28298d9b4e7a74ea3761 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Wed, 17 Apr 2024 13:07:56 +0300 Subject: [PATCH 169/202] QmlDesigner: Enable expanding content library user categories Change-Id: Id3b49773b02e4922a06b71e6ed7a7098ed4d4062 Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- .../contentLibraryQmlSource/ContentLibraryUserView.qml | 10 ++-------- .../contentlibrary/contentlibraryusermodel.cpp | 6 +----- .../contentlibrary/contentlibraryusermodel.h | 2 +- 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml index 47d3c6ce773..85123af3743 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml @@ -73,16 +73,10 @@ HelperWidgets.ScrollView { caption: categoryName visible: categoryVisible - expanded: categoryExpanded - expandOnClick: false category: "ContentLib_User" - onToggleExpand: categoryExpanded = !categoryExpanded - onExpand: categoryExpanded = true - onCollapse: categoryExpanded = false - function expandSection() { - categoryExpanded = true + section.expanded = true } property alias count: repeater.count @@ -123,7 +117,7 @@ HelperWidgets.ScrollView { roleValue: "texture" delegate: ContentLibraryTexture { width: root.cellWidth - height: root.cellHeight + height: root.cellWidth // for textures use a square size since there is no name row // onShowContextMenu: ctxMenu.popupMenu(modelData) // TODO } diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp index f62ef95fccc..243e0d96622 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp @@ -64,9 +64,6 @@ QVariant ContentLibraryUserModel::data(const QModelIndex &index, int role) const if (role == VisibleRole) return true; // TODO - if (role == ExpandedRole) - return true; // TODO - return {}; } @@ -148,7 +145,6 @@ QHash ContentLibraryUserModel::roleNames() const static const QHash roles { {NameRole, "categoryName"}, {VisibleRole, "categoryVisible"}, - {ExpandedRole, "categoryExpanded"}, {ItemsRole, "categoryItems"} }; return roles; @@ -289,7 +285,7 @@ void ContentLibraryUserModel::loadTextureBundle() } int texSectionIdx = 1; - emit dataChanged(index(texSectionIdx, 0), index(texSectionIdx, 0)); + emit dataChanged(index(texSectionIdx), index(texSectionIdx)); } bool ContentLibraryUserModel::hasRequiredQuick3DImport() const diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h index 2dde73e5de4..26edbf5ec33 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h @@ -125,7 +125,7 @@ private: QString m_importerBundleId; QStringList m_importerSharedFiles; - enum Roles { NameRole = Qt::UserRole + 1, VisibleRole, ExpandedRole, ItemsRole }; + enum Roles { NameRole = Qt::UserRole + 1, VisibleRole, ItemsRole }; }; } // namespace QmlDesigner From a95ee86ad27e65bc7568cdb3b7146b1e6d605140 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 18 Apr 2024 16:07:08 +0300 Subject: [PATCH 170/202] QmlDesigner: Reset zoom when aligning view to camera Resetting the zoom after aligning edit camera to scene camera avoids confusing behavior in some cases, primarily when the alignment is triggered from outside 3D view. Fixes: QDS-12348 Fixes: QDS-12350 Change-Id: Ic0c076ea3dff3f1be5f57e20c0dbbe6069867b51 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../mockfiles/qt6/EditCameraController.qml | 5 ++++- .../qml2puppet/editor3d/generalhelper.cpp | 21 ++++++++++++------- .../qml2puppet/editor3d/generalhelper.h | 4 ++-- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml index a76a1cdf08c..2d77aaf9877 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml @@ -138,7 +138,10 @@ Item { else nodes = targetNodes - _lookAtPoint = _generalHelper.alignView(camera, nodes, _lookAtPoint); + var newLookAtAndZoom = _generalHelper.alignView(camera, nodes, _lookAtPoint, + _defaultCameraLookAtDistance); + _lookAtPoint = newLookAtAndZoom.toVector3d(); + _zoomFactor = newLookAtAndZoom.w; storeCameraState(0); } diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp index 4b47179915e..b33a480a450 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp @@ -494,12 +494,10 @@ void GeneralHelper::alignCameras(QQuick3DCamera *camera, const QVariant &nodes) // Aligns the camera to the first camera in nodes list. // Aligning means taking the position and XY rotation from the source camera. Rest of the properties // remain the same, as this is used to align edit cameras, which have fixed Z-rot, fov, and clips. -// The new lookAt is set at same distance away as it was previously and scale isn't adjusted, so -// the zoom factor of the edit camera stays the same. -QVector3D GeneralHelper::alignView(QQuick3DCamera *camera, const QVariant &nodes, - const QVector3D &lookAtPoint) +// The camera zoom is reset to default. +QVector4D GeneralHelper::alignView(QQuick3DCamera *camera, const QVariant &nodes, + const QVector3D &lookAtPoint, float defaultLookAtDistance) { - float lastDistance = (lookAtPoint - camera->position()).length(); const QVariantList varNodes = nodes.value(); QQuick3DCamera *cameraNode = nullptr; for (const auto &varNode : varNodes) { @@ -509,15 +507,24 @@ QVector3D GeneralHelper::alignView(QQuick3DCamera *camera, const QVariant &nodes } if (cameraNode) { + if (auto orthoCamera = qobject_cast(camera)) { + orthoCamera->setHorizontalMagnification(1.f); + orthoCamera->setVerticalMagnification(1.f); + // Force update on transform just in case position and rotation didn't change + float x = orthoCamera->x(); + orthoCamera->setX(x + 1.f); + orthoCamera->setX(x); + } camera->setPosition(cameraNode->scenePosition()); QVector3D newRotation = cameraNode->sceneRotation().toEulerAngles(); newRotation.setZ(0.f); camera->setEulerRotation(newRotation); + } - QVector3D lookAt = camera->position() + camera->forward() * lastDistance; + QVector3D lookAt = camera->position() + camera->forward() * defaultLookAtDistance; - return lookAt; + return QVector4D(lookAt, 1.f); } bool GeneralHelper::fuzzyCompare(double a, double b) diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h index 781a19dbff5..5622918a64b 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h @@ -74,8 +74,8 @@ public: QQuick3DViewport *viewPort, float defaultLookAtDistance, bool closeUp); Q_INVOKABLE void alignCameras(QQuick3DCamera *camera, const QVariant &nodes); - Q_INVOKABLE QVector3D alignView(QQuick3DCamera *camera, const QVariant &nodes, - const QVector3D &lookAtPoint); + Q_INVOKABLE QVector4D alignView(QQuick3DCamera *camera, const QVariant &nodes, + const QVector3D &lookAtPoint, float defaultLookAtDistance); Q_INVOKABLE bool fuzzyCompare(double a, double b); Q_INVOKABLE void delayedPropertySet(QObject *obj, int delay, const QString &property, const QVariant& value); From 2618d1b54431319530f9b32ee59202a671fc1e77 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 18 Apr 2024 16:31:33 +0200 Subject: [PATCH 171/202] QmlDesigner: Create the import for the first node created Change-Id: Id0c251857a69844318fecd40923fcd6a7e1ef33a Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- .../propertyEditorQmlSources/QtQuick/EffectsSection.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml index 2693fc3ef60..060f65babee 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml @@ -66,9 +66,9 @@ Section { if (root.hasDesignerEffect) { root.effectNodeWrapper.deleteModelNode() } else { - modelNodeBackend.createModelNode(-1, "data", "DesignEffect") + modelNodeBackend.createModelNode(-1, "data", "DesignEffect", "QtQuick.Studio.DesignEffects") var effectNode = modelNodeBackend.allChildrenOfType("DesignEffect") - modelNodeBackend.createModelNode(effectNode, "effects", "DesignDropShadow", "QtQuick.Studio.DesignEffects") + modelNodeBackend.createModelNode(effectNode, "effects", "DesignDropShadow") } root.invalidate() } From cf05fa6846862caf45ed76d26653bf8001b3b4a2 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 18 Apr 2024 16:32:07 +0200 Subject: [PATCH 172/202] QmlDesgner: Fix whitespace Change-Id: Iaa0fd96bd553d1f0d081d30b2e8772e3db878b1e Reviewed-by: Thomas Hartmann --- .../propertyEditorQmlSources/QtQuick/EffectsSection.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml index 060f65babee..8b1d201c048 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml @@ -66,7 +66,7 @@ Section { if (root.hasDesignerEffect) { root.effectNodeWrapper.deleteModelNode() } else { - modelNodeBackend.createModelNode(-1, "data", "DesignEffect", "QtQuick.Studio.DesignEffects") + modelNodeBackend.createModelNode(-1, "data", "DesignEffect", "QtQuick.Studio.DesignEffects") var effectNode = modelNodeBackend.allChildrenOfType("DesignEffect") modelNodeBackend.createModelNode(effectNode, "effects", "DesignDropShadow") } From f84070f634badc3514365dc5e926a7ee19cf9eb6 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 18 Apr 2024 16:32:07 +0200 Subject: [PATCH 173/202] QmlDesigner: Latest effects do not require a custom parser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This also fixes the double rendering issue. Change-Id: I8562fb0c4f3c0a57373e117f7a3deb44c71a37de Reviewed-by: Henning GrĂĽndl --- .../qmldesigner/designercore/metainfo/nodemetainfo.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index 0d82a975c22..b8c3e610be5 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -2456,11 +2456,8 @@ bool NodeMetaInfo::usesCustomParser() const return false; auto type = simplifiedTypeName(); - return type == "VisualItemModel" - || type == "VisualDataModel" - || type == "ListModel" - || type == "XmlListModel" - || type == "DesignEffect"; + return type == "VisualItemModel" || type == "VisualDataModel" || type == "ListModel" + || type == "XmlListModel"; } } From 2cd9efee63bfa79a26604b4f496534f495732c8c Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 19 Apr 2024 12:57:58 +0300 Subject: [PATCH 174/202] QmlDesigner: Fix changing dynamic properties on root material object Changing dynamic properties on a CustomMaterial root object didn't trigger rendering. Also there were duplicate of the property generated when it was changed, which made quick3d side ignore the property change. Fixes: QDS-12469 Change-Id: Ia83eaa76fd4f43428a01baea9a3491a8917aa30d Reviewed-by: Thomas Hartmann Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../instances/qt5rendernodeinstanceserver.cpp | 22 ++++++++++++++++++- .../instances/qt5rendernodeinstanceserver.h | 1 + .../qmlprivategate/qmlprivategate.cpp | 4 +++- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp index f9191950fd5..53af00ab094 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp @@ -146,7 +146,9 @@ void Qt5RenderNodeInstanceServer::collectItemChangesAndSendChangeCommands() && !changedPropertyList().isEmpty() && nodeInstanceClient()->bytesToWrite() < 10000) { - Internal::QuickItemNodeInstance::updateDirtyNode(rootNodeInstance().contentItem()); + QQuickItem *rootItem = rootNodeInstance().contentItem(); + QQuickDesignerSupport::addDirty(rootItem, QQuickDesignerSupport::Content); + QQuickDesignerSupport::updateDirtyNode(rootItem); nodeInstanceClient()->pixmapChanged(createPixmapChangedCommand({rootNodeInstance()})); } @@ -239,6 +241,24 @@ void Qt5RenderNodeInstanceServer::changePropertyValues(const ChangeValuesCommand if (instance.hasParent() && instance.propertyNames().contains("_isEffectItem")) makeDirtyRecursive(instance.parent()); + } else if (container.isDynamic() && hasInstanceForId(container.instanceId())) { + // Changes to dynamic properties are not always noticed by normal signal spy mechanism + addChangedProperty(InstancePropertyPair(instanceForId(container.instanceId()), + container.name())); + } + } +} + +void Qt5RenderNodeInstanceServer::changePropertyBindings(const ChangeBindingsCommand &command) +{ + Qt5NodeInstanceServer::changePropertyBindings(command); + + const QVector changes = command.bindingChanges; + for (const PropertyBindingContainer &container : changes) { + if (container.isDynamic() && hasInstanceForId(container.instanceId())) { + // Changes to dynamic properties are not always noticed by normal signal spy mechanism + addChangedProperty(InstancePropertyPair(instanceForId(container.instanceId()), + container.name())); } } } diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.h b/src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.h index 738aa47b185..1e0f885da16 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.h +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.h @@ -18,6 +18,7 @@ public: void completeComponent(const CompleteComponentCommand &command) override; void removeSharedMemory(const RemoveSharedMemoryCommand &command) override; void changePropertyValues(const ChangeValuesCommand &command) override; + void changePropertyBindings(const ChangeBindingsCommand &command) override; protected: void collectItemChangesAndSendChangeCommands() override; diff --git a/src/tools/qml2puppet/qmlprivategate/qmlprivategate.cpp b/src/tools/qml2puppet/qmlprivategate/qmlprivategate.cpp index 928f4bf2956..096ac362441 100644 --- a/src/tools/qml2puppet/qmlprivategate/qmlprivategate.cpp +++ b/src/tools/qml2puppet/qmlprivategate/qmlprivategate.cpp @@ -540,7 +540,9 @@ void tweakObjects(QObject *object) void createNewDynamicProperty(QObject *object, QQmlEngine *engine, const QString &name) { - QQuickDesignerSupportProperties::createNewDynamicProperty(object, engine, name); + QQmlProperty qmlProp(object, name, engine->contextForObject(object)); + if (!qmlProp.isValid()) + QQuickDesignerSupportProperties::createNewDynamicProperty(object, engine, name); } void registerNodeInstanceMetaObject(QObject *object, QQmlEngine *engine) From 487eda9019631e7c7292dc72be7985130f7de0ae Mon Sep 17 00:00:00 2001 From: Knud Dollereder Date: Wed, 17 Apr 2024 16:53:43 +0200 Subject: [PATCH 175/202] QmlProjectManager: Remove old cmake generator and suppress warnings about .hints files not being part of the project Fixes: QDS-12507 Fixes: QDS-12508 Change-Id: If3c2f5ce716b8744dd4fee91ec7bba5293163a97 Reviewed-by: Thomas Hartmann --- src/plugins/qmlprojectmanager/CMakeLists.txt | 7 - .../cmakegen/boilerplate.qrc | 11 - .../cmakegen/checkablefiletreeitem.cpp | 45 -- .../cmakegen/checkablefiletreeitem.h | 31 - .../cmakegen/cmakegenerator.cpp | 8 +- .../cmakegen/cmakegenerator.h | 1 + .../cmakegen/cmakegeneratordialog.cpp | 168 ----- .../cmakegen/cmakegeneratordialog.h | 51 -- .../cmakegeneratordialogtreemodel.cpp | 174 ----- .../cmakegen/cmakegeneratordialogtreemodel.h | 51 -- .../cmakegen/cmakeprojectconverter.cpp | 422 ----------- .../cmakegen/cmakeprojectconverter.h | 77 -- .../cmakegen/cmakeprojectconverterdialog.cpp | 180 ----- .../cmakegen/cmakeprojectconverterdialog.h | 47 -- .../cmakegen/cmakewriter.cpp | 25 +- .../qmlprojectmanager/cmakegen/cmakewriter.h | 5 +- .../cmakegen/cmakewriterv0.cpp | 3 +- .../cmakegen/cmakewriterv1.cpp | 3 +- .../cmakegen/generatecmakelists.cpp | 668 ------------------ .../cmakegen/generatecmakelists.h | 80 --- .../cmakegen/generatecmakelistsconstants.h | 42 -- .../cmakegen/qmlprojectappmainqml.tpl | 14 - .../cmakegen/qmlprojectenvheader.tpl | 11 - .../cmakegen/qmlprojectmaincmakelists.tpl | 52 -- .../cmakegen/qmlprojectmaincpp.tpl | 35 - .../cmakegen/qmlprojectmaincppheader.tpl | 8 - .../cmakegen/qmlprojectmainqml.tpl | 6 - .../cmakegen/qmlprojectmodulecmakelists.tpl | 12 - .../cmakegen/qmlprojectmodules.tpl | 16 - .../qmlprojectgen/qmlprojectgenerator.cpp | 4 +- .../qmlprojectmanager/qmlprojectmanager.qbs | 7 - .../qmlprojectmanager/qmlprojectplugin.cpp | 7 +- 32 files changed, 29 insertions(+), 2242 deletions(-) delete mode 100644 src/plugins/qmlprojectmanager/cmakegen/checkablefiletreeitem.cpp delete mode 100644 src/plugins/qmlprojectmanager/cmakegen/checkablefiletreeitem.h delete mode 100644 src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialog.cpp delete mode 100644 src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialog.h delete mode 100644 src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialogtreemodel.cpp delete mode 100644 src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialogtreemodel.h delete mode 100644 src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverter.cpp delete mode 100644 src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverter.h delete mode 100644 src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverterdialog.cpp delete mode 100644 src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverterdialog.h delete mode 100644 src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.cpp delete mode 100644 src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.h delete mode 100644 src/plugins/qmlprojectmanager/cmakegen/generatecmakelistsconstants.h delete mode 100644 src/plugins/qmlprojectmanager/cmakegen/qmlprojectappmainqml.tpl delete mode 100644 src/plugins/qmlprojectmanager/cmakegen/qmlprojectenvheader.tpl delete mode 100644 src/plugins/qmlprojectmanager/cmakegen/qmlprojectmaincmakelists.tpl delete mode 100644 src/plugins/qmlprojectmanager/cmakegen/qmlprojectmaincpp.tpl delete mode 100644 src/plugins/qmlprojectmanager/cmakegen/qmlprojectmaincppheader.tpl delete mode 100644 src/plugins/qmlprojectmanager/cmakegen/qmlprojectmainqml.tpl delete mode 100644 src/plugins/qmlprojectmanager/cmakegen/qmlprojectmodulecmakelists.tpl delete mode 100644 src/plugins/qmlprojectmanager/cmakegen/qmlprojectmodules.tpl diff --git a/src/plugins/qmlprojectmanager/CMakeLists.txt b/src/plugins/qmlprojectmanager/CMakeLists.txt index b3eb298f3bc..77f3932609a 100644 --- a/src/plugins/qmlprojectmanager/CMakeLists.txt +++ b/src/plugins/qmlprojectmanager/CMakeLists.txt @@ -42,13 +42,6 @@ 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 cmakegenerator.cpp cmakegenerator.h cmakewriter.cpp cmakewriter.h cmakewriterv0.cpp cmakewriterv0.h diff --git a/src/plugins/qmlprojectmanager/cmakegen/boilerplate.qrc b/src/plugins/qmlprojectmanager/cmakegen/boilerplate.qrc index b4eb027e830..4960324ab74 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/boilerplate.qrc +++ b/src/plugins/qmlprojectmanager/cmakegen/boilerplate.qrc @@ -12,15 +12,4 @@ templates/qtquickcontrols2_conf.tpl templates/cmakelists_txt_shared.tpl - - - qmlprojectmaincpp.tpl - qmlprojectmaincppheader.tpl - qmlprojectenvheader.tpl - qmlprojectmodules.tpl - qmlprojectmaincmakelists.tpl - qmlprojectmodulecmakelists.tpl - qmlprojectmainqml.tpl - qmlprojectappmainqml.tpl - diff --git a/src/plugins/qmlprojectmanager/cmakegen/checkablefiletreeitem.cpp b/src/plugins/qmlprojectmanager/cmakegen/checkablefiletreeitem.cpp deleted file mode 100644 index 9eb45d91500..00000000000 --- a/src/plugins/qmlprojectmanager/cmakegen/checkablefiletreeitem.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "checkablefiletreeitem.h" - -using namespace Utils; - -namespace QmlProjectManager { - -CheckableFileTreeItem::CheckableFileTreeItem(const FilePath &filePath) - :QStandardItem(filePath.toString()) -{ - Qt::ItemFlags itemFlags = flags(); - if (!isDir()) - itemFlags |= Qt::ItemIsUserCheckable; - itemFlags &= ~(Qt::ItemIsEditable | Qt::ItemIsSelectable); - setFlags(itemFlags); -} - -const FilePath CheckableFileTreeItem::toFilePath() const -{ - return FilePath::fromString(text()); -} - -bool CheckableFileTreeItem::isFile() const -{ - return FilePath::fromString(text()).isFile(); -} - -bool CheckableFileTreeItem::isDir() const -{ - return FilePath::fromString(text()).isDir(); -} - -void CheckableFileTreeItem::setChecked(bool checked) -{ - this->checked = checked; -} - -bool CheckableFileTreeItem::isChecked() const -{ - return this->checked; -} - -} //QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/cmakegen/checkablefiletreeitem.h b/src/plugins/qmlprojectmanager/cmakegen/checkablefiletreeitem.h deleted file mode 100644 index c3c5145301f..00000000000 --- a/src/plugins/qmlprojectmanager/cmakegen/checkablefiletreeitem.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#ifndef CHECKABLEFILETREEITEM_H -#define CHECKABLEFILETREEITEM_H - -#include - -#include - -namespace QmlProjectManager { - -class CheckableFileTreeItem : public QStandardItem -{ -public: - explicit CheckableFileTreeItem(const Utils::FilePath &text = Utils::FilePath()); - - const Utils::FilePath toFilePath() const; - bool isFile() const; - bool isDir() const; - - bool isChecked() const; - void setChecked(bool checked); - -private: - bool checked; -}; - -} //QmlProjectManager - -#endif // CHECKABLEFILETREEITEM_H diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp index 6a3f4afda03..8e77cde4f76 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp +++ b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp @@ -208,6 +208,12 @@ bool CMakeGenerator::isResource(const Utils::FilePath &path) const return suffixes.contains(path.suffix(), Qt::CaseInsensitive); } +bool CMakeGenerator::ignoreFile(const Utils::FilePath &path) const +{ + static const QStringList suffixes = { "hints" }; + return suffixes.contains(path.suffix(), Qt::CaseInsensitive); +} + void CMakeGenerator::createCMakeFiles(const NodePtr &node) const { QTC_ASSERT(m_writer, return); @@ -481,7 +487,7 @@ void CMakeGenerator::compareWithFileSystem(const NodePtr &node) const while (iter.hasNext()) { auto next = Utils::FilePath::fromString(iter.next()); - if (isResource(next) && !findFile(next)) + if (isResource(next) && !findFile(next) && !ignoreFile(next)) files.push_back(next); } diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h index fd7b7dfd5be..6077166d5b6 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h +++ b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h @@ -47,6 +47,7 @@ public: private: bool isQml(const Utils::FilePath &path) const; bool isResource(const Utils::FilePath &path) const; + bool ignoreFile(const Utils::FilePath &path) const; void createCMakeFiles(const NodePtr &node) const; void createSourceFiles() const; diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialog.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialog.cpp deleted file mode 100644 index aa733b68af4..00000000000 --- a/src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialog.cpp +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "cmakegeneratordialog.h" -#include "../qmlprojectmanagertr.h" -#include "cmakegeneratordialogtreemodel.h" - -#include -#include - -#include -#include -#include -#include - -#include - -using namespace Utils; - -namespace QmlProjectManager { -namespace GenerateCmake { - -CmakeGeneratorDialog::CmakeGeneratorDialog(const FilePath &rootDir, - const FilePaths &files, - const FilePaths invalidFiles) - : QDialog(), m_rootDir(rootDir), m_files(files), m_invalidFiles(invalidFiles) -{ - setWindowTitle(Tr::tr("Select Files to Generate")); - - QLabel *mainLabel = new QLabel(Tr::tr("Start CMakeFiles.txt generation"), this); - mainLabel->setMargin(30); - - QVBoxLayout *dialogLayout = new QVBoxLayout(this); - dialogLayout->addWidget(mainLabel); - dialogLayout->addWidget(createDetailsWidget()); - dialogLayout->addWidget(createButtons()); - setLayout(dialogLayout); - - setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - setMaximumHeight(layout()->totalSizeHint().height()); - - refreshNotificationText(); -} - -QTreeView* CmakeGeneratorDialog::createFileTree() -{ - m_model = new CMakeGeneratorDialogTreeModel(m_rootDir, m_files, this); - - QTreeView *tree = new QTreeView(this); - tree->setModel(m_model); - tree->expandAll(); - tree->setHeaderHidden(true); - - return tree; -} - -QWidget* CmakeGeneratorDialog::createDetailsWidget() -{ - QTreeView* tree = createFileTree(); - - m_notifications = new QTextEdit(this); - m_warningIcon = Utils::Icons::WARNING.pixmap(); - - QSplitter *advancedInnerWidget = new QSplitter(this); - advancedInnerWidget->addWidget(tree); - advancedInnerWidget->addWidget(m_notifications); - advancedInnerWidget->setStretchFactor(0, 2); - advancedInnerWidget->setStretchFactor(1, 1); - advancedInnerWidget->setOrientation(Qt::Vertical); - advancedInnerWidget->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::MinimumExpanding); - - DetailsWidget *advancedWidget = new DetailsWidget(this); - advancedWidget->setMinimumWidth(600); - advancedWidget->setWidget(advancedInnerWidget); - advancedWidget->setSummaryText(Tr::tr("Advanced Options")); - connect(advancedWidget, &DetailsWidget::expanded, this, &CmakeGeneratorDialog::advancedVisibilityChanged); - - return advancedWidget; -} - -QWidget* CmakeGeneratorDialog::createButtons() -{ - QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); - auto *okButton = buttons->button(QDialogButtonBox::Ok); - okButton->setDefault(true); - - connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); - connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); - connect(m_model, &CMakeGeneratorDialogTreeModel::checkedStateChanged, this, &CmakeGeneratorDialog::refreshNotificationText); - - return buttons; -} - -FilePaths CmakeGeneratorDialog::getFilePaths() -{ - FilePaths paths; - - QList items = m_model->checkedItems(); - for (CheckableFileTreeItem *item: items) { - paths.append(FilePath::fromString(item->text())); - } - - return paths; -} - -const QString FILE_CREATE_NOTIFICATION = Tr::tr("File %1 will be created.\n"); -const QString FILE_OVERWRITE_NOTIFICATION = Tr::tr("File %1 will be overwritten.\n"); -const QString FILE_INVALID_NOTIFICATION = Tr::tr( - "File %1 contains invalid characters and will be skipped.\n"); - -void CmakeGeneratorDialog::refreshNotificationText() -{ - QTextDocument *document = m_notifications->document(); - document->clear(); - document->addResource(QTextDocument::ImageResource, QUrl("cmakegendialog://warningicon"), m_warningIcon); - - QTextCursor cursor = m_notifications->textCursor(); - QTextImageFormat iformat; - iformat.setName("cmakegendialog://warningicon"); - - QList nodes = m_model->items(); - - for (const auto &file : m_invalidFiles) { - cursor.insertImage(iformat); - cursor.insertText(QString(FILE_INVALID_NOTIFICATION).arg(file.displayName())); - } - - for (CheckableFileTreeItem *node : nodes) { - if (!m_files.contains(node->toFilePath())) - continue; - - if (!node->toFilePath().exists() && node->isChecked()) { - QString relativePath = QString(node->toFilePath().toString()).remove(m_rootDir.toString()+'/'); - cursor.insertText(QString(FILE_CREATE_NOTIFICATION).arg(relativePath)); - } - } - - if (!document->toPlainText().isEmpty()) - cursor.insertBlock(); - - for (CheckableFileTreeItem *node : nodes) { - if (!m_files.contains(node->toFilePath())) - continue; - - if (node->toFilePath().exists() && node->isChecked()) { - QString relativePath = node->toFilePath().relativePathFrom(m_rootDir).toString(); - cursor.insertImage(iformat); - cursor.insertText(QString(FILE_OVERWRITE_NOTIFICATION).arg(relativePath)); - } - } -} - -void CmakeGeneratorDialog::advancedVisibilityChanged(bool visible) -{ - if (visible) { - setMaximumHeight(QWIDGETSIZE_MAX); - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - } - else { - setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - int height = layout()->totalSizeHint().height(); - setMaximumHeight(height); - resize(width(), height); - } -} - -} //GenerateCmake -} //QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialog.h b/src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialog.h deleted file mode 100644 index fb15ec4de7c..00000000000 --- a/src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialog.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - - -#ifndef CMAKEGENERATORDIALOG_H -#define CMAKEGENERATORDIALOG_H - -#include "cmakegeneratordialogtreemodel.h" - -#include - -#include -#include -#include -#include - -namespace QmlProjectManager { -namespace GenerateCmake { - -class CmakeGeneratorDialog : public QDialog -{ - Q_OBJECT - -public: - CmakeGeneratorDialog(const Utils::FilePath &rootDir, - const Utils::FilePaths &files, - const Utils::FilePaths invalidFiles); - Utils::FilePaths getFilePaths(); - -public slots: - void refreshNotificationText(); - void advancedVisibilityChanged(bool visible); - -private: - QTreeView* createFileTree(); - QWidget* createDetailsWidget(); - QWidget* createButtons(); - -private: - CMakeGeneratorDialogTreeModel *m_model; - QTextEdit *m_notifications; - QVariant m_warningIcon; - Utils::FilePath m_rootDir; - Utils::FilePaths m_files; - Utils::FilePaths m_invalidFiles; -}; - -} //GenerateCmake -} //QmlProjectManager - -#endif // CMAKEGENERATORDIALOG_H diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialogtreemodel.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialogtreemodel.cpp deleted file mode 100644 index 525230bdfdd..00000000000 --- a/src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialogtreemodel.cpp +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "cmakegeneratordialogtreemodel.h" -#include "generatecmakelistsconstants.h" -#include "checkablefiletreeitem.h" -#include "../qmlprojectmanagertr.h" - -#include - -using namespace Utils; - -namespace QmlProjectManager { -namespace GenerateCmake { - -CMakeGeneratorDialogTreeModel::CMakeGeneratorDialogTreeModel(const FilePath &rootDir, - const FilePaths &files, QObject *parent) - :QStandardItemModel(parent), - rootDir(rootDir), - m_icons(new QFileIconProvider()) -{ - createNodes(files, invisibleRootItem()); -} - -CMakeGeneratorDialogTreeModel::~CMakeGeneratorDialogTreeModel() -{ - delete m_icons; -} - -QVariant CMakeGeneratorDialogTreeModel::data(const QModelIndex &index, int role) const -{ - if (index.isValid()) { - const CheckableFileTreeItem *node = constNodeForIndex(index); - if (role == Qt::CheckStateRole) { - if (!node->isDir()) - return node->isChecked() ? Qt::Checked : Qt::Unchecked; - return {}; - } - else if (role == Qt::DisplayRole) { - FilePath fullPath = node->toFilePath(); - return QVariant(fullPath.fileName()); - } - else if (role == Qt::DecorationRole) { - if (node->isFile()) - return Utils::Icons::WARNING.icon(); - if (node->isDir()) - return m_icons->icon(QFileIconProvider::Folder); - else - return Utils::Icons::NEWFILE.icon(); - } - else if (role == Qt::ToolTipRole) { - if (node->isFile()) - return Tr::tr("This file already exists and will be overwritten."); - if (!node->toFilePath().exists()) - return Tr::tr("This file or folder will be created."); - } - } - - return QStandardItemModel::data(index, role); -} - -bool CMakeGeneratorDialogTreeModel::setData(const QModelIndex &index, const QVariant &value, int role) -{ - if (index.isValid()) { - CheckableFileTreeItem *node = nodeForIndex(index); - if (role == Qt::CheckStateRole) { - node->setChecked(value.value()); - emit checkedStateChanged(node); - return true; - } - } - - return QStandardItemModel::setData(index, value, role);; -} - -const QList CMakeGeneratorDialogTreeModel::items() const -{ - QList standardItems = findItems(".*", Qt::MatchRegularExpression | Qt::MatchRecursive); - QList checkableItems; - for (QStandardItem *item : standardItems) - checkableItems.append(static_cast(item)); - - return checkableItems; -} - -const QList CMakeGeneratorDialogTreeModel::checkedItems() const -{ - QList allItems = items(); - - QList checkedItems; - for (CheckableFileTreeItem *item : allItems) { - if (item->isChecked()) - checkedItems.append(item); - } - - return checkedItems; -} - -bool CMakeGeneratorDialogTreeModel::checkedByDefault(const Utils::FilePath &file) const -{ - if (file.exists()) { - QString relativePath = file.relativeChildPath(rootDir).toString(); - if (relativePath.compare(QmlProjectManager::GenerateCmake::Constants::FILENAME_CMAKELISTS) == 0) - return false; - if (relativePath.endsWith(QmlProjectManager::GenerateCmake::Constants::FILENAME_CMAKELISTS) - && relativePath.length() > QString(QmlProjectManager::GenerateCmake::Constants::FILENAME_CMAKELISTS).length()) - return true; - if (relativePath.compare(QmlProjectManager::GenerateCmake::Constants::FILENAME_MODULES) == 0) - return true; - if (relativePath.compare( - FilePath::fromString(QmlProjectManager::GenerateCmake::Constants::DIRNAME_CPP) - .pathAppended(QmlProjectManager::GenerateCmake::Constants::FILENAME_MAINCPP_HEADER) - .toString()) - == 0) - return true; - } - - return !file.exists(); -} - -void CMakeGeneratorDialogTreeModel::createNodes(const FilePaths &candidates, QStandardItem *parent) -{ - if (!parent) - return; - - CheckableFileTreeItem *checkParent = dynamic_cast(parent); - FilePath thisDir = (parent == invisibleRootItem()) ? rootDir : checkParent->toFilePath(); - - for (const FilePath &file : candidates) { - if (file.parentDir() == thisDir) { - CheckableFileTreeItem *fileNode = new CheckableFileTreeItem(file); - fileNode->setChecked(checkedByDefault(file)); - if (!file.exists()) - fileNode->setChecked(true); - parent->appendRow(fileNode); - } - } - - FilePaths directSubDirs; - for (const FilePath &file : candidates) { - FilePath dir = file.parentDir(); - if (dir.parentDir() == thisDir && !directSubDirs.contains(dir)) - directSubDirs.append(dir); - } - - for (const FilePath &subDir : directSubDirs) { - CheckableFileTreeItem *dirNode = new CheckableFileTreeItem(subDir); - parent->appendRow(dirNode); - - FilePaths subDirCandidates; - for (const FilePath &file : candidates) - if (file.isChildOf(subDir)) - subDirCandidates.append(file); - - createNodes(subDirCandidates, dirNode); - } -} - -const CheckableFileTreeItem* CMakeGeneratorDialogTreeModel::constNodeForIndex(const QModelIndex &index) const -{ - const QStandardItem *parent = static_cast(index.internalPointer()); - const QStandardItem *item = parent->child(index.row(), index.column()); - return static_cast(item); -} - -CheckableFileTreeItem* CMakeGeneratorDialogTreeModel::nodeForIndex(const QModelIndex &index) -{ - QStandardItem *parent = static_cast(index.internalPointer()); - QStandardItem *item = parent->child(index.row(), index.column()); - return static_cast(item); -} - -} //GenerateCmake -} //QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialogtreemodel.h b/src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialogtreemodel.h deleted file mode 100644 index 3fb79c2b966..00000000000 --- a/src/plugins/qmlprojectmanager/cmakegen/cmakegeneratordialogtreemodel.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#ifndef CMAKEGENERATORDIALOGTREEMODEL_H -#define CMAKEGENERATORDIALOGTREEMODEL_H - -#include "checkablefiletreeitem.h" - -#include -#include - -#include - -namespace QmlProjectManager { -namespace GenerateCmake { - -class CMakeGeneratorDialogTreeModel : public QStandardItemModel -{ - Q_OBJECT - -public: - CMakeGeneratorDialogTreeModel(const Utils::FilePath &rootDir, - const Utils::FilePaths &files, QObject *parent = nullptr); - ~CMakeGeneratorDialogTreeModel(); - - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; - bool setData(const QModelIndex &index, const QVariant &value, int role); - - const QList items() const; - const QList checkedItems() const; - const CheckableFileTreeItem* constNodeForIndex(const QModelIndex &index) const; - CheckableFileTreeItem* nodeForIndex(const QModelIndex &index); - -signals: - void checkedStateChanged(CheckableFileTreeItem *item); - -protected: - bool checkedByDefault(const Utils::FilePath &file) const; - Utils::FilePath rootDir; - -private: - void createNodes(const Utils::FilePaths &candidates, QStandardItem *parent); - - QFileIconProvider* m_icons; -}; - -} //GenerateCmake -} //QmlProjectManager - - -#endif // CMAKEGENERATORDIALOGTREEMODEL_H diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverter.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverter.cpp deleted file mode 100644 index f6bf1a49759..00000000000 --- a/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverter.cpp +++ /dev/null @@ -1,422 +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 - -#include "cmakeprojectconverter.h" -#include "cmakeprojectconverterdialog.h" -#include "generatecmakelists.h" -#include "generatecmakelistsconstants.h" -#include "../qmlprojectmanagertr.h" - -#include -#include -#include - -#include -#include -#include - -#include - -#include -#include -#include - -using namespace Utils; -using namespace QmlProjectManager::GenerateCmake::Constants; - -namespace QmlProjectManager { -namespace GenerateCmake { - -const QString MENU_ITEM_CONVERT = Tr::tr("Export as Latest Project Format..."); -const QString ERROR_TITLE = Tr::tr("Creating Project"); -const QString SUCCESS_TITLE = Tr::tr("Creating Project"); -const QString ERROR_TEXT = Tr::tr("Creating project failed.\n%1"); -const QString SUCCESS_TEXT = Tr::tr("Creating project succeeded."); - -void CmakeProjectConverter::generateMenuEntry(QObject *parent) -{ - Core::ActionContainer *exportMenu = Core::ActionManager::actionContainer( - QmlProjectManager::Constants::EXPORT_MENU); - auto action = new QAction(MENU_ITEM_CONVERT, parent); - QObject::connect(action, &QAction::triggered, CmakeProjectConverter::onConvertProject); - Core::Command *cmd = Core::ActionManager::registerAction(action, "QmlProject.ConvertToCmakeProject"); - exportMenu->addAction(cmd, QmlProjectManager::Constants::G_EXPORT_CONVERT); - - action->setEnabled(isProjectConvertable(ProjectExplorer::ProjectManager::startupProject())); - QObject::connect(ProjectExplorer::ProjectManager::instance(), - &ProjectExplorer::ProjectManager::startupProjectChanged, - [action]() { - auto currentBuildSystem = QmlBuildSystem::getStartupBuildSystem(); - bool isMCU = currentBuildSystem ? currentBuildSystem->qtForMCUs() : false; - - action->setEnabled(isMCU - && isProjectConvertable( - ProjectExplorer::ProjectManager::startupProject())); - }); -} - -bool CmakeProjectConverter::isProjectConvertable(const ProjectExplorer::Project *project) -{ - if (!project) - return false; - - return !isProjectCurrentFormat(project); -} - -const QStringList sanityCheckFiles({FILENAME_CMAKELISTS, - FILENAME_MODULES, - FILENAME_MAINQML, - QString(DIRNAME_CONTENT)+'/'+FILENAME_CMAKELISTS, - QString(DIRNAME_IMPORT)+'/'+FILENAME_CMAKELISTS, - QString(DIRNAME_CPP)+'/'+FILENAME_MAINCPP, - QString(DIRNAME_CPP)+'/'+FILENAME_ENV_HEADER, - QString(DIRNAME_CPP)+'/'+FILENAME_MAINCPP_HEADER - }); - -bool CmakeProjectConverter::isProjectCurrentFormat(const ProjectExplorer::Project *project) -{ - const QmlProjectManager::QmlProject *qmlprj = qobject_cast(project); - - if (!qmlprj) - return false; - - FilePath rootDir = qmlprj->rootProjectDirectory(); - for (const QString &file : sanityCheckFiles) - if (!rootDir.pathAppended(file).exists()) - return false; - - return true; -} - -void CmakeProjectConverter::onConvertProject() -{ - ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject(); - const QmlProjectManager::QmlProject *qmlProject = - qobject_cast(project); - if (qmlProject) { - CmakeProjectConverterDialog dialog(qmlProject); - if (dialog.exec()) { - FilePath newProjectPath = dialog.newPath(); - CmakeProjectConverter converter; - converter.convertProject(qmlProject, newProjectPath); - } - } -} - -bool CmakeProjectConverter::convertProject(const QmlProjectManager::QmlProject *project, - const FilePath &targetDir) -{ - m_converterObjects.clear(); - m_projectDir = project->projectDirectory(); - m_newProjectDir = targetDir; - m_project = project; - - m_rootDirFiles = QStringList(FILENAME_FILTER_QMLPROJECT); - const QString confFile = GenerateCmake::projectEnvironmentVariable(ENV_VARIABLE_CONTROLCONF); - if (!confFile.isEmpty()) - m_rootDirFiles.append(confFile); - - bool retVal = prepareAndExecute(); - - if (retVal) { - QMessageBox::information(Core::ICore::dialogParent(), SUCCESS_TITLE, SUCCESS_TEXT); - ProjectExplorer::OpenProjectResult result - = ProjectExplorer::ProjectExplorerPlugin::openProject(newProjectFile()); - if (!result) - ProjectExplorer::ProjectExplorerPlugin::showOpenProjectError(result); - } - else { - QMessageBox::critical(Core::ICore::dialogParent(), ERROR_TITLE, ERROR_TEXT.arg(m_errorText)); - } - - return retVal; -} - -bool CmakeProjectConverter::prepareAndExecute() -{ - GenerateCmake::CmakeFileGenerator cmakeGenerator; - - if (!performSanityCheck()) - return false; - if (!prepareBaseDirectoryStructure()) - return false; - if (!prepareCopy()) - return false; - if (!createPreparedProject()) - return false; - if (!cmakeGenerator.prepare(m_newProjectDir, false)) - return false; - if (!cmakeGenerator.execute()) - return false; - if (!modifyNewFiles()) - return false; - - return true; -} - -bool CmakeProjectConverter::isFileBlacklisted(const Utils::FilePath &file) const -{ - if (!file.fileName().compare(FILENAME_CMAKELISTS)) - return true; - if (!file.suffix().compare(FILENAME_SUFFIX_QMLPROJECT)) - return true; - if (!file.suffix().compare(FILENAME_SUFFIX_USER)) - return true; - if (m_rootDirFiles.contains(file.fileName())) - return true; - - return false; -} - -bool CmakeProjectConverter::isDirBlacklisted(const Utils::FilePath &dir) const -{ - if (!dir.isDir()) - return true; - - return false; -} - -const QString ERROR_CANNOT_WRITE_DIR = Tr::tr("Unable to write to directory\n%1."); - -bool CmakeProjectConverter::performSanityCheck() -{ - if (!m_newProjectDir.parentDir().isWritableDir()) { - m_errorText = ERROR_CANNOT_WRITE_DIR.arg(m_newProjectDir.parentDir().toString()); - return false; - } - - return true; -} - -bool CmakeProjectConverter::prepareBaseDirectoryStructure() -{ - addDirectory(m_newProjectDir); - addDirectory(contentDir()); - addDirectory(sourceDir()); - addDirectory(importDir()); - addDirectory(assetDir()); - addDirectory(assetImportDir()); - addFile(contentDir().pathAppended(FILENAME_APPMAINQML)); - - return true; -} - -bool CmakeProjectConverter::prepareCopy() -{ - FilePaths rootFiles = m_projectDir.dirEntries({m_rootDirFiles, QDir::Files}); - for (const FilePath &file : rootFiles) { - addFile(file, m_newProjectDir.pathAppended(file.fileName())); - } - - prepareCopyDirFiles(m_projectDir, contentDir()); - - FilePaths subDirs = m_projectDir.dirEntries(QDir::Dirs|QDir::NoDotAndDotDot); - for (FilePath &subDir : subDirs) { - if (subDir.fileName() == DIRNAME_IMPORT) { - prepareCopyDirTree(subDir, importDir()); - } - else if (subDir.fileName() == DIRNAME_CPP) { - prepareCopyDirTree(subDir, sourceDir()); - } - else if (subDir.fileName() == DIRNAME_ASSET) { - prepareCopyDirTree(subDir, assetDir()); - } - else if (subDir.fileName() == DIRNAME_ASSETIMPORT) { - prepareCopyDirTree(subDir, assetImportDir()); - } - else { - prepareCopyDirTree(subDir, contentDir().pathAppended(subDir.fileName())); - } - } - - return true; -} - -bool CmakeProjectConverter::prepareCopyDirFiles(const FilePath &dir, const FilePath &targetDir) -{ - FilePaths dirFiles = dir.dirEntries(QDir::Files); - for (FilePath file : dirFiles) { - if (!isFileBlacklisted(file)) - addFile(file, targetDir.pathAppended(file.fileName())); - } - - return true; -} - -bool CmakeProjectConverter::prepareCopyDirTree(const FilePath &dir, const FilePath &targetDir) -{ - prepareCopyDirFiles(dir, targetDir); - FilePaths subDirs = dir.dirEntries(QDir::Dirs|QDir::NoDotAndDotDot); - for (FilePath &subDir : subDirs) { - if (isDirBlacklisted(subDir)) - continue; - addDirectory(targetDir.pathAppended(subDir.fileName())); - prepareCopyDirFiles(subDir, targetDir.pathAppended(subDir.fileName())); - prepareCopyDirTree(subDir, targetDir.pathAppended(subDir.fileName())); - } - - return true; -} - -bool CmakeProjectConverter::addDirectory(const Utils::FilePath &target) -{ - return addObject(ProjectConverterObjectType::Directory, FilePath(), target); -} - -bool CmakeProjectConverter::addFile(const Utils::FilePath &target) -{ - return addFile(FilePath(), target); -} - -bool CmakeProjectConverter::addFile(const Utils::FilePath &original, const Utils::FilePath &target) -{ - addDirectory(target.parentDir()); - return addObject(ProjectConverterObjectType::File, original, target); -} - -bool CmakeProjectConverter::addObject(ProjectConverterObjectType type, - const Utils::FilePath &original, const Utils::FilePath &target) -{ - if (target.isChildOf(m_projectDir)) - return false; - - if (!target.isChildOf(m_newProjectDir) && - ((type == ProjectConverterObjectType::Directory) && (target != m_newProjectDir))) { - return false; - } - - for (ProjectConverterObject &o : m_converterObjects) { - if (o.target == target) - return false; - } - - ProjectConverterObject object; - object.type = type; - object.target = target; - object.original = original; - - m_converterObjects.append(object); - - return true; -} - -bool CmakeProjectConverter::createPreparedProject() -{ - for (ProjectConverterObject &pco : m_converterObjects) { - if (pco.type == ProjectConverterObjectType::Directory) { - pco.target.createDir(); - } - else if (pco.type == ProjectConverterObjectType::File) { - if (pco.original.isEmpty()) { - QFile newFile(pco.target.toString()); - newFile.open(QIODevice::WriteOnly); - newFile.close(); - } - else { - pco.original.copyFile(pco.target); - } - } - } - - return true; -} - -const FilePath CmakeProjectConverter::contentDir() const -{ - return m_newProjectDir.pathAppended(DIRNAME_CONTENT); -} - -const FilePath CmakeProjectConverter::sourceDir() const -{ - return m_newProjectDir.pathAppended(DIRNAME_CPP); -} - -const FilePath CmakeProjectConverter::importDir() const -{ - return m_newProjectDir.pathAppended(DIRNAME_IMPORT); -} - -const FilePath CmakeProjectConverter::assetDir() const -{ - return contentDir().pathAppended(DIRNAME_ASSET); -} - -const FilePath CmakeProjectConverter::assetImportDir() const -{ - return m_newProjectDir.pathAppended(DIRNAME_ASSETIMPORT); -} - -const FilePath CmakeProjectConverter::newProjectFile() const -{ - return m_newProjectDir.pathAppended(m_project->projectFilePath().fileName()); -} - -const FilePath CmakeProjectConverter::projectMainFile() const -{ - auto *target = m_project->activeTarget(); - if (target && target->buildSystem()) { - auto buildSystem = qobject_cast(target->buildSystem()); - if (buildSystem) { - return buildSystem->mainFilePath(); - } - } - return {}; -} - -const QString CmakeProjectConverter::projectMainClass() const -{ - return projectMainFile().baseName(); -} - -bool CmakeProjectConverter::modifyNewFiles() -{ - return modifyAppMainQml() && modifyProjectFile(); -} - -const char APPMAIN_QMLFILE_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectappmainqml.tpl"; - -bool CmakeProjectConverter::modifyAppMainQml() -{ - QString appMainQmlPath = contentDir().pathAppended(FILENAME_APPMAINQML).toString(); - QFile appMainQml(appMainQmlPath); - appMainQml.open(QIODevice::ReadWrite); - if (!appMainQml.isOpen()) - return false; - - QString templateContent = GenerateCmake::readTemplate(APPMAIN_QMLFILE_TEMPLATE_PATH); - QString appMainQmlContent = templateContent.arg(projectMainClass()); - - appMainQml.reset(); - appMainQml.write(appMainQmlContent.toUtf8()); - appMainQml.close(); - - return true; -} - -bool CmakeProjectConverter::modifyProjectFile() -{ - QString projectFileName = m_project->projectFilePath().fileName(); - FilePath projectFilePath = m_newProjectDir.pathAppended(projectFileName); - QFile projectFile(projectFilePath.toString()); - projectFile.open(QIODevice::ReadOnly); - if (!projectFile.isOpen()) - return false; - QString projectFileContent = QString::fromUtf8(projectFile.readAll()); - projectFile.close(); - - const QRegularExpression mainFilePattern("^\\s*mainFile:\\s*\".*\"", QRegularExpression::MultilineOption); - const QString mainFileString(" mainFile: \"content/App.qml\""); - - projectFileContent.replace(mainFilePattern, mainFileString); - - projectFile.open(QIODevice::WriteOnly|QIODevice::Truncate); - if (!projectFile.isOpen()) - return false; - projectFile.write(projectFileContent.toUtf8()); - projectFile.close(); - - return true; -} - -} //GenerateCmake -} //QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverter.h b/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverter.h deleted file mode 100644 index 415123db70d..00000000000 --- a/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverter.h +++ /dev/null @@ -1,77 +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 - -#ifndef CMAKEPROJECTCONVERTER_H -#define CMAKEPROJECTCONVERTER_H - -#include -#include - -namespace QmlProjectManager { - -namespace GenerateCmake { - -enum ProjectConverterObjectType { - File, - Directory -}; - -struct ProjectConverterObject { - ProjectConverterObjectType type; - Utils::FilePath target; - Utils::FilePath original; -}; - -class CmakeProjectConverter { - -public: - bool convertProject(const QmlProjectManager::QmlProject *project, - const Utils::FilePath &targetDir); - static void generateMenuEntry(QObject *parent); - static void onConvertProject(); - static bool isProjectConvertable(const ProjectExplorer::Project *project); - static bool isProjectCurrentFormat(const ProjectExplorer::Project *project); - -private: - bool prepareAndExecute(); - bool isFileBlacklisted(const Utils::FilePath &file) const; - bool isDirBlacklisted(const Utils::FilePath &dir) const; - bool performSanityCheck(); - bool prepareBaseDirectoryStructure(); - bool prepareCopyDirFiles(const Utils::FilePath &dir, const Utils::FilePath &targetDir); - bool prepareCopyDirTree(const Utils::FilePath &dir, const Utils::FilePath &targetDir); - bool prepareCopy(); - bool addDirectory(const Utils::FilePath &target); - bool addFile(const Utils::FilePath &target); - bool addFile(const Utils::FilePath &original, const Utils::FilePath &target); - bool addObject(ProjectConverterObjectType type, - const Utils::FilePath &original, const Utils::FilePath &target); - bool createPreparedProject(); - - const Utils::FilePath contentDir() const; - const Utils::FilePath sourceDir() const; - const Utils::FilePath importDir() const; - const Utils::FilePath assetDir() const; - const Utils::FilePath assetImportDir() const; - const Utils::FilePath newProjectFile() const; - - const QString environmentVariable(const QString &key) const; - const Utils::FilePath projectMainFile() const; - const QString projectMainClass() const; - bool modifyNewFiles(); - bool modifyAppMainQml(); - bool modifyProjectFile(); - -private: - QList m_converterObjects; - QStringList m_rootDirFiles; - Utils::FilePath m_projectDir; - Utils::FilePath m_newProjectDir; - const QmlProjectManager::QmlProject *m_project; - QString m_errorText; -}; - -} //GenerateCmake -} //QmlProjectManager - -#endif // CMAKEPROJECTCONVERTER_H diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverterdialog.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverterdialog.cpp deleted file mode 100644 index 1d67a92b394..00000000000 --- a/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverterdialog.cpp +++ /dev/null @@ -1,180 +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 - -#include "cmakeprojectconverterdialog.h" -#include "../qmlprojectmanagertr.h" - -#include - -#include -#include -#include -#include -#include -#include -#include - -using namespace Utils; - -namespace QmlProjectManager { -namespace GenerateCmake { - -const QRegularExpression projectNameRegexp("^(?!(import))(?!(QtQml))(?!(QtQuick))(?:[A-Z][a-zA-Z0-9-_]*)$"); - -static bool projectNameValidationFunction(FancyLineEdit *editor, QString *) -{ - return editor->text().count(projectNameRegexp); -} - -static bool dirValidationFunction(FancyLineEdit *editor, QString *) -{ - return FilePath::fromString(editor->text()).isWritableDir(); -} - -const QString EXPLANATION_TEXT = Tr::tr("This process creates a copy of the existing project. The new project's folder structure is adjusted for CMake build process and necessary related new files are generated.\n\nThe new project can be opened in Qt Creator using the main CMakeLists.txt file."); -const QString PROJECT_NAME_LABEL = Tr::tr("Name:"); -const QString PARENT_DIR_LABEL = Tr::tr("Create in:"); - -CmakeProjectConverterDialog::CmakeProjectConverterDialog(const QmlProjectManager::QmlProject *oldProject) - : QDialog() -{ - const FilePath defaultDir = Core::DocumentManager::projectsDirectory(); - const QString defaultName = uniqueProjectName(defaultDir, oldProject->displayName()); - - QLabel *mainLabel = new QLabel(EXPLANATION_TEXT, this); - mainLabel->setWordWrap(true); - - mainLabel->setMargin(20); - mainLabel->setMinimumWidth(600); - - m_errorLabel = new InfoLabel(); - m_errorLabel->setType(InfoLabel::InfoType::None); - - m_nameEditor = new FancyLineEdit(); - m_nameEditor->setValidationFunction(projectNameValidationFunction); - m_nameEditor->setText(defaultName); - - m_dirSelector = new PathChooser(); - m_dirSelector->setExpectedKind(PathChooser::Directory); - m_dirSelector->setValidationFunction(dirValidationFunction); - m_dirSelector->setFilePath(defaultDir); - - QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); - m_okButton = buttons->button(QDialogButtonBox::Ok); - m_okButton->setDefault(true); - - connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); - connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); - connect(m_nameEditor, &FancyLineEdit::textChanged, this, &CmakeProjectConverterDialog::pathValidChanged); - connect(m_dirSelector->lineEdit(), &FancyLineEdit::textChanged, this, &CmakeProjectConverterDialog::pathValidChanged); - - QGroupBox *form = new QGroupBox(); - QFormLayout *formLayout = new QFormLayout(form); - formLayout->addRow(PROJECT_NAME_LABEL, m_nameEditor); - formLayout->addRow(PARENT_DIR_LABEL, m_dirSelector); - - QVBoxLayout *dialogLayout = new QVBoxLayout(this); - dialogLayout->addWidget(mainLabel); - dialogLayout->addWidget(form); - dialogLayout->addWidget(m_errorLabel); - dialogLayout->addWidget(buttons); - - pathValidChanged(); -} - -void CmakeProjectConverterDialog::pathValidChanged() -{ - bool valid = isValid(); - - if (valid) - m_newProjectDir = m_dirSelector->filePath().pathAppended(m_nameEditor->text()); - else - m_newProjectDir = FilePath(); - - const QString error = errorText(); - m_errorLabel->setType(error.isEmpty() ? InfoLabel::None : InfoLabel::Warning); - m_errorLabel->setText(error); - m_okButton->setEnabled(valid); -} - -const FilePath CmakeProjectConverterDialog::newPath() const -{ - return m_newProjectDir; -} - -const QStringList blackListedStarts = {"import","QtQml","QtQuick"}; - -const QString CmakeProjectConverterDialog::startsWithBlacklisted(const QString &text) const -{ - for (const QString &badWord : blackListedStarts) { - if (text.startsWith(badWord)) - return badWord; - } - - return {}; -} - -const QString ERROR_TEXT_NAME_EMPTY = Tr::tr("Name is empty."); -const QString ERROR_TEXT_NAME_BAD_START = Tr::tr("Name must not start with \"%1\"."); -const QString ERROR_TEXT_NAME_LOWERCASE_START = Tr::tr("Name must begin with a capital letter"); -const QString ERROR_TEXT_NAME_BAD_CHARACTERS = Tr::tr("Name must contain only letters, numbers or characters - _."); - -const QString ERROR_DIR_NOT_DIR = Tr::tr("Target is not a directory."); -const QString ERROR_DIR_NOT_WRITABLE = Tr::tr("Cannot write to target directory."); -const QString ERROR_DIR_EXISTS = Tr::tr("Project directory already exists."); - -const QString CmakeProjectConverterDialog::errorText() const -{ - QString text; - - if (!m_nameEditor->isValid()) { - QString name = m_nameEditor->text(); - - if (name.isEmpty()) - return ERROR_TEXT_NAME_EMPTY; - - const QString badStart = startsWithBlacklisted(text); - if (!badStart.isEmpty()) - return ERROR_TEXT_NAME_BAD_START.arg(badStart); - - if (name[0].isLower()) - return ERROR_TEXT_NAME_LOWERCASE_START; - - return ERROR_TEXT_NAME_BAD_CHARACTERS; - - } - - if (!m_dirSelector->isValid()) { - FilePath path = m_dirSelector->filePath(); - if (!path.isDir()) - return ERROR_DIR_NOT_DIR; - if (!path.isWritableDir()) - return ERROR_DIR_NOT_WRITABLE; - } - - if (m_dirSelector->filePath().pathAppended(m_nameEditor->text()).exists()) - return ERROR_DIR_EXISTS; - - return text; -} - -const QString CmakeProjectConverterDialog::uniqueProjectName(const FilePath &dir, const QString &oldName) const -{ - for (unsigned i = 0; ; ++i) { - QString name = oldName; - if (i) - name += QString::number(i); - if (!dir.pathAppended(name).exists()) - return name; - } - return oldName; -} - -bool CmakeProjectConverterDialog::isValid() -{ - FilePath newPath = m_dirSelector->filePath().pathAppended(m_nameEditor->text()); - return m_dirSelector->isValid() && m_nameEditor->isValid() && !newPath.exists(); -} - -} //GenerateCmake -} //QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverterdialog.h b/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverterdialog.h deleted file mode 100644 index 071eec481af..00000000000 --- a/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverterdialog.h +++ /dev/null @@ -1,47 +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 - - -#ifndef CMAKEPROJECTCONVERTERDIALOG_H -#define CMAKEPROJECTCONVERTERDIALOG_H - -#include -#include -#include -#include -#include - -#include - -namespace QmlProjectManager { -namespace GenerateCmake { - -class CmakeProjectConverterDialog : public QDialog -{ - Q_OBJECT - -public: - CmakeProjectConverterDialog(const QmlProjectManager::QmlProject *oldProject); - const Utils::FilePath newPath() const; - -public slots: - void pathValidChanged(); - -private: - const QString startsWithBlacklisted(const QString &text) const; - const QString errorText() const; - const QString uniqueProjectName(const Utils::FilePath &dir, const QString &oldName) const; - bool isValid(); - -private: - Utils::FilePath m_newProjectDir; - Utils::FancyLineEdit *m_nameEditor; - Utils::PathChooser *m_dirSelector; - Utils::InfoLabel *m_errorLabel; - QPushButton *m_okButton; -}; - -} //GenerateCmake -} //QmlProjectManager - -#endif // CMAKEPROJECTCONVERTERDIALOG_H diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakewriter.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakewriter.cpp index 80ea56dfb9d..0b1d0c4c60e 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/cmakewriter.cpp +++ b/src/plugins/qmlprojectmanager/cmakegen/cmakewriter.cpp @@ -4,7 +4,6 @@ #include "cmakegenerator.h" #include "cmakewriterv0.h" #include "cmakewriterv1.h" -#include "generatecmakelistsconstants.h" #include "qmlprojectmanager/qmlproject.h" #include "qmlprojectmanager/buildsystem/qmlbuildsystem.h" @@ -42,6 +41,16 @@ CMakeWriter::Ptr CMakeWriter::create(CMakeGenerator *parent) return std::make_unique(parent); } +QString CMakeWriter::readTemplate(const QString &templatePath) +{ + QFile templatefile(templatePath); + templatefile.open(QIODevice::ReadOnly); + QTextStream stream(&templatefile); + QString content = stream.readAll(); + templatefile.close(); + return content; +} + CMakeWriter::CMakeWriter(CMakeGenerator *parent) : m_parent(parent) {} @@ -60,7 +69,7 @@ bool CMakeWriter::isPlugin(const NodePtr &node) const QString CMakeWriter::sourceDirName() const { - return Constants::DIRNAME_CPP; + return "src"; } void CMakeWriter::transformNode(NodePtr &) const @@ -173,7 +182,7 @@ QString CMakeWriter::makeSetEnvironmentFn() const QTC_ASSERT(parent()->buildSystem(), return {}); const QmlBuildSystem *buildSystem = parent()->buildSystem(); - const QString configFile = getEnvironmentVariable(Constants::ENV_VARIABLE_CONTROLCONF); + const QString configFile = getEnvironmentVariable(ENV_VARIABLE_CONTROLCONF); QString out("inline void set_qt_environment() {\n"); for (Utils::EnvironmentItem &envItem : buildSystem->environment()) { @@ -223,16 +232,6 @@ std::tuple CMakeWriter::makeResourcesBlocks(const NodePtr &nod return {resourcesOut, bigResourcesOut}; } -QString CMakeWriter::readTemplate(const QString &templatePath) const -{ - QFile templatefile(templatePath); - templatefile.open(QIODevice::ReadOnly); - QTextStream stream(&templatefile); - QString content = stream.readAll(); - templatefile.close(); - return content; -} - void CMakeWriter::writeFile(const Utils::FilePath &path, const QString &content) const { QFile fileHandle(path.toString()); diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakewriter.h b/src/plugins/qmlprojectmanager/cmakegen/cmakewriter.h index 86f6403d130..8766df0dcda 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/cmakewriter.h +++ b/src/plugins/qmlprojectmanager/cmakegen/cmakewriter.h @@ -41,6 +41,9 @@ using FileGetter = std::function(const NodePtr &)>; class CMakeGenerator; +const char ENV_VARIABLE_CONTROLCONF[] = + "QT_QUICK_CONTROLS_CONF"; + const char DO_NOT_EDIT_FILE[] = "### This file is automatically generated by Qt Design Studio.\n" "### Do not change\n\n"; @@ -56,6 +59,7 @@ public: using Ptr = std::shared_ptr; static Ptr create(CMakeGenerator *parent); + static QString readTemplate(const QString &templatePath); CMakeWriter(CMakeGenerator *parent); const CMakeGenerator *parent() const; @@ -85,7 +89,6 @@ protected: QString makeSetEnvironmentFn() const; std::tuple makeResourcesBlocks(const NodePtr &node) const; - QString readTemplate(const QString &templatePath) const; void writeFile(const Utils::FilePath &path, const QString &content) const; private: diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv0.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv0.cpp index feac91f5873..5cb17d1e1bf 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv0.cpp +++ b/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv0.cpp @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "cmakewriterv0.h" #include "cmakegenerator.h" -#include "generatecmakelistsconstants.h" namespace QmlProjectManager { @@ -70,7 +69,7 @@ void CMakeWriterV0::writeRootCMakeFile(const NodePtr &node) const } const QString appName = parent()->projectName() + "App"; - const QString qtcontrolsConfFile = getEnvironmentVariable(Constants::ENV_VARIABLE_CONTROLCONF); + const QString qtcontrolsConfFile = getEnvironmentVariable(ENV_VARIABLE_CONTROLCONF); QString fileSection = ""; if (!qtcontrolsConfFile.isEmpty()) diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv1.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv1.cpp index 096ceb5d19e..220d8622bfe 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv1.cpp +++ b/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv1.cpp @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "cmakewriterv1.h" #include "cmakegenerator.h" -#include "generatecmakelistsconstants.h" #include "qmlprojectmanager/buildsystem/qmlbuildsystem.h" @@ -62,7 +61,7 @@ void CMakeWriterV1::writeRootCMakeFile(const NodePtr &node) const const QString appName = parent()->projectName() + "App"; QString fileSection = ""; - const QString configFile = getEnvironmentVariable(Constants::ENV_VARIABLE_CONTROLCONF); + const QString configFile = getEnvironmentVariable(ENV_VARIABLE_CONTROLCONF); if (!configFile.isEmpty()) fileSection = QString("\t\t%1").arg(configFile); diff --git a/src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.cpp b/src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.cpp deleted file mode 100644 index f46b508c8c4..00000000000 --- a/src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.cpp +++ /dev/null @@ -1,668 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "generatecmakelists.h" - -#include "generatecmakelistsconstants.h" -#include "cmakegeneratordialog.h" -#include "../qmlprojectmanagertr.h" - -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -using namespace Utils; -using namespace QmlProjectManager::GenerateCmake::Constants; - -namespace QmlProjectManager { - -namespace GenerateCmake { - -static bool isQmlDesigner(const ExtensionSystem::PluginSpec *spec) -{ - if (!spec) - return false; - - return spec->name().contains("QmlDesigner"); -} - -static void trackUsage(const QString &id) -{ - const auto plugins = ExtensionSystem::PluginManager::plugins(); - const auto it = std::find_if(plugins.begin(), plugins.end(), &isQmlDesigner); - if (it != plugins.end()) { - QObject *qmlDesignerPlugin = (*it)->plugin(); - QMetaObject::invokeMethod(qmlDesignerPlugin, - "usageStatisticsNotifier", - Qt::DirectConnection, - Q_ARG(QString, id)); - } -} - -bool operator==(const GeneratableFile &left, const GeneratableFile &right) -{ - return (left.filePath == right.filePath && left.content == right.content); -} - -enum ProjectDirectoryError { - NoError = 0, - MissingContentDir = 1<<1, - MissingImportDir = 1<<2, - MissingAssetDir = 1<<3, - MissingAssetImportDir = 1<<4, - MissingCppDir = 1<<5, - MissingMainCMake = 1<<6, - MissingMainQml = 1<<7, - MissingAppMainQml = 1<<8, - MissingQmlModules = 1<<9, - MissingMainCpp = 1<<10, - MissingMainCppHeader = 1<<11, - MissingEnvHeader = 1<<12 -}; - -const QString MENU_ITEM_GENERATE = Tr::tr("Generate CMake Build Files..."); - -void generateMenuEntry(QObject *parent) -{ - Core::ActionContainer *menu = Core::ActionManager::actionContainer(Core::Constants::M_FILE); - - Core::ActionContainer *exportMenu = Core::ActionManager::createMenu( - QmlProjectManager::Constants::EXPORT_MENU); - - exportMenu->menu()->setTitle(Tr::tr("Export Project")); - menu->addMenu(exportMenu, Core::Constants::G_FILE_EXPORT); - - exportMenu->appendGroup(QmlProjectManager::Constants::G_EXPORT_GENERATE); - exportMenu->appendGroup(QmlProjectManager::Constants::G_EXPORT_CONVERT); - exportMenu->addSeparator(QmlProjectManager::Constants::G_EXPORT_CONVERT); - - auto action = new QAction(MENU_ITEM_GENERATE, parent); - QObject::connect(action, &QAction::triggered, GenerateCmake::onGenerateCmakeLists); - Core::Command *cmd = Core::ActionManager::registerAction(action, "QmlProject.CreateCMakeLists"); - exportMenu->addAction(cmd, QmlProjectManager::Constants::G_EXPORT_GENERATE); - - action->setEnabled(false); - QObject::connect(ProjectExplorer::ProjectManager::instance(), - &ProjectExplorer::ProjectManager::startupProjectChanged, - [action]() { - if (auto buildSystem = QmlBuildSystem::getStartupBuildSystem()) - action->setEnabled(!buildSystem->qtForMCUs()); - }); -} - -void onGenerateCmakeLists() -{ - trackUsage("generateCMakeProjectDialogOpened"); - FilePath rootDir = ProjectExplorer::ProjectManager::startupProject()->projectDirectory(); - - int projectDirErrors = isProjectCorrectlyFormed(rootDir); - if (projectDirErrors != NoError) { - showProjectDirErrorDialog(projectDirErrors); - if (isErrorFatal(projectDirErrors)) - return; - } - - CmakeFileGenerator cmakeGen; - cmakeGen.prepare(rootDir); - - FilePaths allFiles; - for (const GeneratableFile &file: cmakeGen.fileQueue().queuedFiles()) - allFiles.append(file.filePath); - - CmakeGeneratorDialog dialog(rootDir, allFiles, cmakeGen.invalidFileNames()); - if (dialog.exec()) { - FilePaths confirmedFiles = dialog.getFilePaths(); - cmakeGen.filterFileQueue(confirmedFiles); - cmakeGen.execute(); - } - - trackUsage("generateCMakeProjectExecuted"); -} - -bool isErrorFatal(int error) -{ - if (error & MissingContentDir || - error & MissingImportDir || - error & MissingCppDir || - error & MissingAppMainQml) - return true; - - return false; -} - -int isProjectCorrectlyFormed(const FilePath &rootDir) -{ - int errors = NoError; - - if (!rootDir.pathAppended(DIRNAME_CONTENT).exists()) - errors |= MissingContentDir; - if (!rootDir.pathAppended(DIRNAME_CONTENT).pathAppended(FILENAME_APPMAINQML).exists()) - errors |= MissingAppMainQml; - - if (!rootDir.pathAppended(DIRNAME_IMPORT).exists()) - errors |= MissingImportDir; - if (!rootDir.pathAppended(DIRNAME_ASSETIMPORT).exists()) - errors |= MissingAssetImportDir; - - if (!rootDir.pathAppended(DIRNAME_CPP).exists()) - errors |= MissingCppDir; - if (!rootDir.pathAppended(DIRNAME_CPP).pathAppended(FILENAME_MAINCPP).exists()) - errors |= MissingMainCpp; - if (!rootDir.pathAppended(DIRNAME_CPP).pathAppended(FILENAME_MAINCPP_HEADER).exists()) - errors |= MissingMainCppHeader; - if (!rootDir.pathAppended(DIRNAME_CPP).pathAppended(FILENAME_ENV_HEADER).exists()) - errors |= MissingEnvHeader; - - if (!rootDir.pathAppended(FILENAME_CMAKELISTS).exists()) - errors |= MissingMainCMake; - if (!rootDir.pathAppended(FILENAME_MODULES).exists()) - errors |= MissingQmlModules; - if (!rootDir.pathAppended(FILENAME_MAINQML).exists()) - errors |= MissingMainQml; - - return errors; -} - -const QString WARNING_MISSING_STRUCTURE_FATAL = Tr::tr("The project is not properly structured for automatically generating CMake files.\n\nAborting process.\n\nThe following files or directories are missing:\n\n%1"); -//const QString WARNING_MISSING_STRUCTURE_NONFATAL = Tr::tr("The project is not properly structured for automatically generating CMake files.\n\nThe following files or directories are missing and may be created:\n\n%1"); -const QString WARNING_TITLE_FATAL = Tr::tr("Cannot Generate CMake Files"); -//const QString WARNING_TITLE_NONFATAL = Tr::tr("Problems with Generating CMake Files"); - -void showProjectDirErrorDialog(int error) -{ - bool isFatal = isErrorFatal(error); - - if (isFatal) { - QString fatalList; - - if (error & MissingContentDir) - fatalList.append(QString(DIRNAME_CONTENT) + "\n"); - if (error & MissingAppMainQml) - fatalList.append(QString(DIRNAME_CONTENT) - + QDir::separator() - + QString(FILENAME_APPMAINQML) - + "\n"); - if (error & MissingCppDir) - fatalList.append(QString(DIRNAME_CPP) + "\n"); - if (error & MissingImportDir) - fatalList.append(QString(DIRNAME_IMPORT) + "\n"); - - QMessageBox::critical(nullptr, - WARNING_TITLE_FATAL, - WARNING_MISSING_STRUCTURE_FATAL.arg(fatalList)); - } -} - -bool FileQueue::queueFile(const FilePath &filePath, const QString &fileContent) -{ - GeneratableFile file; - file.filePath = filePath; - file.content = fileContent; - file.fileExists = filePath.exists(); - m_queuedFiles.append(file); - - return true; -} - -const QVector FileQueue::queuedFiles() const -{ - return m_queuedFiles; -} - -bool FileQueue::writeQueuedFiles() -{ - for (GeneratableFile &file: m_queuedFiles) - if (!writeFile(file)) - return false; - - return true; -} - -bool FileQueue::writeFile(const GeneratableFile &file) -{ - QFile fileHandle(file.filePath.toString()); - fileHandle.open(QIODevice::WriteOnly); - QTextStream stream(&fileHandle); - stream << file.content; - fileHandle.close(); - - return true; -} - -void FileQueue::filterFiles(const Utils::FilePaths keepFiles) -{ - QtConcurrent::blockingFilter(m_queuedFiles, [keepFiles](const GeneratableFile &qf) { - return keepFiles.contains(qf.filePath); - }); -} - -QString readTemplate(const QString &templatePath) -{ - QFile templatefile(templatePath); - templatefile.open(QIODevice::ReadOnly); - QTextStream stream(&templatefile); - QString content = stream.readAll(); - templatefile.close(); - - return content; -} - -const QString projectEnvironmentVariable(const QString &key) -{ - QString value = {}; - - if (auto buildSystem = QmlBuildSystem::getStartupBuildSystem()) { - auto envItems = buildSystem->environment(); - auto confEnv = std::find_if(envItems.begin(), envItems.end(), [key](EnvironmentItem &item) { - return item.name == key; - }); - if (confEnv != envItems.end()) - value = confEnv->value; - } - - return value; -} - -const QDir::Filters FILES_ONLY = QDir::Files; -const QDir::Filters DIRS_ONLY = QDir::Dirs|QDir::Readable|QDir::NoDotAndDotDot; - -const char MAIN_CMAKEFILE_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectmaincmakelists.tpl"; -const char QMLMODULES_FILE_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectmodules.tpl"; - -bool CmakeFileGenerator::prepare(const FilePath &rootDir, bool checkFileBelongs) -{ - m_checkFileIsInProject = checkFileBelongs; - - FilePath contentDir = rootDir.pathAppended(DIRNAME_CONTENT); - FilePath importDir = rootDir.pathAppended(DIRNAME_IMPORT); - FilePath assetImportDir = rootDir.pathAppended(DIRNAME_ASSETIMPORT); - - generateModuleCmake(contentDir); - generateImportCmake(importDir); - generateImportCmake(assetImportDir); - generateMainCmake(rootDir); - generateEntryPointFiles(rootDir); - - return true; -} - -const FileQueue CmakeFileGenerator::fileQueue() const -{ - return m_fileQueue; -} - -void CmakeFileGenerator::filterFileQueue(const Utils::FilePaths &keepFiles) -{ - m_fileQueue.filterFiles(keepFiles); -} - -bool CmakeFileGenerator::execute() -{ - return m_fileQueue.writeQueuedFiles(); -} - -FilePaths CmakeFileGenerator::invalidFileNames() const -{ - return m_invalidFileNames; -} - -const char DO_NOT_EDIT_FILE_COMMENT[] = "### This file is automatically generated by Qt Design Studio.\n### Do not change\n\n"; -const char ADD_SUBDIR[] = "add_subdirectory(%1)\n"; - -void CmakeFileGenerator::generateMainCmake(const FilePath &rootDir) -{ - //TODO startupProject() may be a terrible way to try to get "current project". It's not necessarily the same thing at all. - QString projectName = ProjectExplorer::ProjectManager::startupProject()->displayName(); - QString appName = projectName + "App"; - - QString fileSection = ""; - const QString qtcontrolsConfFile = GenerateCmake::projectEnvironmentVariable(ENV_VARIABLE_CONTROLCONF); - if (!qtcontrolsConfFile.isEmpty()) - fileSection = QString(" FILES\n %1").arg(qtcontrolsConfFile); - - QString cmakeFileContent = GenerateCmake::readTemplate(MAIN_CMAKEFILE_TEMPLATE_PATH) - .arg(appName) - .arg(fileSection); - - queueCmakeFile(rootDir, cmakeFileContent); - - QString subdirIncludes; - subdirIncludes.append(QString(ADD_SUBDIR).arg(DIRNAME_CONTENT)); - subdirIncludes.append(QString(ADD_SUBDIR).arg(DIRNAME_IMPORT)); - if (rootDir.pathAppended(DIRNAME_ASSETIMPORT).exists()) - subdirIncludes.append(QString(ADD_SUBDIR).arg(DIRNAME_ASSETIMPORT)); - - QString modulesAsPlugins; - for (const QString &moduleName : m_moduleNames) - modulesAsPlugins.append(" " + moduleName + "plugin\n"); - - QString moduleFileContent = GenerateCmake::readTemplate(QMLMODULES_FILE_TEMPLATE_PATH) - .arg(appName) - .arg(subdirIncludes) - .arg(modulesAsPlugins); - - m_fileQueue.queueFile(rootDir.pathAppended(FILENAME_MODULES), moduleFileContent); -} - -void CmakeFileGenerator::generateImportCmake(const FilePath &dir, const QString &modulePrefix) -{ - if (!dir.exists()) - return; - - QString fileContent; - - fileContent.append(DO_NOT_EDIT_FILE_COMMENT); - FilePaths subDirs = dir.dirEntries(DIRS_ONLY); - for (FilePath &subDir : subDirs) { - if (isDirBlacklisted(subDir)) - continue; - if (getDirectoryTreeQmls(subDir).isEmpty() && getDirectoryTreeResources(subDir).isEmpty()) - continue; - fileContent.append(QString(ADD_SUBDIR).arg(subDir.fileName())); - QString prefix = modulePrefix.isEmpty() ? - QString(modulePrefix % subDir.fileName()) : - QString(QString(modulePrefix + '.') + subDir.fileName()); - if (getDirectoryQmls(subDir).isEmpty()) { - generateImportCmake(subDir, prefix); - } else { - generateModuleCmake(subDir, prefix); - } - } - - queueCmakeFile(dir, fileContent); -} - -const char MODULEFILE_PROPERTY_SINGLETON[] = "QT_QML_SINGLETON_TYPE"; -const char MODULEFILE_PROPERTY_SET[] = "set_source_files_properties(%1\n PROPERTIES\n %2 %3\n)\n\n"; -const char MODULEFILE_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectmodulecmakelists.tpl"; - -void CmakeFileGenerator::generateModuleCmake(const FilePath &dir, const QString &uri) -{ - QString fileTemplate = GenerateCmake::readTemplate(MODULEFILE_TEMPLATE_PATH); - - QString singletonContent; - FilePaths qmldirFileList = dir.dirEntries({QStringList(FILENAME_QMLDIR), FILES_ONLY}); - if (!qmldirFileList.isEmpty()) { - QStringList singletons = getSingletonsFromQmldirFile(qmldirFileList.first()); - for (QString &singleton : singletons) { - singletonContent.append(QString(MODULEFILE_PROPERTY_SET).arg(singleton).arg(MODULEFILE_PROPERTY_SINGLETON).arg("true")); - } - } - - QStringList qmlFileList = getDirectoryTreeQmls(dir); - QString qmlFiles; - for (QString &qmlFile : qmlFileList) - qmlFiles.append(QString(" %1\n").arg(qmlFile)); - - QStringList resourceFileList = getDirectoryTreeResources(dir); - QString resourceFiles; - for (QString &resourceFile : resourceFileList) - resourceFiles.append(QString(" %1\n").arg(resourceFile)); - - QString moduleContent; - if (!qmlFiles.isEmpty()) { - moduleContent.append(QString(" QML_FILES\n%1").arg(qmlFiles)); - } - if (!resourceFiles.isEmpty()) { - moduleContent.append(QString(" RESOURCES\n%1").arg(resourceFiles)); - } - - QString moduleUri = uri.isEmpty() ? - dir.fileName() : - uri; - - QString moduleName = QString(moduleUri).replace('.', '_'); - m_moduleNames.append(moduleName); - - QString fileContent; - fileContent.append(fileTemplate.arg(singletonContent, moduleName, moduleUri, moduleContent)); - queueCmakeFile(dir, fileContent); -} - -QStringList CmakeFileGenerator::getSingletonsFromQmldirFile(const FilePath &filePath) -{ - QStringList singletons; - QFile f(filePath.toString()); - f.open(QIODevice::ReadOnly); - QTextStream stream(&f); - - while (!stream.atEnd()) { - QString line = stream.readLine(); - if (line.startsWith("singleton", Qt::CaseInsensitive)) { - QStringList tokenizedLine = line.split(QRegularExpression("\\s+")); - QString fileName = tokenizedLine.last(); - if (fileName.endsWith(".qml", Qt::CaseInsensitive)) { - singletons.append(fileName); - } - } - } - - f.close(); - - return singletons; -} - -QStringList CmakeFileGenerator::getDirectoryQmls(const FilePath &dir) -{ - QStringList moduleFiles; - - const QStringList qmlFilesOnly(FILENAME_FILTER_QML); - FilePaths allFiles = dir.dirEntries({qmlFilesOnly, FILES_ONLY}); - for (FilePath &file : allFiles) { - if (includeFile(file)) { - moduleFiles.append(file.fileName()); - } - } - - return moduleFiles; -} - -QStringList CmakeFileGenerator::getDirectoryResources(const FilePath &dir) -{ - QStringList moduleFiles; - - FilePaths allFiles = dir.dirEntries(FILES_ONLY); - for (FilePath &file : allFiles) { - if (!file.fileName().endsWith(".qml", Qt::CaseInsensitive) && includeFile(file)) { - moduleFiles.append(file.fileName()); - } - } - - return moduleFiles; -} - -QStringList CmakeFileGenerator::getDirectoryTreeQmls(const FilePath &dir) -{ - QStringList qmlFileList; - - qmlFileList.append(getDirectoryQmls(dir)); - - FilePaths subDirsList = dir.dirEntries(DIRS_ONLY); - for (FilePath &subDir : subDirsList) { - if (isDirBlacklisted(subDir)) - continue; - QStringList subDirQmlFiles = getDirectoryTreeQmls(subDir); - for (QString &qmlFile : subDirQmlFiles) { - qmlFileList.append(subDir.fileName().append('/').append(qmlFile)); - } - } - - return qmlFileList; -} - -static void appendWidthQuotes(QStringList &list, const QString &string) -{ - if (string.contains(' ')) - list.append("\"" + string + "\""); - else - list.append(string); -} - -QStringList CmakeFileGenerator::getDirectoryTreeResources(const FilePath &dir) -{ - QStringList resourceFileList; - - //for (const auto &string : getDirectoryResources(dir)) - // appendWidthQuotes(resourceFileList, string); - resourceFileList.append(getDirectoryResources(dir)); - - FilePaths subDirsList = dir.dirEntries(DIRS_ONLY); - for (FilePath &subDir : subDirsList) { - if (isDirBlacklisted(subDir)) - continue; - QStringList subDirResources = getDirectoryTreeResources(subDir); - for (QString &resource : subDirResources) { - appendWidthQuotes(resourceFileList, subDir.fileName().append('/').append(resource)); - } - } - - return resourceFileList; -} - -void CmakeFileGenerator::queueCmakeFile(const FilePath &dir, const QString &content) -{ - FilePath filePath = dir.pathAppended(FILENAME_CMAKELISTS); - m_fileQueue.queueFile(filePath, content); -} - -bool CmakeFileGenerator::isFileBlacklisted(const QString &fileName) -{ - return (!fileName.compare(FILENAME_QMLDIR) || - !fileName.compare(FILENAME_CMAKELISTS)); -} - -bool CmakeFileGenerator::isDirBlacklisted(const FilePath &dir) -{ - return (!dir.fileName().compare(DIRNAME_DESIGNER)); -} - -bool CmakeFileGenerator::validFileName(const Utils::FilePath &filePath) -{ - QStringList invalidChars = {"!", "\"", "ÂŁ", "$", "%", "!", "^", "&", "*", "(", ")", "=", "+", - "'", ",", ";", ":", "#", "~", "{", "{", "[", "]", "<", ">", "?"}; - const QString baseName = filePath.baseName(); - for (const auto &c : invalidChars) { - if (baseName.contains(c)) - return false; - } - return true; -} - -bool CmakeFileGenerator::includeFile(const FilePath &filePath) -{ - if (m_checkFileIsInProject) { - ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject(); - if (!project->isKnownFile(filePath)) - return false; - } - - if (validFileName(filePath)) - return !isFileBlacklisted(filePath.fileName()); - else - m_invalidFileNames.append(filePath); - - return false; -} - -bool CmakeFileGenerator::generateEntryPointFiles(const FilePath &dir) -{ - const QString qtcontrolsConf = GenerateCmake::projectEnvironmentVariable(ENV_VARIABLE_CONTROLCONF); - if (!qtcontrolsConf.isEmpty()) - m_resourceFileLocations.append(qtcontrolsConf); - - bool cppOk = generateMainCpp(dir); - bool qmlOk = generateMainQml(dir); - - return cppOk && qmlOk; -} - -const char MAIN_CPPFILE_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectmaincpp.tpl"; -const char MAIN_CPPFILE_HEADER_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectmaincppheader.tpl"; -const char MAIN_CPPFILE_HEADER_PLUGIN_LINE[] = "Q_IMPORT_QML_PLUGIN(%1)\n"; -const char ENV_HEADER_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectenvheader.tpl"; -const char ENV_HEADER_VARIABLE_LINE[] = " qputenv(\"%1\", \"%2\");\n"; - -bool CmakeFileGenerator::generateMainCpp(const FilePath &dir) -{ - FilePath srcDir = dir.pathAppended(DIRNAME_CPP); - - QString cppContent = GenerateCmake::readTemplate(MAIN_CPPFILE_TEMPLATE_PATH); - FilePath cppFilePath = srcDir.pathAppended(FILENAME_MAINCPP); - bool cppOk = m_fileQueue.queueFile(cppFilePath, cppContent); - - QString modulesAsPlugins; - for (const QString &moduleName : m_moduleNames) - modulesAsPlugins.append( - QString(MAIN_CPPFILE_HEADER_PLUGIN_LINE).arg(moduleName + "Plugin")); - - QString headerContent = GenerateCmake::readTemplate(MAIN_CPPFILE_HEADER_TEMPLATE_PATH) - .arg(modulesAsPlugins); - FilePath headerFilePath = srcDir.pathAppended(FILENAME_MAINCPP_HEADER); - bool pluginHeaderOk = m_fileQueue.queueFile(headerFilePath, headerContent); - - bool envHeaderOk = true; - QString environment; - - if (auto buildSystem = QmlBuildSystem::getStartupBuildSystem()) { - for (EnvironmentItem &envItem : buildSystem->environment()) { - QString key = envItem.name; - QString value = envItem.value; - if (isFileResource(value)) - value.prepend(":/"); - environment.append(QString(ENV_HEADER_VARIABLE_LINE).arg(key).arg(value)); - } - QString envHeaderContent = GenerateCmake::readTemplate(ENV_HEADER_TEMPLATE_PATH) - .arg(environment); - FilePath envHeaderPath = srcDir.pathAppended(FILENAME_ENV_HEADER); - envHeaderOk = m_fileQueue.queueFile(envHeaderPath, envHeaderContent); - } - - return cppOk && pluginHeaderOk && envHeaderOk; -} - -const char MAIN_QMLFILE_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectmainqml.tpl"; - -bool CmakeFileGenerator::generateMainQml(const FilePath &dir) -{ - QString content = GenerateCmake::readTemplate(MAIN_QMLFILE_TEMPLATE_PATH); - FilePath filePath = dir.pathAppended(FILENAME_MAINQML); - return m_fileQueue.queueFile(filePath, content); -} - -bool CmakeFileGenerator::isFileResource(const QString &relativeFilePath) -{ - if (m_resourceFileLocations.contains(relativeFilePath)) - return true; - - return false; -} - - - -} //GenerateCmake -} //QmlProjectManager - diff --git a/src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.h b/src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.h deleted file mode 100644 index db025daa275..00000000000 --- a/src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.h +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include - -#include - -namespace QmlProjectManager { -namespace GenerateCmake { -struct GeneratableFile { - Utils::FilePath filePath; - QString content; - bool fileExists; -}; - -bool operator==(const GeneratableFile &left, const GeneratableFile &right); - -void generateMenuEntry(QObject *parent); -void onGenerateCmakeLists(); -bool isErrorFatal(int error); -int isProjectCorrectlyFormed(const Utils::FilePath &rootDir); -void showProjectDirErrorDialog(int error); -QString readTemplate(const QString &templatePath); -const QString projectEnvironmentVariable(const QString &key); - -class FileQueue { -public: - bool queueFile(const Utils::FilePath &filePath, const QString &fileContent); - const QVector queuedFiles() const; - bool writeQueuedFiles(); - void filterFiles(const Utils::FilePaths keepFiles); - -private: - bool writeFile(const GeneratableFile &file); - -private: - QVector m_queuedFiles; -}; - -class CmakeFileGenerator { -public: - bool prepare(const Utils::FilePath &rootDir, bool check = true); - const FileQueue fileQueue() const; - void filterFileQueue(const Utils::FilePaths &keepFiles); - bool execute(); - Utils::FilePaths invalidFileNames() const; - -private: - void generateMainCmake(const Utils::FilePath &rootDir); - void generateImportCmake(const Utils::FilePath &dir, const QString &modulePrefix = QString()); - void generateModuleCmake(const Utils::FilePath &dir, const QString &moduleUri = QString()); - bool generateEntryPointFiles(const Utils::FilePath &dir); - bool generateMainCpp(const Utils::FilePath &dir); - bool generateMainQml(const Utils::FilePath &dir); - QStringList getDirectoryQmls(const Utils::FilePath &dir); - QStringList getDirectoryResources(const Utils::FilePath &dir); - QStringList getSingletonsFromQmldirFile(const Utils::FilePath &filePath); - QStringList getDirectoryTreeQmls(const Utils::FilePath &dir); - QStringList getDirectoryTreeResources(const Utils::FilePath &dir); - void queueCmakeFile(const Utils::FilePath &filePath, const QString &content); - bool isFileResource(const QString &relativeFilePath); - bool isFileBlacklisted(const QString &fileName); - bool isDirBlacklisted(const Utils::FilePath &dir); - bool includeFile(const Utils::FilePath &filePath); - bool validFileName(const Utils::FilePath &filePath); - -private: - FileQueue m_fileQueue; - QStringList m_resourceFileLocations; - QStringList m_moduleNames; - bool m_checkFileIsInProject; - - Utils::FilePaths m_invalidFileNames; -}; - -} //GenerateCmake - -} //QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/cmakegen/generatecmakelistsconstants.h b/src/plugins/qmlprojectmanager/cmakegen/generatecmakelistsconstants.h deleted file mode 100644 index b983ce7fece..00000000000 --- a/src/plugins/qmlprojectmanager/cmakegen/generatecmakelistsconstants.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#ifndef GENERATECMAKELISTSCONSTANTS_H -#define GENERATECMAKELISTSCONSTANTS_H - -#pragma once - -namespace QmlProjectManager { -namespace GenerateCmake { -namespace Constants { - -const char DIRNAME_CONTENT[] = "content"; -const char DIRNAME_IMPORT[] = "imports"; -const char DIRNAME_ASSET[] = "assets"; -const char DIRNAME_ASSETIMPORT[] = "asset_imports"; -const char DIRNAME_CPP[] = "src"; -const char DIRNAME_DESIGNER[] = "designer"; - -const char FILENAME_CMAKELISTS[] = "CMakeLists.txt"; -const char FILENAME_APPMAINQML[] = "App.qml"; -const char FILENAME_MAINQML[] = "main.qml"; -const char FILENAME_MAINCPP[] = "main.cpp"; -const char FILENAME_MAINCPP_HEADER[] = "import_qml_plugins.h"; -const char FILENAME_MODULES[] = "qmlmodules"; -const char FILENAME_QMLDIR[] = "qmldir"; -const char FILENAME_ENV_HEADER[] = "app_environment.h"; - -const char FILENAME_SUFFIX_QMLPROJECT[] = "qmlproject"; -const char FILENAME_SUFFIX_QML[] = "qml"; -const char FILENAME_SUFFIX_USER[] = "user"; - -const char FILENAME_FILTER_QMLPROJECT[] = "*.qmlproject"; -const char FILENAME_FILTER_QML[] = "*.qml"; - -const char ENV_VARIABLE_CONTROLCONF[] = "QT_QUICK_CONTROLS_CONF"; - -} //Constants -} //GenerateCmake -} //QmlProjectManager - -#endif // GENERATECMAKELISTSCONSTANTS_H diff --git a/src/plugins/qmlprojectmanager/cmakegen/qmlprojectappmainqml.tpl b/src/plugins/qmlprojectmanager/cmakegen/qmlprojectappmainqml.tpl deleted file mode 100644 index 35f8218dc65..00000000000 --- a/src/plugins/qmlprojectmanager/cmakegen/qmlprojectappmainqml.tpl +++ /dev/null @@ -1,14 +0,0 @@ -import QtQuick -import QtQuick.Window - -Window { - visible: true - title: "%1" - width: mainScreen.width - height: mainScreen.height - - %1 { - id: mainScreen - } - -} diff --git a/src/plugins/qmlprojectmanager/cmakegen/qmlprojectenvheader.tpl b/src/plugins/qmlprojectmanager/cmakegen/qmlprojectenvheader.tpl deleted file mode 100644 index bf60adb4ac1..00000000000 --- a/src/plugins/qmlprojectmanager/cmakegen/qmlprojectenvheader.tpl +++ /dev/null @@ -1,11 +0,0 @@ -/* - * This file is automatically generated by Qt Design Studio. - * Do not change. -*/ - -#include - -void set_qt_environment() -{ -%1 -} diff --git a/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmaincmakelists.tpl b/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmaincmakelists.tpl deleted file mode 100644 index 9ca0ace63ba..00000000000 --- a/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmaincmakelists.tpl +++ /dev/null @@ -1,52 +0,0 @@ -cmake_minimum_required(VERSION 3.21.1) - -option(LINK_INSIGHT "Link Qt Insight Tracker library" ON) -option(BUILD_QDS_COMPONENTS "Build design studio components" ON) - -project(%1 LANGUAGES CXX) - -set(CMAKE_AUTOMOC ON) -set(CMAKE_INCLUDE_CURRENT_DIR ON) -set(QT_QML_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/qml) -set(QML_IMPORT_PATH ${QT_QML_OUTPUT_DIRECTORY} - CACHE STRING "Import paths for Qt Creator's code model" - FORCE -) - -find_package(Qt6 6.2 REQUIRED COMPONENTS Core Gui Qml Quick) - -if (Qt6_VERSION VERSION_GREATER_EQUAL 6.3) - qt_standard_project_setup() -endif() - -qt_add_executable(%1 src/main.cpp) - -qt_add_resources(%1 "configuration" - PREFIX "/" -%2 -) - -target_link_libraries(%1 PRIVATE - Qt${QT_VERSION_MAJOR}::Core - Qt${QT_VERSION_MAJOR}::Gui - Qt${QT_VERSION_MAJOR}::Quick - Qt${QT_VERSION_MAJOR}::Qml -) - -if (BUILD_QDS_COMPONENTS) - include(${CMAKE_CURRENT_SOURCE_DIR}/qmlcomponents) -endif() - -include(${CMAKE_CURRENT_SOURCE_DIR}/qmlmodules) - -if (LINK_INSIGHT) - include(${CMAKE_CURRENT_SOURCE_DIR}/insight) -endif () - -include(GNUInstallDirs) -install(TARGETS %1 - BUNDLE DESTINATION . - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} -) - diff --git a/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmaincpp.tpl b/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmaincpp.tpl deleted file mode 100644 index 0ff9201d916..00000000000 --- a/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmaincpp.tpl +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only - -#include -#include - -#include "app_environment.h" -#include "import_qml_plugins.h" - -int main(int argc, char *argv[]) -{ - set_qt_environment(); - QGuiApplication app(argc, argv); - - QQmlApplicationEngine engine; - const QUrl url(u"qrc:/qt/qml/Main/main.qml"_qs); - QObject::connect( - &engine, &QQmlApplicationEngine::objectCreated, &app, - [url](QObject *obj, const QUrl &objUrl) { - if (!obj && url == objUrl) - QCoreApplication::exit(-1); - }, - Qt::QueuedConnection); - - engine.addImportPath(QCoreApplication::applicationDirPath() + "/qml"); - engine.addImportPath(":/"); - - engine.load(url); - - if (engine.rootObjects().isEmpty()) { - return -1; - } - - return app.exec(); -} diff --git a/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmaincppheader.tpl b/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmaincppheader.tpl deleted file mode 100644 index 60cef09a82c..00000000000 --- a/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmaincppheader.tpl +++ /dev/null @@ -1,8 +0,0 @@ -/* - * This file is automatically generated by Qt Design Studio. - * Do not change. -*/ - -#include - -%1 diff --git a/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmainqml.tpl b/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmainqml.tpl deleted file mode 100644 index fa8f6d1cc18..00000000000 --- a/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmainqml.tpl +++ /dev/null @@ -1,6 +0,0 @@ -import QtQuick -import content - -App { -} - diff --git a/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmodulecmakelists.tpl b/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmodulecmakelists.tpl deleted file mode 100644 index 93573a1ed7a..00000000000 --- a/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmodulecmakelists.tpl +++ /dev/null @@ -1,12 +0,0 @@ -### This file is automatically generated by Qt Design Studio. -### Do not change - -%1 - -qt_add_library(%2 STATIC) -qt6_add_qml_module(%2 - URI "%3" - VERSION 1.0 - RESOURCE_PREFIX "/qt/qml" -%4 -) diff --git a/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmodules.tpl b/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmodules.tpl deleted file mode 100644 index 2b74c49af38..00000000000 --- a/src/plugins/qmlprojectmanager/cmakegen/qmlprojectmodules.tpl +++ /dev/null @@ -1,16 +0,0 @@ -### This file is automatically generated by Qt Design Studio. -### Do not change - -qt6_add_qml_module(%1 - URI "Main" - VERSION 1.0 - RESOURCE_PREFIX "/qt/qml" - NO_PLUGIN - QML_FILES main.qml -) - -%2 - -target_link_libraries(%1 PRIVATE -%3 -) diff --git a/src/plugins/qmlprojectmanager/qmlprojectgen/qmlprojectgenerator.cpp b/src/plugins/qmlprojectmanager/qmlprojectgen/qmlprojectgenerator.cpp index 32506183fa7..0270a290fdd 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectgen/qmlprojectgenerator.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectgen/qmlprojectgenerator.cpp @@ -2,7 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "qmlprojectgenerator.h" -#include "../cmakegen/generatecmakelists.h" +#include "../cmakegen/cmakewriter.h" #include "../qmlprojectmanagertr.h" #include @@ -61,7 +61,7 @@ bool QmlProjectFileGenerator::execute() importDirs.removeAll("content"); const QString importPaths = createDirArrayEntry("importPaths", importDirs); - const QString fileContent = GenerateCmake::readTemplate(QMLPROJECT_FILE_TEMPLATE_PATH) + const QString fileContent = GenerateCmake::CMakeWriter::readTemplate(QMLPROJECT_FILE_TEMPLATE_PATH) .arg(contentEntry, imageEntry, jsEntry, assetEntry, importPaths); QFile file(m_targetFile.toString()); diff --git a/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs b/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs index 271b8828ab6..0fa49b5102a 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs +++ b/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs @@ -47,13 +47,6 @@ QtcPlugin { name: "CMake Generator" prefix: "cmakegen/" files: [ - "generatecmakelists.cpp", "generatecmakelists.h", - "generatecmakelistsconstants.h", - "checkablefiletreeitem.cpp", "checkablefiletreeitem.h", - "cmakegeneratordialogtreemodel.cpp", "cmakegeneratordialogtreemodel.h", - "cmakegeneratordialog.cpp", "cmakegeneratordialog.h", - "cmakeprojectconverter.cpp", "cmakeprojectconverter.h", - "cmakeprojectconverterdialog.cpp", "cmakeprojectconverterdialog.h", "cmakegenerator.cpp", "cmakegenerator.h", "cmakewriter.cpp", "cmakewriter.h", "cmakewriterv0.cpp", "cmakewriterv0.h", diff --git a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp index 14ac6060b38..9cd1bae9c67 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp @@ -9,8 +9,7 @@ #include "qmlprojectmanagertr.h" #include "qmlprojectrunconfiguration.h" #include "projectfilecontenttools.h" -#include "cmakegen/cmakeprojectconverter.h" -#include "cmakegen/generatecmakelists.h" +#include "cmakegen/cmakegenerator.h" #include #include @@ -389,10 +388,6 @@ void QmlProjectPlugin::initialize() GenerateCmake::CMakeGenerator::createMenuAction(this); } - - GenerateCmake::generateMenuEntry(this); - if (ICore::isQtDesignStudio()) - GenerateCmake::CmakeProjectConverter::generateMenuEntry(this); } void QmlProjectPlugin::displayQmlLandingPage() From 0496c872a311d73bcbc611d3aab7f13df0543fc5 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 21 Dec 2023 15:25:24 +0100 Subject: [PATCH 176/202] QmlDesigner: Allow to add a signal Change-Id: Ib34cb19c9a046d8a404c5be06099280f29686662 Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- .../imports/HelperWidgets/DynamicPropertiesSection.qml | 2 +- .../components/propertyeditor/dynamicpropertiesproxymodel.cpp | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DynamicPropertiesSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DynamicPropertiesSection.qml index 9b23842fe80..88a0debae80 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DynamicPropertiesSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DynamicPropertiesSection.qml @@ -708,7 +708,7 @@ Section { StudioControls.ComboBox { id: comboBox actionIndicator.visible: false - model: ["int", "real", "color", "string", "bool", "url", "alias", + model: ["int", "real", "color", "string", "bool", "url", "alias", "signal", "TextureInput", "vector2d", "vector3d", "vector4d"] width: cePopup.itemWidth } diff --git a/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp b/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp index 45f89ae3392..6fb45cc9250 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp @@ -167,6 +167,10 @@ void DynamicPropertiesProxyModel::createProperty(const QString &name, const QStr QVariant value = defaultValueForType(typeName); VariantProperty variantProp = modelNode.variantProperty(name.toUtf8()); variantProp.setDynamicTypeNameAndValue(typeName, value); + } else if (type == "signal") { + SignalDeclarationProperty signalDeclarationProperty + = modelNode.signalDeclarationProperty(name.toUtf8()); + signalDeclarationProperty.setSignature("()"); } else { QString expression = defaultExpressionForType(typeName); From 778688154d55719b2b53ff8747003cd0fb18bf2e Mon Sep 17 00:00:00 2001 From: Shrief Gabr Date: Fri, 19 Apr 2024 15:47:20 +0300 Subject: [PATCH 177/202] QmlDesigner: Fix model editor layout issues on adding rows/columns Task-number: QDS-12352 Change-Id: Ie37d1afe2a243d08a09a55778cf0bf00de8f327c Reviewed-by: Ali Kianian Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../CollectionDetailsView.qml | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml index e40a3c94577..2193bd1763e 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml @@ -252,37 +252,35 @@ Rectangle { function ensureRowIsVisible(row) { let rows = tableView.model.rowCount() - if (row < 0 || row >= rows) { - tableView.targetRow = -1 - return - } + let rowIsLoaded = tableView.isRowLoaded(row) + + if (row < 0 || row >= rows || rowIsLoaded) { + if (rowIsLoaded) + tableView.positionViewAtRow(row, Qt.AlignLeft | Qt.AlignTop) - if (tableView.isRowLoaded(row)) { - tableView.positionViewAtRow(row, Qt.AlignLeft | Qt.AlignTop) tableView.targetRow = -1 return } tableView.targetRow = row - verticalScrollBar.position = row / rows + tableView.positionViewAtRow(row, Qt.AlignLeft | Qt.AlignTop) ensureTimer.start() } function ensureColumnIsVisible(column) { let columns = tableView.model.columnCount() - if (column < 0 || column >= columns) { - tableView.targetColumn = -1 - return - } + let columnIsLoaded = tableView.isColumnLoaded(column) + + if (column < 0 || column >= columns || columnIsLoaded) { + if (columnIsLoaded) + tableView.positionViewAtColumn(column, Qt.AlignLeft | Qt.AlignTop) - if (tableView.isColumnLoaded(column)) { - tableView.positionViewAtColumn(column, Qt.AlignLeft | Qt.AlignTop) tableView.targetColumn = -1 return } tableView.targetColumn = column - horizontalScrollBar.position = column / columns + tableView.positionViewAtColumn(column, Qt.AlignLeft | Qt.AlignTop) ensureTimer.start() } From 5a49c1669437ef2d4e1c2e284c912cb774495999 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 22 Mar 2024 17:45:21 +0200 Subject: [PATCH 178/202] QmlDesigner: Use space key to move close to object at crosshairs When in fly mode in 3D view, crosshairs are shown in the middle of the active split. Pressing space in fly mode when there is a model at the crosshairs will move the camera close to the model. Fixes: QDS-12292 Change-Id: Id15c13458af3763f4e0712614cf9cf3ed695fb5d Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- src/tools/qml2puppet/editor3d_qt6.qrc | 2 + .../qml2puppet/mockfiles/images/crosshair.png | Bin 0 -> 172 bytes .../mockfiles/images/crosshair@2x.png | Bin 0 -> 202 bytes .../mockfiles/qt6/EditCameraController.qml | 29 ++++++++++- .../qml2puppet/editor3d/generalhelper.cpp | 49 ++++++++++++++++++ .../qml2puppet/editor3d/generalhelper.h | 2 + 6 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 src/tools/qml2puppet/mockfiles/images/crosshair.png create mode 100644 src/tools/qml2puppet/mockfiles/images/crosshair@2x.png diff --git a/src/tools/qml2puppet/editor3d_qt6.qrc b/src/tools/qml2puppet/editor3d_qt6.qrc index 2913fbe15e0..d76b1941b99 100644 --- a/src/tools/qml2puppet/editor3d_qt6.qrc +++ b/src/tools/qml2puppet/editor3d_qt6.qrc @@ -8,6 +8,8 @@ mockfiles/images/editor_camera@2x.png mockfiles/images/editor_particlesystem.png mockfiles/images/editor_particlesystem@2x.png + mockfiles/images/crosshair.png + mockfiles/images/crosshair@2x.png mockfiles/images/directional.png mockfiles/images/directional@2x.png mockfiles/images/point.png diff --git a/src/tools/qml2puppet/mockfiles/images/crosshair.png b/src/tools/qml2puppet/mockfiles/images/crosshair.png new file mode 100644 index 0000000000000000000000000000000000000000..6c302635132c09f675b8fc177096802a7c86aa3a GIT binary patch literal 172 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-7G?$phNU`BwlFX-hz9tCxH2#>D9bBA(EPb` z*REdu|NsADHM2Ji3=GUAL4Lsu3g=Ta?PD1j7@R#_978NlCnq>Cs426(akwGb&Qkno z1jG=J{g zwX0YE|Np;O&Fl>W0|RqOkY6x^!ub?U`&b4BhD1*n$B>A_$q5Jerie;-F!iXhEy+wU zkiKfLDlz2ninfL84o(T-T{3O$;slW%rdtfD$tex0$wmt`43Z3dotZQvHtj2r*u<57 zDOZAZae~MNh1LCKnQo>%SrS(bRyEeeN-?OU>fBLDVNYRTU|{fc^>bP0l+XkK7L7iS literal 0 HcmV?d00001 diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml index 2d77aaf9877..255d93e5295 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml @@ -102,6 +102,23 @@ Item { storeCameraState(0); } + function approachObject() + { + if (!camera) + return; + + var pickResult = _generalHelper.pickViewAt(view3d, width / 2, height / 2); + var resolvedResult = _generalHelper.resolvePick(pickResult.objectHit); + + if (resolvedResult) { + var newLookAtAndZoom = _generalHelper.approachNode(camera, _defaultCameraLookAtDistance, + resolvedResult, view3D); + _lookAtPoint = newLookAtAndZoom.toVector3d(); + _zoomFactor = newLookAtAndZoom.w; + storeCameraState(0); + } + } + function jumpToRotation(rotation) { let distance = camera.scenePosition.minus(_lookAtPoint).length() @@ -240,6 +257,13 @@ Item { } } + Image { + anchors.centerIn: parent + source: "qrc:///qtquickplugin/mockfiles/images/crosshair.png" + visible: cameraCtrl.flyMode && viewRoot.activeSplit === cameraCtrl.splitId + opacity: 0.7 + } + MouseArea { id: mouseHandler acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton @@ -304,7 +328,10 @@ Item { Keys.onPressed: (event) => { event.accepted = true; - _generalHelper.startCameraMove(cameraCtrl.camera, cameraCtrl.getMoveVectorForKey(event.key)); + if (cameraCtrl.flyMode && event.key === Qt.Key_Space) + approachObject(); + else + _generalHelper.startCameraMove(cameraCtrl.camera, cameraCtrl.getMoveVectorForKey(event.key)); } Keys.onReleased: (event) => { diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp index b33a480a450..dadd8176330 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp @@ -398,6 +398,55 @@ QVector4D GeneralHelper::focusNodesToCamera(QQuick3DCamera *camera, float defaul return QVector4D(lookAt, cameraZoomFactor); } +// Approaches the specified node without changing camera orientation +QVector4D GeneralHelper::approachNode( + QQuick3DCamera *camera, float defaultLookAtDistance, QObject *node, + QQuick3DViewport *viewPort) +{ + auto node3d = qobject_cast(node); + if (!camera || !node3d) + return QVector4D(0.f, 0.f, 0.f, 1.f); + + QVector3D minBounds = maxVec; + QVector3D maxBounds = minVec; + + getBounds(viewPort, node3d, minBounds, maxBounds); // Bounds are in node3d local coordinates + + QVector3D extents = maxBounds - minBounds; + QVector3D focusLookAt = minBounds + (extents / 2.f); + + if (node3d->parentNode()) { + QMatrix4x4 m = node3d->parentNode()->sceneTransform(); + focusLookAt = m.map(focusLookAt); + } + + float maxExtent = qSqrt(qreal(extents.x()) * qreal(extents.x()) + + qreal(extents.y()) * qreal(extents.y()) + + qreal(extents.z()) * qreal(extents.z())); + + // Reset camera position to default zoom + QMatrix4x4 m = camera->sceneTransform(); + const float *dataPtr(m.data()); + QVector3D newLookVector(dataPtr[8], dataPtr[9], dataPtr[10]); + newLookVector.normalize(); + + // We don't want to change camera orientation, so calculate projection point on current + // camera look vector + QVector3D focusLookAtVector = focusLookAt - camera->position(); + float dot = QVector3D::dotProduct(newLookVector, focusLookAtVector); + QVector3D newLookAt = camera->position() + dot * newLookVector; + + newLookVector *= defaultLookAtDistance; + camera->setPosition(newLookAt + newLookVector); + + float divisor = 1050.f; + float newZoomFactor = qBound(.01f, maxExtent / divisor, 100.f); + float cameraZoomFactor = zoomCamera(viewPort, camera, 0, defaultLookAtDistance, newLookAt, + newZoomFactor, false); + + return QVector4D(newLookAt, cameraZoomFactor); +} + // This function can be used to synchronously focus camera on a node, which doesn't have to be // a selection box for bound calculations to work. This is used to focus the view for // various preview image generations, where doing things asynchronously is not good diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h index 5622918a64b..62a95d86a6f 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h @@ -70,6 +70,8 @@ public: const QVariant &nodes, QQuick3DViewport *viewPort, float oldZoom, bool updateZoom = true, bool closeUp = false); + Q_INVOKABLE QVector4D approachNode(QQuick3DCamera *camera, float defaultLookAtDistance, + QObject *node, QQuick3DViewport *viewPort); Q_INVOKABLE void calculateNodeBoundsAndFocusCamera(QQuick3DCamera *camera, QQuick3DNode *node, QQuick3DViewport *viewPort, float defaultLookAtDistance, bool closeUp); From 1b52357d0197a2ab071c781edefb2632340f9e71 Mon Sep 17 00:00:00 2001 From: Knud Dollereder Date: Fri, 19 Apr 2024 16:17:57 +0200 Subject: [PATCH 179/202] QmlProjectManager: Fix cmake generator update issues Fixes: QDS-12518 Change-Id: I27d45213100e42117b130bcbbceb5e115ed68445 Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- .../cmakegen/cmakegenerator.cpp | 44 +++++++----- .../cmakegen/cmakewriterv1.cpp | 67 ++++++++++--------- 2 files changed, 63 insertions(+), 48 deletions(-) diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp index 8e77cde4f76..659c544ebd8 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp +++ b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp @@ -154,10 +154,10 @@ void CMakeGenerator::initialize(QmlProject *project) parseNodeTree(m_root, rootProjectNode); parseSourceTree(); - compareWithFileSystem(m_root); - createCMakeFiles(m_root); createSourceFiles(); + + compareWithFileSystem(m_root); } void CMakeGenerator::update(const QSet &added, const QSet &removed) @@ -170,7 +170,7 @@ void CMakeGenerator::update(const QSet &added, const QSet &rem std::set dirtyModules; for (const QString &add : added) { const Utils::FilePath path = Utils::FilePath::fromString(add); - if (auto node = findOrCreateNode(m_root, path)) { + if (auto node = findOrCreateNode(m_root, path.parentDir())) { insertFile(node, path); if (auto module = findModuleFor(node)) dirtyModules.insert(module); @@ -182,15 +182,15 @@ void CMakeGenerator::update(const QSet &added, const QSet &rem for (const QString &remove : removed) { const Utils::FilePath path = Utils::FilePath::fromString(remove); - if (auto node = findNode(m_root, path)) { + if (auto node = findNode(m_root, path.parentDir())) { removeFile(node, path); if (auto module = findModuleFor(node)) dirtyModules.insert(module); } } - for (auto module : dirtyModules) - m_writer->writeModuleCMakeFile(module, m_root); + createCMakeFiles(m_root); + createSourceFiles(); } bool CMakeGenerator::isQml(const Utils::FilePath &path) const @@ -282,9 +282,8 @@ NodePtr CMakeGenerator::findModuleFor(const NodePtr &node) const NodePtr CMakeGenerator::findNode(NodePtr &node, const Utils::FilePath &path) const { - const Utils::FilePath parentDir = path.parentDir(); for (NodePtr &child : node->subdirs) { - if (child->dir == parentDir) + if (child->dir == path) return child; if (path.isChildOf(child->dir)) return findNode(child, path); @@ -300,21 +299,34 @@ NodePtr CMakeGenerator::findOrCreateNode(NodePtr &node, const Utils::FilePath &p if (!path.isChildOf(node->dir)) return nullptr; - const Utils::FilePath parentDir = path.parentDir(); - const Utils::FilePath relative = parentDir.relativeChildPath(node->dir); + auto findSubDir = [](NodePtr &node, const Utils::FilePath &path) -> NodePtr { + for (NodePtr child : node->subdirs) { + if (child->dir == path) + return child; + } + return nullptr; + }; + + const Utils::FilePath relative = path.relativeChildPath(node->dir); const QChar separator = relative.pathComponentSeparator(); const QList components = relative.pathView().split(separator); - NodePtr last = node; + NodePtr lastNode = node; for (const auto &comp : components) { + + Utils::FilePath subPath = lastNode->dir.pathAppended(comp.toString()); + if (NodePtr sub = findSubDir(lastNode, subPath)) { + lastNode = sub; + continue; + } NodePtr newNode = std::make_shared(); - newNode->parent = last; + newNode->parent = lastNode; newNode->name = comp.toString(); - newNode->dir = last->dir.pathAppended(comp.toString()); - last->subdirs.push_back(newNode); - last = newNode; + newNode->dir = subPath; + lastNode->subdirs.push_back(newNode); + lastNode = newNode; } - return last; + return lastNode; } bool findFileWithGetter(const Utils::FilePath &file, const NodePtr &node, const FileGetter &getter) diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv1.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv1.cpp index 220d8622bfe..6d2d93a76b9 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv1.cpp +++ b/src/plugins/qmlprojectmanager/cmakegen/cmakewriterv1.cpp @@ -57,50 +57,53 @@ void CMakeWriterV1::writeRootCMakeFile(const NodePtr &node) const writeFile(componentPath, compTemplate); } - const Utils::FilePath file = node->dir.pathAppended("CMakeLists.txt"); - const QString appName = parent()->projectName() + "App"; - - QString fileSection = ""; - const QString configFile = getEnvironmentVariable(ENV_VARIABLE_CONTROLCONF); - if (!configFile.isEmpty()) - fileSection = QString("\t\t%1").arg(configFile); - - const QString fileTemplate = readTemplate(":/templates/cmakeroot_v1"); - const QString fileContent = fileTemplate.arg(appName, fileSection); - writeFile(file, fileContent); - const Utils::FilePath sharedFile = node->dir.pathAppended("CMakeLists.txt.shared"); - const QString sharedTemplate = readTemplate(":/templates/cmake_shared"); - writeFile(sharedFile, sharedTemplate); - - const Utils::FilePath userFile = node->dir.pathAppended("qds.cmake"); - QString userFileContent(DO_NOT_EDIT_FILE); - userFileContent.append(makeSubdirectoriesBlock(node)); - userFileContent.append("\n"); - - QString pluginNames; - std::vector plugs = plugins(node); - for (size_t i = 0; i < plugs.size(); ++i) { - pluginNames.append("\t" + plugs[i] + "plugin"); - if (i != plugs.size() - 1) - pluginNames.append("\n"); + if (!sharedFile.exists()) { + const QString sharedTemplate = readTemplate(":/templates/cmake_shared"); + writeFile(sharedFile, sharedTemplate); } - QString linkLibrariesTemplate( - "target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE\n" - "%1)"); + const Utils::FilePath file = node->dir.pathAppended("CMakeLists.txt"); + if (!file.exists()) { + const QString appName = parent()->projectName() + "App"; - userFileContent.append(linkLibrariesTemplate.arg(pluginNames)); + QString fileSection = ""; + const QString configFile = getEnvironmentVariable(ENV_VARIABLE_CONTROLCONF); + if (!configFile.isEmpty()) + fileSection = QString("\t\t%1").arg(configFile); - writeFile(userFile, userFileContent); + const QString fileTemplate = readTemplate(":/templates/cmakeroot_v1"); + const QString fileContent = fileTemplate.arg(appName, fileSection); + writeFile(file, fileContent); + } } void CMakeWriterV1::writeModuleCMakeFile(const NodePtr &node, const NodePtr &) const { QTC_ASSERT(parent(), return); - if (node->type == Node::Type::App) + if (node->type == Node::Type::App) { + const Utils::FilePath userFile = node->dir.pathAppended("qds.cmake"); + QString userFileContent(DO_NOT_EDIT_FILE); + userFileContent.append(makeSubdirectoriesBlock(node)); + userFileContent.append("\n"); + + QString pluginNames; + std::vector plugs = plugins(node); + for (size_t i = 0; i < plugs.size(); ++i) { + pluginNames.append("\t" + plugs[i] + "plugin"); + if (i != plugs.size() - 1) + pluginNames.append("\n"); + } + + QString linkLibrariesTemplate( + "target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE\n" + "%1)"); + + userFileContent.append(linkLibrariesTemplate.arg(pluginNames)); + writeFile(userFile, userFileContent); return; + } Utils::FilePath writeToFile = node->dir.pathAppended("CMakeLists.txt"); if (node->type == Node::Type::Folder && parent()->hasChildModule(node)) { From 8c23e1406e03f0236c515aef00173961d2c2bca3 Mon Sep 17 00:00:00 2001 From: Teea Poldsam Date: Tue, 16 Apr 2024 08:14:13 +0300 Subject: [PATCH 180/202] Doc: Update Adding flow items Fixes: QDS-11405 Change-Id: I1c6e1811a1e5db48e912159b817e2ba7edbc3761 Reviewed-by: Leena Miettinen --- .../images/studio-flow-item.png | Bin 12570 -> 0 bytes .../images/studio-flow-item.webp | Bin 0 -> 8150 bytes .../src/qtdesignstudio-app-flows.qdoc | 41 ++++++++---------- 3 files changed, 17 insertions(+), 24 deletions(-) delete mode 100644 doc/qtdesignstudio/images/studio-flow-item.png create mode 100644 doc/qtdesignstudio/images/studio-flow-item.webp diff --git a/doc/qtdesignstudio/images/studio-flow-item.png b/doc/qtdesignstudio/images/studio-flow-item.png deleted file mode 100644 index 905174d6002c663a4409d838ff5dca5dca590ba8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12570 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 S=uC;{)<4S+*PAmR;{gB=0Lk|N literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc index f335e1b2521..2c7564fe860 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc @@ -142,29 +142,26 @@ \title Adding Flow Items - After you create a \l{Adding Flow Views}{Flow View} component, you can - use a project wizard template to add a \uicontrol {Flow Item} component - for each screen in the UI. + After you create a \l{Adding Flow Views}{Flow View} component, use a project wizard + template to add a \uicontrol {Flow Item} component for each screen in the UI. If you \l{Importing 2D Assets}{imported} your screen designs from a design tool as individual \l{glossary-component}{components} - (\e {.ui.qml} files), you can use them as content for flow items. + (\e {.ui.qml} files), you can use them as content for flow items like any other components. The imported components are listed in \uicontrol Components > \uicontrol {My Components}. - If you are building your UI from scratch in \QDS, you must first add - components to the flow items to create the screens as you would any - components. For more information, see \l {Using Components}. The - flow items that you attach the components to are listed under + If you are building your UI from scratch in \QDS, add components to the flow items + first to create the screens as you would any components. For more information, see + \l {Using Components}. The flow items that you attach the components to are listed under \uicontrol {My Components}. - \image studio-flow-item.png "Custom Flow Item in Components" + \image studio-flow-item.webp "Custom Flow Item in Components" \note You must use the wizard to create the flow items. After you create a flow view, the \uicontrol {Flow View} module is added to - \uicontrol Components. It contains a \uicontrol {Flow Item} component that - you can use to \l{Applying States in Flows}{apply states to flow items}, and - that you should use solely for that purpose. + \uicontrol Components. It contains the \uicontrol {Flow Item} component for + \l{Applying States in Flows}{applying states to flow items}, and solely for that purpose. To add flow items: @@ -175,7 +172,7 @@ to create flow items for each screen in the UI. \li Add content to the flow item in one of the following ways: \list - \li Drag-and-drop components from \uicontrol Components to a + \li Drag components from \uicontrol Components to a flow item in the \l {2D} view or \l Navigator. \li Drag a screen from \uicontrol Components > \uicontrol {My Components} to a flow item in @@ -184,10 +181,9 @@ \li In \l Properties, edit the properties of each flow item. \endlist - You can now drag the flow items from \uicontrol Components - > \uicontrol {My Components} to the flow view in the \uicontrol {2D} - or \uicontrol Navigator view. When you have all the flow items in place, you can - \l{Adding Action Areas and Transitions}{add action areas} to them to create + Now, drag the flow items from \uicontrol Components > \uicontrol {My Components} to the + flow view in the \uicontrol {2D} or \uicontrol Navigator view. When you have all the flow + items in place, \l{Adding Action Areas and Transitions}{add action areas} to them to create transitions between them. \section1 Flow Item Properties @@ -203,23 +199,20 @@ properties are used to \l{Applying States in Flows}{apply states} in flows. - To include another flow view into a flow view, select the UI file (.ui.qml) + To include another flow view as a flow item into a flow view, select the UI file (.ui.qml) that specifies the flow view in the \uicontrol {Loader source} field. Usually, a flow item is inactive and invisible when it is not currently selected in the flow. Especially, all events from the flow item are ignored. To make a flow item always active, so that another flow item within it can respond to events and trigger the opening of a dialog, for example, - select the \uicontrol {Force active} check box. + select the \uicontrol {Force active} checkbox. - By default, transitions are drawn from action areas to the target flow item. + In the flow view, transitions are drawn from action areas to the target flow item by default. To draw the transitions from the edges of flow items instead, select the - \uicontrol {Join lines} check box in the \uicontrol {Transition Lines} + \uicontrol {Join lines} checkbox in the \uicontrol {Transition Lines} section. - In the \uicontrol Layout tab, you can use \l{Setting Anchors and Margins} - {anchors} to position the component. - In the \uicontrol Advanced section, you can manage the more \l{Specifying Developer Properties}{advanced properties} of components. */ From 873d10403c02e5d9a8c61b433d67a72e0e093241 Mon Sep 17 00:00:00 2001 From: Shrief Gabr Date: Wed, 10 Apr 2024 17:33:05 +0300 Subject: [PATCH 181/202] QmlDesigner: Fix Image, Url, and String datatype warnings Fixes: QDS-12157 Fixes: QDS-12160 Change-Id: Icc890e1cfda4d6d42de094c2b40f66f3d1d01039 Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Ali Kianian Reviewed-by: Miikka Heikkinen --- .../collectioneditor/collectiondetails.cpp | 65 +++++-------------- 1 file changed, 16 insertions(+), 49 deletions(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp index 9005882813d..bfe3ca59f53 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp @@ -55,28 +55,21 @@ public: /** * @brief getCustomUrl - * MimeType = * Address = * * @param value The input value to be evaluated - * @param dataType if the value is a valid url or image, the data type + * @param dataType if the value is a valid url, the data type * will be stored to this parameter, otherwise, it will be Unknown - * @param urlResult if the value is a valid url or image, the address + * @param urlResult if the value is a valid url, the address * will be stored in this parameter, otherwise it will be empty. - * @param subType if the value is a valid image, the image subtype - * will be stored in this parameter, otherwise it will be empty. - * @return true if the result is either url or image + * @return true if the result is url */ static bool getCustomUrl(const QString &value, CollectionDetails::DataType &dataType, - QUrl *urlResult = nullptr, - QString *subType = nullptr) + QUrl *urlResult = nullptr) { static const QRegularExpression urlRegex{ - "^(?" - "(?image)\\/" - "(?apng|avif|gif|jpeg|png|(?:svg\\+xml)|webp|xyz)\\:)?" // end of MimeType - "(?
    " + "^(?
    " "(?https?:\\/\\/" "(?:www\\.|(?!www))[A-z0-9][A-z0-9-]+[A-z0-9]\\.[^\\s]{2,}|www\\.[A-z0-9][A-z0-9-]+" "[A-z0-9]\\.[^\\s]{2,}|https?:\\/\\/" @@ -87,29 +80,18 @@ static bool getCustomUrl(const QString &value, }; const QRegularExpressionMatch match = urlRegex.match(value.trimmed()); - if (match.hasMatch()) { - if (match.hasCaptured("Address")) { - if (match.hasCaptured("MimeType") && match.captured("MainType") == "image") - dataType = CollectionDetails::DataType::Image; - else - dataType = CollectionDetails::DataType::Url; + if (match.hasCaptured("Address")) { + dataType = CollectionDetails::DataType::Url; - if (urlResult) - urlResult->setUrl(match.captured("Address")); + if (urlResult) + urlResult->setUrl(match.captured("Address")); - if (subType) - *subType = match.captured("SubType"); - - return true; - } + return true; } if (urlResult) urlResult->clear(); - if (subType) - subType->clear(); - dataType = CollectionDetails::DataType::Unknown; return false; } @@ -243,14 +225,8 @@ static QVariant valueToVariant(const QJsonValue &value, CollectionDetails::DataT return variantValue.toBool(); case DataType::Color: return variantValue.value(); - case DataType::Image: { - DataType type; - QUrl url; - if (getCustomUrl(variantValue.toString(), type, &url)) - return url; - return variantValue.toString(); - } case DataType::Url: + case DataType::Image: return variantValue.value(); default: return variantValue; @@ -280,12 +256,7 @@ static QJsonValue variantToJsonValue( return variant.toDouble(); case DataType::Integer: return variant.toInt(); - case DataType::Image: { - const QUrl url(variant.toUrl()); - if (url.isValid()) - return QString("image/xyz:%1").arg(url.toString()); - return {}; - } + case DataType::Image: case DataType::String: case DataType::Color: case DataType::Url: @@ -564,13 +535,6 @@ QVariant CollectionDetails::data(int row, int column) const const QJsonValue cellValue = d->dataRecords.at(row).at(column); - if (typeAt(column) == DataType::Image) { - const QUrl imageUrl = valueToVariant(cellValue, DataType::Image).toUrl(); - - if (imageUrl.isValid()) - return imageUrl; - } - return cellValue.toVariant(); } @@ -609,7 +573,10 @@ DataTypeWarning::Warning CollectionDetails::cellWarningCheck(int row, int column if (columnType == DataType::Unknown || isEmptyJsonValue(cellValue)) return DataTypeWarning::Warning::None; - if (columnType == DataType::Real && cellType == DataType::Integer) + if ((columnType == DataType::String || columnType == DataType::Real) && cellType == DataType::Integer) + return DataTypeWarning::Warning::None; + + if ((columnType == DataType::Url || columnType == DataType::Image) && cellType == DataType::String) return DataTypeWarning::Warning::None; if (columnType != cellType) From 10bcd931c6bc35e1333a614eabbba84a33583fba Mon Sep 17 00:00:00 2001 From: Teea Poldsam Date: Thu, 4 Apr 2024 15:05:20 +0300 Subject: [PATCH 182/202] Doc: Remove FAQ Fixes: QDS-8661 Change-Id: I107fe76afa37d17fa822c92eca59db5729d73c55 Reviewed-by: Mats Honkamaa Reviewed-by: Qt CI Patch Build Bot --- .../src/qtdesignstudio-faq.qdoc | 139 ------------------ .../src/qtdesignstudio-help-overview.qdoc | 6 +- .../src/qtdesignstudio-toc.qdoc | 1 - doc/qtdesignstudio/src/qtdesignstudio.qdoc | 1 - 4 files changed, 1 insertion(+), 146 deletions(-) delete mode 100644 doc/qtdesignstudio/src/qtdesignstudio-faq.qdoc diff --git a/doc/qtdesignstudio/src/qtdesignstudio-faq.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-faq.qdoc deleted file mode 100644 index 8f7267e0d3d..00000000000 --- a/doc/qtdesignstudio/src/qtdesignstudio-faq.qdoc +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only - -/*! - \previouspage creator-how-to-get-help.html - \page studio-faq.html - \nextpage studio-platforms.html - - \title Frequently Asked Questions - - This section contains answers to some frequently asked questions about \QDS - grouped by categories. You might also find answers to your questions in the - product documentation by searching or browsing the index in the - \l{Get help}{Help mode}. Many questions are also answered by the - \l{Examples}{examples} and \l{Tutorials}{video tutorials}. - - \list - \li \l {FAQ - \QB}{\QB} - \li \l {FAQ - Assets}{Assets} - \li \l {FAQ - Components}{Components} - \li \l {FAQ - Views}{Views} - \li \l {FAQ - Integration Between \QDS and Qt Creator}{Integration Between \QDS and Qt Creator} - \li \l {FAQ - Performance}{Performance} - \li \l {FAQ - Data Simulation}{Data Simulation} - \endlist - - \section1 FAQ - \QB - - \section2 How does \QBPS differ from \QBSK and \QBF? - - \QBPS, \QBSK, and \QBF are functionally similar. The biggest difference - between the tools is that \QBSK and \QBF can export .svg (vector), .png, and - .jpeg files, while \QBPS only supports .png and .jpeg. Adobe Illustrator - users can port their designs into Photoshop, but they must be rasterized - into \e {smart objects}. - - For more information, see \l {Exporting from Design Tools}. - - \section2 Do I need to copy the .qml files in the resource folder after each design modification? - - No you don't. When you add new or modified .metadata files to your project - from Photoshop, \QBPS, \QBSK, or \QBF, select the \uicontrol {Merge QML} - check box in the \uicontrol {Asset Import} dialog to merge the changes into - existing QML files instead of overwriting them. - - For more information, see \l {Importing 2D Assets}. - - \section2 Where can I find log files generated by \QB while exporting metadata? - - On Windows, the logs are stored inside the temp folder in - \c {C:\Users\\AppData\Local\Temp}. The log files are named as - \e csxs-.log. Please note that you might have to set - the log level to generate logs. Also note the CEP version while setting the - log level. The CEP version depends on the Photoshop version you are using. - Currently the latest version is version 10. - - \section1 FAQ - Assets - - \section2 Can I import my organization's preferred font in \QDS? - - Yes, you can import your custom fonts, for example, in .ttf or .otf formats. - Fonts installed on your system will be available to use in your imported - designs. If you need to deploy the device, you will have to import the font - into the project. - - For more information, see \l {Using Custom Fonts}. - - \section1 FAQ - Components - - \section2 Can I use custom components? - - Yes, you can create custom components and controls by using wizard templates - or move component instances into separate files to turn them into new - components that you can create instances of. For more information, see - \l {Using Components}. - - \section2 What are the 3D import formats for \QDS? - - You can import files stored in several widely-used formats, such as .fbx, - .obj, .gltf, .glb, .blend, .dae, .uia, and .uip. - - For more information, see \l {Importing 3D Assets}. - - \section2 How can I integrate custom C++ components into QDS? - - You must create your own QML module that contains the components and - provides additional information about your components. For more information, - see \l {Using QML Modules with Plugins}. - - \section1 FAQ - Views - - \section2 What are the keyboard shortcuts for moving around in the \uicontrol{3D} view? - - \list - \li To pan: \key Alt + middle mouse button - \li To orbit (rotate): \key Alt (or \key Option on \macos) + left mouse button - \li To zoom: \key Alt + right mouse button - \endlist - - For more information, see the \l {3D} view. - - \section1 FAQ - Integration Between \QDS and Qt Creator - - \section2 Can I automatically propagate name changes between \QDS and Qt Creator? - - Unfortunately we do not automate renaming files between tools at the moment. - If you decide to change the name of a property, alias, or signal in \QDS, - you need to manually change the name in Qt Creator to maintain the connection. - However, you can rename symbols in all files within a project. To rename a - QML type in a project, select \uicontrol Tools > \uicontrol QML/JS > - \uicontrol {Rename Symbol Under Cursor} or press \key Ctrl+Shift+R. For more - information, see \l {Rename symbols}. - - \section2 How can I add .qml files to my project in Qt Creator? - - Use the project wizard templates to create an application in \QDS and copy - your .qml files to the project folder. Then make some changes to the project - configuration and source files, as instructed in - \l {Converting UI Projects to Applications}. - - \section1 FAQ - Performance - - \section2 Will my application with 3D components run at 60 FPS? - - With the ability to test the full (2D/3D) UI in \QDS on target hardware, - you will quickly be able to determine if a 3D object is causing performance - issues using the \uicontrol FPS field in the \uicontrol Design mode. - \uicontrol FPS displays the frames-per-second (FPS) refresh rate of - previewed animations. - - See \l {Optimizing Your 3D Scene} to learn how you can enhance the - performance by optimizing your scene. - - \section1 FAQ - Data Simulation - - \section2 Can I automatically generate dummy data? - No, this is not supported at the moment. For more information about creating - the data manually, see \l {Loading Placeholder Data}. - */ diff --git a/doc/qtdesignstudio/src/qtdesignstudio-help-overview.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-help-overview.qdoc index be9e6b4ed55..36fbc8c0155 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-help-overview.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-help-overview.qdoc @@ -11,8 +11,7 @@ \table \row \li \image qds-front-help.png - \li Learn more about using the \uicontrol Help mode, frequently - asked questions, and supported platforms. + \li Learn more about using the \uicontrol Help mode and supported platforms. \endtable \list @@ -23,9 +22,6 @@ and index functions to find particular topics in the helps, or request context-sensitive help by pressing \key F1 in the Design mode. - \li \l{Frequently Asked Questions} - - Contains answers to some frequently asked questions about \QDS. \li \l{Supported Platforms} You can install and run \QDS on several operating systems to design diff --git a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc index e86bd693299..3ca30792af9 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc @@ -262,7 +262,6 @@ \li \l {Search from documentation} \li \l {Select the help start page} \endlist - \li \l{Frequently Asked Questions} \li \l{Supported Platforms} \endlist \li \l{Technical Support} diff --git a/doc/qtdesignstudio/src/qtdesignstudio.qdoc b/doc/qtdesignstudio/src/qtdesignstudio.qdoc index 6d844746057..6aab55a1ad5 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio.qdoc @@ -86,7 +86,6 @@ \li \b {\l Help} \list \li \l{Get help}{Getting Help} - \li \l{Frequently Asked Questions} \li \l{Supported Platforms} \endlist \row From ec88d279a8d52189685008c8fe46371d13d2505f Mon Sep 17 00:00:00 2001 From: David Schulz Date: Mon, 22 Apr 2024 10:18:09 +0200 Subject: [PATCH 183/202] TextEditor: Optimize unindent backspace behavior MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only remove more than one character when the cursor is inside the indentation in the beginning of the line. In all other cases always only remove one character. Fixes: QTCREATORBUG-30725 Change-Id: I973101a95768cdd8b1a318972f53423eb72eb157 Reviewed-by: Christian Stenger Reviewed-by: AndrĂ© Hartmann Reviewed-by: --- src/plugins/texteditor/texteditor.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index 3256ee91b79..280ca8e433f 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -6876,8 +6876,7 @@ void TextEditorWidgetPrivate::handleBackspaceKey() } } } else if (typingSettings.m_smartBackspaceBehavior == TypingSettings::BackspaceUnindents) { - const QChar previousChar = q->document()->characterAt(pos - 1); - if (!(previousChar == QLatin1Char(' ') || previousChar == QLatin1Char('\t'))) { + if (c.positionInBlock() > TabSettings::firstNonSpace(c.block().text())) { if (cursorWithinSnippet) c.beginEditBlock(); c.deletePreviousChar(); From 64ed6fff6d68461d3e43d39ba8f6c3d1f3b29f2a Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 22 Apr 2024 13:57:30 +0300 Subject: [PATCH 184/202] QmlDesigner: Select model under cursor when using "Edit in 3D View" If selection context contains valid scene position, use that to select the model under cursor. Fixes: QDS-12346 Change-Id: I55c58ed303e1746b7b234fa6ac31272fdfafb40d Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../components/componentcore/modelnodeoperations.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index 66c1229a76e..a5274c70e2d 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -1688,7 +1688,14 @@ void editIn3dView(const SelectionContext &selectionContext) if (selectionContext.view() && selectionContext.hasSingleSelectedModelNode() && selectionContext.currentSingleSelectedNode().metaInfo().isQtQuick3DView3D()) { QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("Editor3D", true); - selectionContext.view()->emitView3DAction(View3DActionType::AlignViewToCamera, true); + const QPointF scenePos = selectionContext.scenePosition(); + if (scenePos.isNull()) { + selectionContext.view()->emitView3DAction(View3DActionType::AlignViewToCamera, true); + } else { + selectionContext.view()->emitCustomNotification("pick_3d_node_from_2d_scene", + {selectionContext.currentSingleSelectedNode()}, + {scenePos}); + } } } From 3292745a0aa2ffd221b958f36cbef197c8ff2ef6 Mon Sep 17 00:00:00 2001 From: Alessandro Portale Date: Thu, 18 Apr 2024 18:46:20 +0200 Subject: [PATCH 185/202] Android: Give Android 14.0 the code name The name "UpsideDownCake" for Android 14.0 has been revealed a while ago. Align its package presentation to Android Studio. Task-number: QTCREATORBUG-30711 Change-Id: I7ca42ca73c7bdf5b7732bab2d2bd36f7a730ccb7 Reviewed-by: Jarek Kobus --- src/plugins/android/androidmanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/android/androidmanager.cpp b/src/plugins/android/androidmanager.cpp index b95b6bb4dd1..bd230c76af7 100644 --- a/src/plugins/android/androidmanager.cpp +++ b/src/plugins/android/androidmanager.cpp @@ -532,7 +532,7 @@ QString androidNameForApiLevel(int x) case 33: return QLatin1String("Android 13.0 (\"Tiramisu\")"); case 34: - return QLatin1String("Android API 34"); + return QLatin1String("Android 14.0 (\"UpsideDownCake\")"); default: return Tr::tr("Unknown Android version. API Level: %1").arg(x); } From 1d66a5585942a71613031d8a159ff71996181258 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Fri, 19 Apr 2024 22:30:08 +0200 Subject: [PATCH 186/202] GoogleTest: Update to v1.14 Change-Id: I8773824675ed9ed58f4aa191d67dea032ddc533b Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- src/libs/3rdparty/googletest | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/3rdparty/googletest b/src/libs/3rdparty/googletest index b796f7d4468..f8d7d77c069 160000 --- a/src/libs/3rdparty/googletest +++ b/src/libs/3rdparty/googletest @@ -1 +1 @@ -Subproject commit b796f7d44681514f58a683a3a71ff17c94edb0c1 +Subproject commit f8d7d77c06936315286eb55f8de22cd23c188571 From dad555a088062f585e2c15370d8fe8963801358e Mon Sep 17 00:00:00 2001 From: Shrief Gabr Date: Wed, 17 Apr 2024 10:34:17 +0300 Subject: [PATCH 187/202] QmlDesigner: Fix save indicator behavior on selection change Task-number: QDS-12499 Change-Id: If91aab8d133a9269d9fc381ea0a130d3953aa69d Reviewed-by: Ali Kianian Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../CollectionDetailsToolbar.qml | 15 +++------------ .../collectioneditor/collectiondetailsmodel.cpp | 13 +++++++++++++ .../collectioneditor/collectiondetailsmodel.h | 4 ++++ 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml index 852341b9d5d..e7997f7eae6 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml @@ -17,7 +17,6 @@ Rectangle { required property var model required property var backend property int selectedRow: -1 - property bool hasUnsavedChanges: false implicitHeight: StudioTheme.Values.toolbarHeight color: StudioTheme.Values.themeToolbarBackground @@ -35,14 +34,6 @@ Rectangle { fileDialog.reject() } - Connections { - target: root.model - - function onDataChanged() { - hasUnsavedChanges = true - } - } - RowLayout { id: container @@ -131,8 +122,8 @@ Rectangle { buttonIcon: StudioTheme.Constants.save_medium tooltip: qsTr("Save changes") - enabled: root.model.collectionName !== "" && root.hasUnsavedChanges - onClicked: hasUnsavedChanges = !root.model.saveDataStoreCollections() + enabled: root.model.collectionName !== "" && root.model.hasUnsavedChanges + onClicked: root.model.saveDataStoreCollections() Rectangle { width: StudioTheme.Values.smallStatusIndicatorDiameter @@ -140,7 +131,7 @@ Rectangle { radius: StudioTheme.Values.smallStatusIndicatorDiameter / 2 anchors.right: parent.right anchors.top: parent.top - visible: hasUnsavedChanges + visible: root.model.hasUnsavedChanges color: StudioTheme.Values.themeIconColorSelected } } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp index 1643dfc23e2..d2917ec3022 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp @@ -93,6 +93,7 @@ bool CollectionDetailsModel::setData(const QModelIndex &index, const QVariant &v if (prevWarning != m_currentCollection.cellWarningCheck(index.row(), index.column())) roles << DataTypeWarningRole; + setHasUnsavedChanges(true); emit dataChanged(index, index, roles); } @@ -131,6 +132,7 @@ bool CollectionDetailsModel::insertRows(int row, int count, [[maybe_unused]] con beginInsertRows({}, row, row + count - 1); m_currentCollection.insertEmptyRows(row, count); endInsertRows(); + setHasUnsavedChanges(true); return true; } @@ -247,6 +249,7 @@ bool CollectionDetailsModel::addColumn(int column, const QString &name, const QS {}, CollectionDataTypeModel::dataTypeFromString(propertyType)); endInsertColumns(); + setHasUnsavedChanges(true); return m_currentCollection.containsPropertyName(name); } @@ -302,6 +305,7 @@ bool CollectionDetailsModel::setPropertyType(int column, const QString &newValue {Qt::DisplayRole, Qt::EditRole, DataTypeRole, DataTypeWarningRole, ColumnDataTypeRole}); } + setHasUnsavedChanges(true); return changed; } @@ -434,6 +438,7 @@ bool CollectionDetailsModel::saveDataStoreCollections() if (reference != currentReference) closeCollectionIfSaved(reference); } + setHasUnsavedChanges(false); return true; } } @@ -611,4 +616,12 @@ QString CollectionDetailsModel::warningToString(DataTypeWarning::Warning warning return DataTypeWarning::getDataTypeWarningString(warning); } +void CollectionDetailsModel::setHasUnsavedChanges(bool val) +{ + if (m_hasUnsavedChanges == val) + return; + m_hasUnsavedChanges = val; + emit hasUnsavedChangesChanged(); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h index 24a040cce6c..8844ff4a3ef 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h @@ -20,6 +20,7 @@ class CollectionDetailsModel : public QAbstractTableModel Q_PROPERTY(int selectedColumn READ selectedColumn WRITE selectColumn NOTIFY selectedColumnChanged) Q_PROPERTY(int selectedRow READ selectedRow WRITE selectRow NOTIFY selectedRowChanged) Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) + Q_PROPERTY(bool hasUnsavedChanges MEMBER m_hasUnsavedChanges WRITE setHasUnsavedChanges NOTIFY hasUnsavedChangesChanged) public: enum DataRoles { SelectedRole = Qt::UserRole + 1, DataTypeRole, ColumnDataTypeRole, DataTypeWarningRole }; @@ -70,12 +71,14 @@ public: const CollectionDetails upToDateConstCollection(const CollectionReference &reference) const; bool collectionHasColumn(const CollectionReference &reference, const QString &columnName) const; QString getFirstColumnName(const CollectionReference &reference) const; + void setHasUnsavedChanges(bool val); signals: void collectionNameChanged(const QString &collectionName); void selectedColumnChanged(int); void selectedRowChanged(int); void isEmptyChanged(bool); + void hasUnsavedChangesChanged(); void warning(const QString &title, const QString &body); private slots: @@ -93,6 +96,7 @@ private: QHash m_openedCollections; CollectionDetails m_currentCollection; bool m_isEmpty = true; + bool m_hasUnsavedChanges = false; int m_selectedColumn = -1; int m_selectedRow = -1; From bb6110082a51db2a0c9bea8ca9390840b6c4c431 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Mon, 22 Apr 2024 14:48:33 +0200 Subject: [PATCH 188/202] QmlDesigner: Hide visible CheckBox EffectsSection Hide the visible CheckBox in the EffectsSection when there is no effect currently added to the item. Change-Id: I68dede067a92bf5f733c582424ed5b2c2745e651 Reviewed-by: Thomas Hartmann --- .../propertyEditorQmlSources/QtQuick/EffectsSection.qml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml index 8b1d201c048..b640d82d0ba 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml @@ -75,9 +75,14 @@ Section { } } - PropertyLabel { text: qsTr("Visibility") } + PropertyLabel { + text: qsTr("Visibility") + visible: root.hasDesignerEffect + } SecondColumnLayout { + visible: root.hasDesignerEffect + CheckBox { text: qsTr("Visible") implicitWidth: StudioTheme.Values.twoControlColumnWidth @@ -90,6 +95,7 @@ Section { } Item { + visible: root.hasDesignerEffect width: 1 height: StudioTheme.Values.sectionHeadSpacerHeight } @@ -444,6 +450,7 @@ Section { } Item { + visible: root.hasDesignerEffect width: 1 height: StudioTheme.Values.sectionHeadSpacerHeight } From d83e7d3d3d6c4f436a774e217d42d5a0c275517b Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Mon, 22 Apr 2024 17:09:16 +0200 Subject: [PATCH 189/202] QmlDesigner: Add proper ranges in EffectsSection Change-Id: If3f8b395c9a63c535f43be84d7a855196c67769e Reviewed-by: Thomas Hartmann --- .../QtQuick/EffectsSection.qml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml index b640d82d0ba..056bd5fd602 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml @@ -213,6 +213,8 @@ Section { implicitWidth: StudioTheme.Values.twoControlColumnWidth + StudioTheme.Values.actionIndicatorWidth backendValue: root.effectNodeWrapper.properties.layerBlurRadius + minimumValue: 0 + maximumValue: 250 } ExpandingSpacer {} @@ -252,6 +254,8 @@ Section { implicitWidth: StudioTheme.Values.twoControlColumnWidth + StudioTheme.Values.actionIndicatorWidth backendValue: root.effectNodeWrapper.properties.backgroundBlurRadius + minimumValue: 0 + maximumValue: 250 } ExpandingSpacer {} @@ -376,6 +380,8 @@ Section { implicitWidth: StudioTheme.Values.twoControlColumnWidth + StudioTheme.Values.actionIndicatorWidth backendValue: delegate.wrapper.properties.blur + minimumValue: 0 + maximumValue: 250 } ExpandingSpacer {} @@ -392,6 +398,8 @@ Section { + StudioTheme.Values.actionIndicatorWidth backendValue: delegate.wrapper.properties.spread enabled: modelNodeBackend.isInstanceOf("Rectangle") + minimumValue: -2048 + maximumValue: 2048 } ExpandingSpacer {} @@ -414,8 +422,8 @@ Section { implicitWidth: StudioTheme.Values.twoControlColumnWidth + StudioTheme.Values.actionIndicatorWidth backendValue: delegate.wrapper.properties.offsetX - maximumValue: 0xffff minimumValue: -0xffff + maximumValue: 0xffff } Spacer { implicitWidth: StudioTheme.Values.controlLabelGap } @@ -431,8 +439,8 @@ Section { implicitWidth: StudioTheme.Values.twoControlColumnWidth + StudioTheme.Values.actionIndicatorWidth backendValue: delegate.wrapper.properties.offsetY - maximumValue: 0xffff minimumValue: -0xffff + maximumValue: 0xffff } Spacer { implicitWidth: StudioTheme.Values.controlLabelGap } From e9ee1c6b567eb9a68be887254733ec5c1080c169 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 22 Apr 2024 17:01:53 +0200 Subject: [PATCH 190/202] QmlDesigner: Do not use property as default property name property is a keyword and a bad default. Change-Id: Ieb38f08f5f049fe6b540304945ce95ae6c18b55e Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- .../components/propertyeditor/dynamicpropertiesproxymodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp b/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp index 6fb45cc9250..fdd58c77a3c 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp @@ -143,7 +143,7 @@ QString DynamicPropertiesProxyModel::newPropertyName() const { DynamicPropertiesModel *propsModel = dynamicPropertiesModel(); - return QString::fromUtf8(uniquePropertyName("property", propsModel->singleSelectedNode())); + return QString::fromUtf8(uniquePropertyName("newName", propsModel->singleSelectedNode())); } void DynamicPropertiesProxyModel::createProperty(const QString &name, const QString &type) From e262ec1ebbb51402fdd8583e51f131ee5f04e3a6 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 3 Apr 2024 16:38:23 +0200 Subject: [PATCH 191/202] QmlDesigner: Integrate item library entries from project storage Task-number: QDS-12102 Change-Id: Id6fbfcfb44d3b8c290f5e5d74addf33ef4d9a5e5 Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../itemLibrary/images/ambient-sound-16.png | Bin 0 -> 315 bytes .../itemLibrary/images/ambient-sound-24.png | Bin 0 -> 514 bytes .../images/ambient-sound-24@2x.png | Bin 0 -> 1846 bytes .../images/animated-image-icon.png | Bin 0 -> 298 bytes .../images/animated-image-icon16.png | Bin 0 -> 211 bytes .../images/animated-image-icon@2x.png | Bin 0 -> 342 bytes .../images/animatedsprite-loading.png | Bin 0 -> 118 bytes .../itemLibrary/images/audio-engine-16.png | Bin 0 -> 363 bytes .../itemLibrary/images/audio-engine-24.png | Bin 0 -> 472 bytes .../itemLibrary/images/audio-engine-24@2x.png | Bin 0 -> 803 bytes .../itemLibrary/images/audio-listener-16.png | Bin 0 -> 311 bytes .../itemLibrary/images/audio-listener-24.png | Bin 0 -> 924 bytes .../images/audio-listener-24@2x.png | Bin 0 -> 1691 bytes .../itemLibrary/images/audio-output-16px.png | Bin 0 -> 359 bytes .../itemLibrary/images/audio-output-24px.png | Bin 0 -> 595 bytes .../images/audio-output-24px@2x.png | Bin 0 -> 1129 bytes .../itemLibrary/images/audio-room-16.png | Bin 0 -> 276 bytes .../itemLibrary/images/audio-room-24.png | Bin 0 -> 419 bytes .../itemLibrary/images/audio-room-24@2x.png | Bin 0 -> 878 bytes .../itemLibrary/images/border-image-icon.png | Bin 0 -> 299 bytes .../images/border-image-icon16.png | Bin 0 -> 228 bytes .../images/border-image-icon@2x.png | Bin 0 -> 386 bytes .../itemLibrary/images/busyindicator-icon.png | Bin 0 -> 320 bytes .../images/busyindicator-icon16.png | Bin 0 -> 229 bytes .../images/busyindicator-icon@2x.png | Bin 0 -> 643 bytes .../itemLibrary/images/button-icon.png | Bin 0 -> 162 bytes .../itemLibrary/images/button-icon16.png | Bin 0 -> 145 bytes .../itemLibrary/images/button-icon@2x.png | Bin 0 -> 259 bytes .../itemLibrary/images/checkbox-icon.png | Bin 0 -> 258 bytes .../itemLibrary/images/checkbox-icon16.png | Bin 0 -> 230 bytes .../itemLibrary/images/checkbox-icon@2x.png | Bin 0 -> 336 bytes .../images/column-positioner-icon-16px.png | Bin 0 -> 113 bytes .../images/column-positioner-icon.png | Bin 0 -> 118 bytes .../images/column-positioner-icon@2x.png | Bin 0 -> 121 bytes .../itemLibrary/images/combobox-icon.png | Bin 0 -> 156 bytes .../itemLibrary/images/combobox-icon16.png | Bin 0 -> 155 bytes .../itemLibrary/images/combobox-icon@2x.png | Bin 0 -> 185 bytes .../itemLibrary/images/component-icon.png | Bin 0 -> 626 bytes .../itemLibrary/images/component-icon16.png | Bin 0 -> 438 bytes .../itemLibrary/images/component-icon@2x.png | Bin 0 -> 1107 bytes .../itemLibrary/images/control-icon.png | Bin 0 -> 293 bytes .../itemLibrary/images/control-icon16.png | Bin 0 -> 229 bytes .../itemLibrary/images/control-icon@2x.png | Bin 0 -> 509 bytes .../itemLibrary/images/default-icon.png | Bin 0 -> 813 bytes .../itemLibrary/images/default3d.png | Bin 0 -> 375 bytes .../itemLibrary/images/default3d16.png | Bin 0 -> 253 bytes .../itemLibrary/images/default3d@2x.png | Bin 0 -> 499 bytes .../itemLibrary/images/delaybutton-icon.png | Bin 0 -> 189 bytes .../itemLibrary/images/delaybutton-icon16.png | Bin 0 -> 160 bytes .../images/delaybutton-icon@2x.png | Bin 0 -> 286 bytes .../itemLibrary/images/dial-icon.png | Bin 0 -> 267 bytes .../itemLibrary/images/dial-icon16.png | Bin 0 -> 243 bytes .../itemLibrary/images/dial-icon@2x.png | Bin 0 -> 505 bytes .../itemLibrary/images/drop-area-16px.png | Bin 0 -> 205 bytes .../itemLibrary/images/drop-area-24px.png | Bin 0 -> 490 bytes .../itemLibrary/images/drop-area-24px@2x.png | Bin 0 -> 638 bytes .../images/extended-view3d-16px.png | Bin 0 -> 311 bytes .../images/extended-view3d-24px.png | Bin 0 -> 384 bytes .../images/extended-view3d-24px@2x.png | Bin 0 -> 674 bytes .../itemLibrary/images/flickable-icon.png | Bin 0 -> 246 bytes .../itemLibrary/images/flickable-icon16.png | Bin 0 -> 209 bytes .../itemLibrary/images/flickable-icon@2x.png | Bin 0 -> 322 bytes .../itemLibrary/images/flipable-icon.png | Bin 0 -> 678 bytes .../itemLibrary/images/flipable-icon16.png | Bin 0 -> 466 bytes .../images/flow-positioner-icon-16px.png | Bin 0 -> 98 bytes .../images/flow-positioner-icon.png | Bin 0 -> 101 bytes .../images/flow-positioner-icon@2x.png | Bin 0 -> 129 bytes .../itemLibrary/images/focusscope-icon.png | Bin 0 -> 143 bytes .../itemLibrary/images/focusscope-icon16.png | Bin 0 -> 161 bytes .../itemLibrary/images/focusscope-icon@2x.png | Bin 0 -> 182 bytes .../itemLibrary/images/frame-icon.png | Bin 0 -> 121 bytes .../itemLibrary/images/frame-icon16.png | Bin 0 -> 117 bytes .../itemLibrary/images/frame-icon@2x.png | Bin 0 -> 125 bytes .../images/grid-positioner-icon-16px.png | Bin 0 -> 93 bytes .../images/grid-positioner-icon.png | Bin 0 -> 97 bytes .../images/grid-positioner-icon@2x.png | Bin 0 -> 125 bytes .../itemLibrary/images/gridview-icon.png | Bin 0 -> 127 bytes .../itemLibrary/images/gridview-icon16.png | Bin 0 -> 102 bytes .../itemLibrary/images/gridview-icon@2x.png | Bin 0 -> 137 bytes .../itemLibrary/images/groupbox-icon.png | Bin 0 -> 133 bytes .../itemLibrary/images/groupbox-icon16.png | Bin 0 -> 125 bytes .../itemLibrary/images/groupbox-icon@2x.png | Bin 0 -> 136 bytes .../itemLibrary/images/image-icon.png | Bin 0 -> 434 bytes .../itemLibrary/images/image-icon16.png | Bin 0 -> 296 bytes .../itemLibrary/images/image-icon@2x.png | Bin 0 -> 596 bytes .../itemLibrary/images/item-icon.png | Bin 0 -> 148 bytes .../itemLibrary/images/item-icon16.png | Bin 0 -> 135 bytes .../itemLibrary/images/item-icon@2x.png | Bin 0 -> 167 bytes .../itemLibrary/images/itemdelegate-icon.png | Bin 0 -> 127 bytes .../images/itemdelegate-icon16.png | Bin 0 -> 124 bytes .../images/itemdelegate-icon@2x.png | Bin 0 -> 133 bytes .../itemLibrary/images/keyframe-16px.png | Bin 0 -> 190 bytes .../itemLibrary/images/label-icon.png | Bin 0 -> 206 bytes .../itemLibrary/images/label-icon16.png | Bin 0 -> 182 bytes .../itemLibrary/images/label-icon@2x.png | Bin 0 -> 284 bytes .../itemLibrary/images/listview-icon.png | Bin 0 -> 148 bytes .../itemLibrary/images/listview-icon16.png | Bin 0 -> 136 bytes .../itemLibrary/images/listview-icon@2x.png | Bin 0 -> 158 bytes .../itemLibrary/images/loader-icon.png | Bin 0 -> 321 bytes .../itemLibrary/images/loader-icon16.png | Bin 0 -> 222 bytes .../itemLibrary/images/loader-icon@2x.png | Bin 0 -> 483 bytes .../itemLibrary/images/media-player-16px.png | Bin 0 -> 148 bytes .../itemLibrary/images/media-player-24px.png | Bin 0 -> 179 bytes .../images/media-player-24px@2x.png | Bin 0 -> 260 bytes .../itemLibrary/images/mouse-area-icon.png | Bin 0 -> 358 bytes .../itemLibrary/images/mouse-area-icon16.png | Bin 0 -> 263 bytes .../itemLibrary/images/mouse-area-icon@2x.png | Bin 0 -> 755 bytes .../itemLibrary/images/page-icon.png | Bin 0 -> 190 bytes .../itemLibrary/images/page-icon16.png | Bin 0 -> 148 bytes .../itemLibrary/images/page-icon@2x.png | Bin 0 -> 195 bytes .../itemLibrary/images/pageindicator-icon.png | Bin 0 -> 179 bytes .../images/pageindicator-icon16.png | Bin 0 -> 158 bytes .../images/pageindicator-icon@2x.png | Bin 0 -> 207 bytes .../itemLibrary/images/pane-icon.png | Bin 0 -> 93 bytes .../itemLibrary/images/pane-icon16.png | Bin 0 -> 92 bytes .../itemLibrary/images/pane-icon@2x.png | Bin 0 -> 96 bytes .../itemLibrary/images/pathview-icon.png | Bin 0 -> 457 bytes .../itemLibrary/images/pathview-icon16.png | Bin 0 -> 320 bytes .../itemLibrary/images/pathview-icon@2x.png | Bin 0 -> 864 bytes .../itemLibrary/images/progressbar-icon.png | Bin 0 -> 101 bytes .../itemLibrary/images/progressbar-icon16.png | Bin 0 -> 92 bytes .../images/progressbar-icon@2x.png | Bin 0 -> 127 bytes .../itemLibrary/images/radiobutton-icon.png | Bin 0 -> 279 bytes .../itemLibrary/images/radiobutton-icon16.png | Bin 0 -> 218 bytes .../images/radiobutton-icon@2x.png | Bin 0 -> 482 bytes .../itemLibrary/images/rangeslider-icon.png | Bin 0 -> 269 bytes .../itemLibrary/images/rangeslider-icon16.png | Bin 0 -> 231 bytes .../images/rangeslider-icon@2x.png | Bin 0 -> 282 bytes .../itemLibrary/images/rect-icon.png | Bin 0 -> 169 bytes .../itemLibrary/images/rect-icon16.png | Bin 0 -> 135 bytes .../itemLibrary/images/rect-icon@2x.png | Bin 0 -> 237 bytes .../itemLibrary/images/repeater-icon.png | Bin 0 -> 191 bytes .../itemLibrary/images/repeater-icon16.png | Bin 0 -> 187 bytes .../itemLibrary/images/repeater-icon@2x.png | Bin 0 -> 196 bytes .../itemLibrary/images/roundbutton-icon.png | Bin 0 -> 229 bytes .../itemLibrary/images/roundbutton-icon16.png | Bin 0 -> 186 bytes .../images/roundbutton-icon@2x.png | Bin 0 -> 381 bytes .../images/row-positioner-icon-16px.png | Bin 0 -> 115 bytes .../images/row-positioner-icon.png | Bin 0 -> 120 bytes .../images/row-positioner-icon@2x.png | Bin 0 -> 126 bytes .../itemLibrary/images/scrollview-icon.png | Bin 0 -> 110 bytes .../itemLibrary/images/scrollview-icon16.png | Bin 0 -> 116 bytes .../itemLibrary/images/scrollview-icon@2x.png | Bin 0 -> 145 bytes .../itemLibrary/images/slider-icon.png | Bin 0 -> 190 bytes .../itemLibrary/images/slider-icon16.png | Bin 0 -> 156 bytes .../itemLibrary/images/slider-icon@2x.png | Bin 0 -> 227 bytes .../itemLibrary/images/spatial-audio-16.png | Bin 0 -> 319 bytes .../itemLibrary/images/spatial-audio-24.png | Bin 0 -> 664 bytes .../images/spatial-audio-24@2x.png | Bin 0 -> 1536 bytes .../itemLibrary/images/spinbox-icon.png | Bin 0 -> 144 bytes .../itemLibrary/images/spinbox-icon16.png | Bin 0 -> 151 bytes .../itemLibrary/images/spinbox-icon@2x.png | Bin 0 -> 178 bytes .../itemLibrary/images/stackview-icon.png | Bin 0 -> 162 bytes .../itemLibrary/images/stackview-icon16.png | Bin 0 -> 151 bytes .../itemLibrary/images/stackview-icon@2x.png | Bin 0 -> 167 bytes .../itemLibrary/images/swipeview-icon.png | Bin 0 -> 163 bytes .../itemLibrary/images/swipeview-icon16.png | Bin 0 -> 152 bytes .../itemLibrary/images/swipeview-icon@2x.png | Bin 0 -> 184 bytes .../itemLibrary/images/switch-icon.png | Bin 0 -> 205 bytes .../itemLibrary/images/switch-icon16.png | Bin 0 -> 160 bytes .../itemLibrary/images/switch-icon@2x.png | Bin 0 -> 314 bytes .../itemLibrary/images/text-edit-icon.png | Bin 0 -> 150 bytes .../itemLibrary/images/text-edit-icon16.png | Bin 0 -> 169 bytes .../itemLibrary/images/text-edit-icon@2x.png | Bin 0 -> 193 bytes .../itemLibrary/images/text-icon.png | Bin 0 -> 126 bytes .../itemLibrary/images/text-icon16.png | Bin 0 -> 141 bytes .../itemLibrary/images/text-icon@2x.png | Bin 0 -> 156 bytes .../itemLibrary/images/text-input-icon.png | Bin 0 -> 158 bytes .../itemLibrary/images/text-input-icon16.png | Bin 0 -> 140 bytes .../itemLibrary/images/text-input-icon@2x.png | Bin 0 -> 170 bytes .../itemLibrary/images/textarea-icon.png | Bin 0 -> 149 bytes .../itemLibrary/images/textarea-icon16.png | Bin 0 -> 133 bytes .../itemLibrary/images/textarea-icon@2x.png | Bin 0 -> 163 bytes .../itemLibrary/images/textfield-icon.png | Bin 0 -> 154 bytes .../itemLibrary/images/textfield-icon16.png | Bin 0 -> 147 bytes .../itemLibrary/images/textfield-icon@2x.png | Bin 0 -> 172 bytes .../itemLibrary/images/timeline-16px.png | Bin 0 -> 389 bytes .../images/timeline-animation-16px.png | Bin 0 -> 296 bytes .../itemLibrary/images/timer-16px.png | Bin 0 -> 339 bytes .../itemLibrary/images/timer-24px.png | Bin 0 -> 712 bytes .../itemLibrary/images/timer-24px@2x.png | Bin 0 -> 1305 bytes .../itemLibrary/images/toolbar-icon.png | Bin 0 -> 131 bytes .../itemLibrary/images/toolbar-icon16.png | Bin 0 -> 114 bytes .../itemLibrary/images/toolbar-icon@2x.png | Bin 0 -> 140 bytes .../itemLibrary/images/toolbutton-icon.png | Bin 0 -> 141 bytes .../itemLibrary/images/toolbutton-icon16.png | Bin 0 -> 128 bytes .../itemLibrary/images/toolbutton-icon@2x.png | Bin 0 -> 158 bytes .../itemLibrary/images/toolseparator-icon.png | Bin 0 -> 111 bytes .../images/toolseparator-icon16.png | Bin 0 -> 123 bytes .../images/toolseparator-icon@2x.png | Bin 0 -> 131 bytes .../itemLibrary/images/tumbler-icon.png | Bin 0 -> 132 bytes .../itemLibrary/images/tumbler-icon16.png | Bin 0 -> 127 bytes .../itemLibrary/images/tumbler-icon@2x.png | Bin 0 -> 153 bytes .../itemLibrary/images/video-16px.png | Bin 0 -> 216 bytes .../itemLibrary/images/video-24px.png | Bin 0 -> 286 bytes .../itemLibrary/images/video-24px@2x.png | Bin 0 -> 399 bytes .../itemLibrary/images/video-output-16px.png | Bin 0 -> 289 bytes .../itemLibrary/images/video-output-24px.png | Bin 0 -> 387 bytes .../images/video-output-24px@2x.png | Bin 0 -> 610 bytes .../itemLibrary/images/webview-icon.png | Bin 0 -> 804 bytes .../itemLibrary/images/webview-icon16.png | Bin 0 -> 519 bytes .../itemLibrary/multimedia.metainfo | 84 ++ .../qmldesigner/itemLibrary/qml.metainfo | 53 ++ .../itemLibrary/qtquickcontrols2.metainfo | 575 ++++++++++++ .../quick.metainfo | 406 ++------- .../qmldesigner/itemLibrary/quick3d.metainfo | 125 +++ src/libs/languageutils/componentversion.cpp | 63 +- src/libs/languageutils/componentversion.h | 59 +- src/libs/sqlite/sqlitevalue.h | 38 +- src/libs/utils/smallstring.h | 8 + .../itemlibrary/itemlibrarymodel.cpp | 5 +- .../components/itemlibrary/itemlibrarymodel.h | 2 +- .../itemlibrary/itemlibrarywidget.cpp | 4 +- .../designercore/include/projectstorageids.h | 2 + .../projectstorage/projectstorage.h | 123 ++- .../projectstorage/projectstorageinterface.h | 2 + .../projectstorage/projectstoragetypes.h | 7 +- .../projectstorage/projectstorageupdater.cpp | 138 ++- .../projectstorage/projectstorageupdater.h | 24 +- .../projectstorage/typeannotationreader.cpp | 14 +- .../projectstorage/typeannotationreader.h | 4 +- .../qmldesigner/qmldesignerprojectmanager.cpp | 11 +- .../qmldesigner/qtquickplugin/quick.metainfo | 2 - .../tests/matchers/projectstorage-matcher.h | 21 + tests/unit/tests/matchers/unittest-matchers.h | 68 ++ tests/unit/tests/mocks/projectstoragemock.h | 5 + .../tests/printers/gtest-creator-printing.cpp | 5 +- .../designer/qtquickcontrols2.metainfo | 575 ++++++++++++ .../AssetUtils/designer/assetutils.metainfo | 21 + .../Effects/designer/effectlib.metainfo | 401 ++++++++ .../Helpers/designer/helpers.metainfo | 261 ++++++ .../designer/particleeffects.metainfo | 246 +++++ .../Particles3D/designer/particles3d.metainfo | 562 ++++++++++++ .../Physics/designer/physics.metainfo | 261 ++++++ .../qml/QtQuick3D/designer/quick3d.metainfo | 861 ++++++++++++++++++ .../projectstorage/projectstorage-test.cpp | 182 +++- .../projectstorageupdater-test.cpp | 271 ++++-- .../typeannotationreader-test.cpp | 100 +- .../unittests/utils/smallstring-test.cpp | 13 + 239 files changed, 5024 insertions(+), 578 deletions(-) create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/ambient-sound-16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/ambient-sound-24.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/ambient-sound-24@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/animated-image-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/animated-image-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/animated-image-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/animatedsprite-loading.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/audio-engine-16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/audio-engine-24.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/audio-engine-24@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/audio-listener-16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/audio-listener-24.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/audio-listener-24@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/audio-output-16px.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/audio-output-24px.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/audio-output-24px@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/audio-room-16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/audio-room-24.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/audio-room-24@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/border-image-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/border-image-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/border-image-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/busyindicator-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/busyindicator-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/busyindicator-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/button-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/button-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/button-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/checkbox-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/checkbox-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/checkbox-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/column-positioner-icon-16px.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/column-positioner-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/column-positioner-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/combobox-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/combobox-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/combobox-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/component-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/component-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/component-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/control-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/control-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/control-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/default-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/default3d.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/default3d16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/default3d@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/delaybutton-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/delaybutton-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/delaybutton-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/dial-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/dial-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/dial-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/drop-area-16px.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/drop-area-24px.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/drop-area-24px@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/extended-view3d-16px.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/extended-view3d-24px.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/extended-view3d-24px@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/flickable-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/flickable-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/flickable-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/flipable-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/flipable-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/flow-positioner-icon-16px.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/flow-positioner-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/flow-positioner-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/focusscope-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/focusscope-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/focusscope-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/frame-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/frame-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/frame-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/grid-positioner-icon-16px.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/grid-positioner-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/grid-positioner-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/gridview-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/gridview-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/gridview-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/groupbox-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/groupbox-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/groupbox-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/image-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/image-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/image-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/item-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/item-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/item-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/itemdelegate-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/itemdelegate-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/itemdelegate-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/keyframe-16px.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/label-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/label-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/label-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/listview-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/listview-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/listview-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/loader-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/loader-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/loader-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/media-player-16px.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/media-player-24px.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/media-player-24px@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/mouse-area-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/mouse-area-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/mouse-area-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/page-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/page-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/page-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/pageindicator-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/pageindicator-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/pageindicator-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/pane-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/pane-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/pane-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/pathview-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/pathview-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/pathview-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/progressbar-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/progressbar-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/progressbar-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/radiobutton-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/radiobutton-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/radiobutton-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/rangeslider-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/rangeslider-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/rangeslider-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/rect-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/rect-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/rect-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/repeater-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/repeater-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/repeater-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/roundbutton-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/roundbutton-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/roundbutton-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/row-positioner-icon-16px.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/row-positioner-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/row-positioner-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/scrollview-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/scrollview-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/scrollview-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/slider-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/slider-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/slider-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/spatial-audio-16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/spatial-audio-24.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/spatial-audio-24@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/spinbox-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/spinbox-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/spinbox-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/stackview-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/stackview-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/stackview-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/swipeview-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/swipeview-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/swipeview-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/switch-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/switch-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/switch-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/text-edit-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/text-edit-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/text-edit-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/text-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/text-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/text-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/text-input-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/text-input-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/text-input-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/textarea-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/textarea-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/textarea-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/textfield-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/textfield-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/textfield-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/timeline-16px.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/timeline-animation-16px.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/timer-16px.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/timer-24px.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/timer-24px@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/toolbar-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/toolbar-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/toolbar-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/toolbutton-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/toolbutton-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/toolbutton-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/toolseparator-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/toolseparator-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/toolseparator-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/tumbler-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/tumbler-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/tumbler-icon@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/video-16px.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/video-24px.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/video-24px@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/video-output-16px.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/video-output-24px.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/video-output-24px@2x.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/webview-icon.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/images/webview-icon16.png create mode 100644 share/qtcreator/qmldesigner/itemLibrary/multimedia.metainfo create mode 100644 share/qtcreator/qmldesigner/itemLibrary/qml.metainfo create mode 100644 share/qtcreator/qmldesigner/itemLibrary/qtquickcontrols2.metainfo rename share/qtcreator/qmldesigner/{propertyEditorQmlSources => itemLibrary}/quick.metainfo (51%) create mode 100644 share/qtcreator/qmldesigner/itemLibrary/quick3d.metainfo create mode 100644 tests/unit/tests/unittests/projectstorage/data/qml/QtQuick/Controls/designer/qtquickcontrols2.metainfo create mode 100644 tests/unit/tests/unittests/projectstorage/data/qml/QtQuick3D/AssetUtils/designer/assetutils.metainfo create mode 100644 tests/unit/tests/unittests/projectstorage/data/qml/QtQuick3D/Effects/designer/effectlib.metainfo create mode 100644 tests/unit/tests/unittests/projectstorage/data/qml/QtQuick3D/Helpers/designer/helpers.metainfo create mode 100644 tests/unit/tests/unittests/projectstorage/data/qml/QtQuick3D/ParticleEffects/designer/particleeffects.metainfo create mode 100644 tests/unit/tests/unittests/projectstorage/data/qml/QtQuick3D/Particles3D/designer/particles3d.metainfo create mode 100644 tests/unit/tests/unittests/projectstorage/data/qml/QtQuick3D/Physics/designer/physics.metainfo create mode 100644 tests/unit/tests/unittests/projectstorage/data/qml/QtQuick3D/designer/quick3d.metainfo diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/ambient-sound-16.png b/share/qtcreator/qmldesigner/itemLibrary/images/ambient-sound-16.png new file mode 100644 index 0000000000000000000000000000000000000000..6b16d8139780f038a1194b25dea1b0cf69f05b19 GIT binary patch literal 315 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4i*LmhONKMUokK+GI_cg?&NG1n3`pB_4BBJ-``@HA%el`A!sW{PjU@#ER{ixTzM zEdpk7xBso_d+&e#^5@D$a`S(4$MlzLuGlqU$Ch`qOH8~DMzzcdz1?+cUhd2P2lk#z zeWU$r;pq<3@8|wZJ+YV7?~8_X|Aeh6hv%d@3st&@9coyVS+DeDuH@D9Tfr?`R;lSX z>V--k{&p|CY=x^tac-ohN>P}yj#U2zwf7enOxonm9i(_M*W9UFd3&Glua(aZ*w4*p zKE3buMosOdch_|Bite}T%PD=nT~jrj@9d25iMeYua}TX#H`lwPnUQti|Nk}Xnbw=@ XJd7`hW!b{Oz`)??>gTe~DWM4fN92yO literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/ambient-sound-24.png b/share/qtcreator/qmldesigner/itemLibrary/images/ambient-sound-24.png new file mode 100644 index 0000000000000000000000000000000000000000..0549a847587b0870d144cc66a8d7c731de5c26d8 GIT binary patch literal 514 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|4i*Lm2CurW#S9FLCp}#pLo80;y&Rpv#~{J> zApf6i(5x-GmeF-ldUqo9rpX9RUh27WY0A=A9^W9N_DL?e<&$qtis_KMn6*|XOHFBI zV&BA;ssHBBJo~Wg*Ik2lx1E!}@23zW3>*;!oBOfUtfQ{L~S|Gi;bTD&VO05-AieW@q-yl z)n+Eli(1b6qA;Re@$9lX@h!K76KC5M*B71(TzJ@fwy9*0zM!DkE90o8Qyz72bkChQ zY2u}{I+qqB|J1_p*3B|(0{3=E7+%q*;I?3`TOJiL7T0)j%qqGIBbQqnTAa`FmF$|`Ca znp!%#din;2M#d(lX66=_R@OGQ_70BDF0O9w9-dy_KE8edfx#i6;SrHh(Xnyy35iL` zDXD4c8JSr*xq0~og~cVM<&{;{HMMmOjZMuhZS5Uh-95d16DCfYJZ0*%8MEiioi~5M zqNOWWtzNTs-TDn1H*Masb^DH8yZ7wfxBtMw!$*#vIC<*y*>mSFT)cGo%GGPvZ``_b z_ul;nkDfez_WZ@mSFhi^efR#u$4{TXeEs(Q$IoBC|NQ+oXK8vc0|RrDr;B5VgyhtN z+|j|T3~UeH=g2%$(Bl-7Sn3fnEkS9*l2*qB%OqxHELBKtaWtE}Xv3$VXAP>U5eGX& zyHZp{;*{2H;>c+>+;;Hh_u^ZY9nze?=6r8mAjC3NVFin$Dud{&TM_5W#8cMq+NMAV#21R$V=T%OT#ARecor=D|B<8vr9wyO-Y`F)y>T7 zUlm3>B{t9ABI|9)GvV)Vmor^6KP+R=P{}@I5dNc#`{mCX&wr_#w3)X{WU_qVitMV? z`F+SadtF+vc;wRNoTb+#nb&;XAYwdCKIg@}4PQ+p!<;^zC{HmwA|x(d`oVBBQ^cL~ z!T~C#AGggHUZ^oEP3*%H27YFb#|07zk5mQi4u(EC@$n4%!rrr%Ws|>!2JAU_pK;AI zjs`I~hHIAv`R5C8&rMa_p}J*SZU9TKk%++EIE7~Cs*czi?oFHD)H2mAy3Fl5=eR7- zl#faU^Vl1b;shUVo?U-j`M_tHmNyIi=Cv*BxwJXrXrgUXegSjMk-BFJ4$GR4emE)p zRDNP%?-Z^EtvZ&JkK!kSerW$GdGBqrka5wu38mk~doS*ibFdC>?2lx+X*TbW&e>(z zbE>2rwoTpkXZfuYw={nE1o2-n6y9X=t;JFBW0ZQOrDVq{)w7((RllV<9yDZ#FH}ys zb^pipgU^k&$z+vHzPr0=!O8#akE=hozgTx6xh*}4U*@5p#%j(v)r-w%F%^3KSD3DJdlY#Y!LqDqUSDc z;pmf-A98%y;O@mb?aRk1E2cGTzg>tH-hTGnzI20d*8C&P8{aNbwU;uA{q@k^v)|D0 zi%BNe@&7EQg(l)0dh&`lZwKAK@KE7qa>v2P*Dd!kmvjA`Wiy-O&i2>U)~T14@?SO; zxL>%@R_^n~H}Z?O*PT{k*yx|0Vk5=mFuQ!}y~S2K4;uaFF4=r@gQX%*Lt188sC=a^ zZ@J*k|4lA8mz;Hrynb5goubn2J68-}C>*aoFY9&82|ZslEx1&_;^&h3A70n|85kHC NJYD@<);T3K0RUmi*W~~J literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/animated-image-icon.png b/share/qtcreator/qmldesigner/itemLibrary/images/animated-image-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..71371f97a356e416da78948871d071ad40e7210c GIT binary patch literal 298 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|4rT@hhU+WOo?>8NunF)9ab;j&SV1meDoqV% zU|4=NsA+xfq|jR)5S4FBY5pZN1Di+RQ>(Y~44;pc;R7$R#|JP1wd(@NCU-LA(_dVx-P|N;& zoA)Gpo2KHn&`F7cIR|tql`0vmH+Zo5{=6IU{He14g(*L@n!Py>2*-FaT-$dtZvkiM wN@oEUgOz-a0t!2C2}q^#cxj$jTroF(qouTUzPn!l0|Nttr>mdKI;Vst0Ld$Pr2qf` literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/animated-image-icon16.png b/share/qtcreator/qmldesigner/itemLibrary/images/animated-image-icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..86b80e95ab5a88c7264023cc125b6619f5404496 GIT binary patch literal 211 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd7G?$phPQVgfdmu-d_r6q7#LRI1+|}TQy3T+ zxJrWjf*BYEB;-8;x=x(`$r}HMfq^01)5S4_BRV9;R{Wi8HNrl zI|3w)5A-~JwmRrw&(Sm5th*GHPD?CO$Wk*ZaFF2SS(9+gi;=BOZ(S(Q>S=}s1}=q` ij=V8T4=@z7FbJ+Q>f|kJEVDNPHb6Mw<&;$TCGe?C0 literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/animated-image-icon@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/animated-image-icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..4ad2a9011b2fc9c189901ce2f04a8ae4bebaa618 GIT binary patch literal 342 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-7G?$phNU`BwlFX-CtiTIuKij4- zFfeeH1o;IsFbc@)xdn8e{}s|=e1d_2;g+Y1V~BQy4PZVF-=$Cd^z=h@Vta+2HuK|3VCnzXXmR$GKlSxjcYxB>%7)M_PSZ_ z22vHGZedo3J$?%Iu>a$)x!-(OR$^K#n|=K2y-Vg@JDkZJ?K$z(VSR(?A{sU_jWbQA z_*k*CCun_5nE1w(r8}jULF{8RuWH1R`xl+Newu2wr S(agZWz~JfX=d#Wzp$P!0c!+fX literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/animatedsprite-loading.png b/share/qtcreator/qmldesigner/itemLibrary/images/animatedsprite-loading.png new file mode 100644 index 0000000000000000000000000000000000000000..ff2bbbd140fc31c4de2eb530414d8153eb0715b4 GIT binary patch literal 118 zcmeAS@N?(olHy`uVBq!ia0y~yU}RumU~ph$W?*1AKiT>(0|NtFfKP~P>46Xb|No!p z`u8#e1B14wi(`n#@#G)=3= literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/audio-engine-16.png b/share/qtcreator/qmldesigner/itemLibrary/images/audio-engine-16.png new file mode 100644 index 0000000000000000000000000000000000000000..da40bc69a2b481195c875e93421f3c6328353b96 GIT binary patch literal 363 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4rT@h1`S>QUyS$&tk+CP$B-`u&%ESrt!n!oAJi3=9km Mp00i_>zopr0MbmYZ2$lO literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/audio-engine-24.png b/share/qtcreator/qmldesigner/itemLibrary/images/audio-engine-24.png new file mode 100644 index 0000000000000000000000000000000000000000..b3ebdf745b73741313790f4ade37be56e22daed9 GIT binary patch literal 472 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|4rT@hhU+WOo?>8Nm>J*`;>y6l@c;k+6@w4l z{Z=!Vfq}uOB*-tAfq{{UlZ!`0R#{!!z{17V%`YG!DLc2WxwXAx^3(+jS8v$3efOTD zr%qqEeCO%&Hy=L!lXvmcWnf^~6!>P+uRvzKVezRW)HRs3|K;R3Vhv$tP1yb*j5H*xkZ1|Q~E3kahWsBD!RSJrD9XY9imKf3^FO}3T2kb8L%$UE)IRia6HFhmu>vntc`mt kV)TCP{4ZYf-OhjZOO3flzAp4*U|?YIboFyt=akR{0B<+v6951J literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/audio-engine-24@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/audio-engine-24@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..476df8640fc0cb923855e63c42966a4f5b128eb4 GIT binary patch literal 803 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4rT@hhJ-tuTNxM_z6JP%xH2#>{Qv)d#RvnE z$y}xk3=DxKL4Lsu42(>y>>OO&0)j$fQmUHTdius@cJ>aAt{&cg{sB=j3CU^cxq10T zCFM1B^$l&k{S&7yUA}4C&iw}t9yxXT!o^EBZr#3f_x_WQpTB(j_51I?$RLUL3=E9j zo-U3d9>?EaJ3gt&L4YkG?5K{`vku119hS<^<~;iwpO)=7LoLSU&C$D-zJFumuP#}p zuiqz>|55-5%9+Jtdfc9haUz4cR7TGC&Y0(2Rl5SU8DQ*0&{)i ze~X*wmWnf&~4LvZkxFAfg&1+2Xx0*sokoG&zqJ_&FT zUlz_Gwc(S3=!LeAl1#7Iw3tgh6?a^kmv)&)jNAO4N%zq+D{Zem_@MXn_%2zw>ICnAZ@6pQOoKDUNpZk~M=FGO~JyT}->rUMd>x3=0(tHlY+?mbf-JrQq zbG=-|%b1-rzDP8zt(jn*CewBx&fe=|t;7|k=RD?;-Hp`?kD0qMS8dRB){+kgbH%5agX{NOxNkaU{&~vF%Mx6#x178@gJbrKdgi}9_y1c~iq|mv Y*8bMrQ>V6{fq{X+)78&qol`;+077Jy9RL6T literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/audio-listener-16.png b/share/qtcreator/qmldesigner/itemLibrary/images/audio-listener-16.png new file mode 100644 index 0000000000000000000000000000000000000000..ecc583b859afda6c4437913df6bc1d9f8e5980df GIT binary patch literal 311 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4i*LmhONKMUokK+{PT2i4ABVAy|7W+g^}U- z$M?nM7r&Z`e>_qY#G}^iIOERz1=^`%MiyS0sgsJlrkBLNlc?cup7Q@d&Nhwuwzrae zU+-=H`|A5k^FJM(os7kDx3=aU&wkEOH_QIQ7A6KU+hSYRH9OKDtov@ai_L6~XVbKW zzt=T1vahqsQ0AN^_GZW1X$u?u6RbJ)bbh~_a@ma6ru0OZkc{N7EQYXNo3cZzRU}x= zZMG-uuZ(^c%+0{}(j4#JxUtDp{cfQmw_VR&mglk?=T-{Ue7tLC@P2OK zuakz#-qRRt3I#7ET+`Y+H#K|j6c*Oq&khvIpZUvtKz07!kJlc?{k>hooWA_pxwOJ< S>lqjr7(8A5T-G@yGywpt^M{TA literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/audio-listener-24.png b/share/qtcreator/qmldesigner/itemLibrary/images/audio-listener-24.png new file mode 100644 index 0000000000000000000000000000000000000000..ee181f57cc8f6b3e43e6d4c6bd4f22da63269f59 GIT binary patch literal 924 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|4rT@hhU+WOo?>8N>ml4A0A#H=KVO_bq|{ zn{`yRVv9h-+UE-Y#O!7DSOlCpjvV@H@GRuOMcKE;84DGzc_dv57w3_Rc-e62=Ig}) ze8;{f2+jGfHtT$}%(@GUC%?&9$JOj;!=B0TwLHv;QB)~!+G_8;GY_~ltcn$#*K@?| zx|<)H$es2X@u548D*Zla@b35r#`#)v4({gPkatkLW`#i8y<+JH>VHK~dmFK91dEF@ z>{C6Z9CbjizO8YoTGL%CiAh0KnzzDz%=vV$cxu+iuBrN{~zSawk*;g@?#p5n*82=4*AsP(ABY+|3IIYg8E|%Tr%0H04Q%d9A{# vb39he%k0^d5QXGtmSs1;)w^>j{$rS-Wm2+qznmij0|SGntDnm{r-UW|Mo{50 literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/audio-listener-24@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/audio-listener-24@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..2588277e5315b2ebede19de87ffb9dee62be346b GIT binary patch literal 1691 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4rT@hhJ-tuTNxObLIZq4Tp1V`{{R2KVib&q z0QEwkFs^A80|Udck|4ie1_nkZW)@a94o)s^9$r3v0YM=VQE>@LDQOv5Ie7&|WffI5 zbq!4|9bG*GLn9M23o9F2J9`I5XE%2bPaj`D|B#5NnAo`Zgrww@wDhd(oZP&EqT-U$ zvhs?`>YBRxhUS*Gj;@}*2@@wxnL2Iy%vrPN%$>hr;gV%5R<2sTX8p!Zo40P;zGLUE z-Fx;PJaqWT(c>pho;rQz?78z7E?&BPy_W}!KE_CKZYkKZ(Wcu<*1f{ zK5NnvwM?nlDO;v+O}ojey-7o9GSAdgCXZ*`?bQ>C4U=x2S{y5(ax`K3_xo4*)5W)K zF8=Z7TdmE#^Tqq-pZ`8bfBMpHpSypq9C)(l%ljuU``C)6o)vBQdU#S=(_{I%@9j(S zqhh}uxH!?d8mF#Qu5`Y;G?-!TtxTr9yLexO z$Yw{+&%GU$)i5dG6njosKWB}IKC9dL>wR964psAh?77wW=?c4=fhfa{AITN}9<0(1 zd-Kn+U5268ic^4drbCWIf#*jxhM2p@ro?tLHsmi>TkyES{nhy|zn}dvW!Qh7vF9|; zJ%)}~CwV@o%Cr7(6-!tr{a}h;ihI>J4hIWHfsUC6*573)*V<~z9B0jAA-tc_rAh5V zDL=#Bu4~H=_P%Zim15DmzJhZ}@VUpovTsjXd~b2>fvUYptOZ}Z_fC!cFfWX`?(HU) zHB&?~3njSUADYha{&iLMhNwv;XCi)VW&Uu?e)FCa4<>x9H)G!6WN*p1Uv49tu3OxC zW}7wDjNgCWH1fV6;N7gZpel`FL9Bk%{k8L#ecrtF!0yXe8_KPsQaRW8Jy(C#c=lvk zL-MC&&a9q4miz~J%Na~wYfm@baf#t}v3sob-}_Q)80)<+-Hh2+6T_dx?)Pl}0m~(F zIS1dKe51)Mb8W(9xeaUApPhO3_t#R%X$zKhHtn*uES0_X;HQV> z_sT36=Dt)$-|mmkwHi(x*UP$a_)~*!aFmR+mH72D`~EzSu9KR#&0p{3{QmFZ0TDCr a{8zfG5o4(?@5aEuz~JfX=d#Wzp$Py=r;CID literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/audio-output-16px.png b/share/qtcreator/qmldesigner/itemLibrary/images/audio-output-16px.png new file mode 100644 index 0000000000000000000000000000000000000000..6ae703de64697f2f070e14fbf6429bf8aefae9e7 GIT binary patch literal 359 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7^*mi1Lo9lyUf!?mS}1Y+ z!yBM{Kjab=A@$vVwb*V7w~#rUpD2x!%Y{-8&8g_bax(Hk&?JEa;m&!j8g33 zozKhO6?XX7PCx(s^B(`_mHYfxMd;kJw>Xg?E4E+&`ul2Ny25y)dt!^InC8ths@{M6mg?k3GImE`Ahu&^@=I zO*n-mSlxo&XsbK(=$E>~jpp*H z>o>B>M~MCL`m?g#H1)u`_oqu*f=w1^Gj5P(eG(Y5^W0Sl4-K95#x;yxMwb7@mfcNd QU|?YIboFyt=akR{00@nq3jhEB literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/audio-output-24px.png b/share/qtcreator/qmldesigner/itemLibrary/images/audio-output-24px.png new file mode 100644 index 0000000000000000000000000000000000000000..f2133ca716739f83e9fe9284fdec8086317dc87b GIT binary patch literal 595 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|4mJh`h6m-gKNuL8q&!_5Lp(a?UiI$qEtFyZ zaQ>^;ArV7Qvne*-L6=e#v%EGe}+@nXHSl= zZi^RQ?(u2<0@>2E(Fwud4F^g|IELmS-PS@UiFUe&iR(QM*|$@9pt>@ z>-6KU&N4&2~;Y7m3lf=R*y#hj!1zCAY+h_D)(LGJTRjoTJp^nm5N)*XRkg zE?gM3z4(GllS=PL?pG0=m7y)6%(Eh9)ldCs&uE|e`7fvS#;si|ZY!C84BeLMe=_gN z+Z_?E5v45CKYv@B^W5f0+|65SG=;-t4;tTW-KO!LKZ!H0OgN19MuYp0so&?TDb`Gz z+k1-fvg)4AKRUi{T6uHoF^i^N)mbmUHAFb5RNJT-G@aeio~`<4f8E;d8o#o?VtY^5 zTHHLkrSs&S_xCv_3A~b+uy=E?XzwkvWmKfmad+mshNeP zjjg?dle3Gfn}?^jk6%b=WOQs?LQ+b4c1~V?K~YIrc~y0NLt{((go%?TPn$7w&isXo zmaSZ~cKwEpo3?D-v2*v{{YQ=+KX>uk&D(eHJ$mx=+4C1~-+lW0`#5HKQM2yfnj>J!ov6)Gj`tDobO+5v^h*wNbW?|^5xbyd$w)i z+a#y4V*CD!E~;(QLjpcDSUs6i{r*R18ADb0)x>HC%kL}+3hGLqbg#0^36>A!-j~lT zSFzUo>;ZP=9egn>P2!o3`y9w@+$Os8c%ec*!{!6JZ=IPknJZX*Rz6TKxXN&>K|gBU zwgZ*S9?X_o|I0sUV)S8ttTiKa|0aG7*8~Sv^RF&fo;_4CSp1~Wdds@m2UG;+evdkB zd%!k%S*k!rM`fSeZtG1C~oK^hnr1~e*Wq2 z^<4{+Ek$OuOuVmDB>t|QDZO)-@s|0_3mw=!zdsOM)57@nUcNowQiFt!=66e^7n(kp z`o;67OkQ_KigL`VRYi5Iu1jhpe$BV@cDeD_nVt3jBvtPAJ4fFBQejlp4xIbSx;oK4 z%dx2|Dfi~x2%R<3r}7TB?)|vd`SI+@Y}@SX8M7+93$Gk+4q#wlVDNPHb6Mw<&;$Ud C+eYI6 literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/audio-room-16.png b/share/qtcreator/qmldesigner/itemLibrary/images/audio-room-16.png new file mode 100644 index 0000000000000000000000000000000000000000..98f245d624033cf11b67200ca3fb82a78bf31ea8 GIT binary patch literal 276 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7ZhN{ohFJ8zy=?2{94K(~ zqx`In%p;~8P16i_uzWXAxUrMr;32Pz_gGyxHM)EfpKHCjl=+zJO{hVE?mc0XO2^=v z6}x`!X#X#>!%H(QG)*r(O>^mt*+0L|_;+uC>Ae$YMCUwKQ(`O4=XAf(-(i$EVPma|*C0gIraQ5u zK#;#+0i#{#?HPwy0~%3-nPgl$2L~G<~h^ z?Tk;R`@u(pEI zyxej#loof`cTVP-$itE@N2K%~=-E9^Kf9%QMo-6AvxbBHW(;4} a?tfWQp%HoJ%5???1_n=8KbLh*2~7ZG2e`uk literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/audio-room-24@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/audio-room-24@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..bef7f80e3ecabe7e94d2e3681db4bc828eb7540a GIT binary patch literal 878 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4mJh`hH$2z?F%|9iLh`S0KNir@YG zdv5pEnKLDeu4I|MGr#eUY2CBqU#s>io?!p?zTusz7GvsSp#U=^YVlRRobSx+4a$?g zo&P*$E*may zWXTmyh(2JoX}Oh5O+Q2SVO4vU`$7qUAAFB}+EUE2jm=skQ)x@Q>TIWahWo7X^Hl05 zir6ygzGwf(oX_Ci(9f8ouXOqoL%Ygf(OZ>lcGI0M={(^3t|9F`Ye}J(43nICbRipK zj(&<6v%L4iOtB4(^S^KgYhKAP zi!sF;M_+JN&JvwuqPns_$MmQ1)G2HHUa9^r*r=K+KS4QDa;gcB4QowM!Eu(4G6ljf zCYNZcw#l5Vbhz|>lG((yN{8Nun+JFab;j&SU~|e{{Gc; z1_lO^k|4ie21Zr^c^wCjfRx(0>5ERBfAZnm-~JD~KyGOFba4#P2;MueY$&Js{b0#UBd(Ny zz*7?B7tFvYAny?{`Na7TAAeb0ukv7EU`X(EaSY*zPEKfOG-h{La!#_%SUsKT;4EVf zL2Xtxws5wEM|o!D+?LQ{kx(@r*g&NHqrHM_MI(<) z%fyL_)%_PH8d~yqPc1emdB7mEP+OX3WmC|>;}@Q*oY#`|x+kP*uR34%^ra%^4=>}_ zGMsBkE|&VHpSSIQM}L7~bJpKjTeXTwDtAL~$EvRVcG-X4>BGgBPd*5m6mlWu!-tP` z`kEK2c2qTodz$WT&wk(?l%gSfhrvFGQOvV|TY+_h$2V7Av6E7QI*-Cb9lCzhvUp6) ze11tWQIKCS=!j!t`mHAdJhx=Bn(f*%45r8hF-h?!wp??zb$YMZXE3FBhR}sGif0yF vT^9Z4(Vt4*zO$Jxw9H={hi|>}he>(*=j&4}9T^!I7#KWV{an^LB{Ts5*CnJX literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/busyindicator-icon.png b/share/qtcreator/qmldesigner/itemLibrary/images/busyindicator-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..666d1ed93f76570004e94223558089ced1e89d8c GIT binary patch literal 320 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|4rT@hhU+WOo?>8Nun+JFab;j&SU~|e{{Gc; z1_lO^k|4ie21Yi1Az=|!O#|P|s;Qgy?tAy=?{s64BnAeCIi4<#AsXjfC*5XhR^V}N zKH+*%v*6$V=O5Y-nVB9RBqSzZ`=NV`opZ+c_|`X6D^gV SxiBy=FnGH9xvXzC!Sh;2@y#N ziR-=_?w$GR)zJ9zE+ZS;ymBrco?;;hi5q@K3Jtp%J5ux#cWh@o@}RCep~7KF)P#`p poCygDtXb{S9NU@lG|xyfJiRHy-ZYs-mVtqR!PC{xWt~$(69Cw*S6Bc5 literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/busyindicator-icon@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/busyindicator-icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..bb2278ff899d341822106b980a6c37bf341fbece GIT binary patch literal 643 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4rT@hhJ-tuTNxM_CI|S0xH2#>tQchAU965X z$gL$oe!&ckOl<62+ya798kz>?RyMYFZk}F2!EvcY#g)}HefE(xM9k|8j6CF$r>VO$wXPv$5p3dE$$0OyUfe z%fFQW+oi_x-oz?weNTPp%aGfNPOrX7&ky{Tvu%gzi_tNcf$bC4^qrH=ed=~Iyc?FC6|K8#?G{O? zi7yVi9-DqQ@}&AI*=4g1EWFQ>a=WKQ`Lk=(((~EJUIt9~G;3qs{JihwbCWe#&j)bz zEo?}AT66l8<+Q6a7Jc8ZS>+r&W&2(Kp4eZn75-XE{{8od(P>|7cAdvnP--7#KWV{an^LB{Ts5LX9yg literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/button-icon16.png b/share/qtcreator/qmldesigner/itemLibrary/images/button-icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..5c921deb136718941fab6fd02406e74d40af23a6 GIT binary patch literal 145 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd7G?$phPQVgfdqsCd_r6q7#LQdf{?klLKqkr zSWANZf*Bag%3n@qK5(0XfkD&L#W92+b1azTy7EXa8KqMdBD37}7mm978;gzrC`NuR%e8`9hP1 zmd1tu|0CFi-JN;!zCU}-w3oR@^5pRtsry?dEv|od_@rF=j^lTa#qDNpI8w)Xz~^Yw zO0UMQsZugo3sx~kJ?KxLYVWtMEyix=y8Nunh1Cab;j&SV0zW+J3B- zfq_A&B*-tAftgKI#>v+&EN$uPOA=!OCV14Ejpi(`n!`Lh>2g&GuiTrM8F z*}-$~|As6kMee(YKke62Xn38zP}?K=M~g`^5AUOSTb)}J{USD(@uW8{Z(dpwzuihE zB4_&Aj?|5NW%QD&_vVSdwQBcxoqHhM$9(eS&3ElMf9BPFU~7G>lQUqyM?PFkI@bh$W43U_+wBK9ofC2~e;BDLE-PL?midyPX3=9kmp00i_>zopr0A-_KhyVZp literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/checkbox-icon@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/checkbox-icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..51c5601de02228eb480bf24d3c72cbc1c2756ef7 GIT binary patch literal 336 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4rT@hhJ-tuTNxM_%mREuTp1V`R*(*k?Ao!8 zfq_A=B*-tAflE|c#wjeiaNqt*mv7$r_P6{Z?AX?H5>4_1UkA2 zv7h*>KU?t5q}{6vwi&+uzlz6WTlLdqUXu^T9>-U5ExE@R%v$lS?a;O_8UkmY2Yyjt zIHP{Mf6@6v8@4Mf*#1xK!0zvdzG}+6yuEnAU#4qz-1qb!+=>)f6yI;cvhdfROK~UU z?6kY)zWI23;e@@?ylP4B+t-V@$}cnR+SLE}E$hqfT8D$biW)a9u)Vp%^~1ciFWfuk uz5C+5VqQFxvwC%5c1+OjcmL&V815Wt4A!dsben;Jfx*+&&t;ucLK6V8t<7UsY9gaR2_( z&+Zp8FfcHd1o;IsI6S+N#=yWJ=IP=X!XcadBQze_%ku=NahxtU!(Mg Qfq{X+)78&qol`;+0N&an@&Et; literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/column-positioner-icon.png b/share/qtcreator/qmldesigner/itemLibrary/images/column-positioner-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..0cd116d13cd2efa5c1c62a6f4d982b052304ff9b GIT binary patch literal 118 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|CT0c(hPjegvl$o|I0Jk_T>t<7UsY9gaR2_( z&+Zp8FfcHd1o;IsI6S+N#=yWJ?djqe!V#UE(7?1dbafbm=G6-qI38*2%4A?>m?@=~#GEr{=|Tqq(+rEnd`ss$d$KZ^ YXXH$&oauIffq{X+)78&qol`;+0AK!OJnPB5@ODSGfM=|sR8$3l%wwhTXm6c4|sG>BthU|{fc^>bP0l+XkK2Bv$$z`)??>gTe~DWM4fks&Z3 literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/combobox-icon@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/combobox-icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..5f82390596b1ef5a2e41ef5c366376d1e772ca3e GIT binary patch literal 185 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-7G?$phNU`BwlFX-2nG0rxH2#>tbl>lt5#h+ zd$wj%)^7#|2Ii6=zhDLddD}g=s~8v&kqLjfdyV5>E*( zF0SOM2QrU0cu8n#YEFLYC@gN`P`5#va}nc&rw$1@CmPt?R1Fw!3bkb{)MJ@!;IMgi gVMtTfK|Tg6>sb@l?^b4FU|?YIboFyt=akR{0PGt&wg3PC literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/component-icon.png b/share/qtcreator/qmldesigner/itemLibrary/images/component-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..9c7df42bc7019f9de572461dbe9011eae259c898 GIT binary patch literal 626 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|4mJh`h6m-gKNuL8EInNuLp(a)PIb%>4isqH z|CZZp!-m|kf7b)Yd_fU5M*&C2 zDN0QbOb@5qibV7AE{%)0JdDng7 z?SkUm+_mQm=LGjXo)Z@pWi`=5#jd}vZ=a;3WaaJKw|C!SnBuxbTWi;pc{O7E# zQF}kFe)ROy&c`=%zB%5gE^rU6R+0$Q5i|Z+ap%Ui+??C@R^?tXn>Ky=_OP&UpPeyg z>t0J8;NI4C+QG@?UDpYp1NS-SFyCvx*kiaXZvE!S`zF4@w;BHTy#M+5@PRcQ6{~Ks z?@8PJyq!rZbnAyo*@n9u?_OI?2)&y4V6lA7{!fSAJ>dEvD#151e`;<9Yhsq&d0Agy z-&#o-nVP)FFjJNd6MfX~ry9-7{#{iNwDOAo1E;M~Z)0L&Y+hTOJDRkyqNt|kkF(X> mI>U+cDz4tW8_V>ATj$_`Z~ilw${83K7(8A5T-G@yGywn)Nh#R? literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/component-icon16.png b/share/qtcreator/qmldesigner/itemLibrary/images/component-icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..99941541c6fbb6416e052edc2b4a272e351cce85 GIT binary patch literal 438 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7Ydu{YLo9mtPBQdnb`)sc zzdOcNo%d$+Q+Ja887)|rL!+6Q5qxNiiW73XLhRY_tSA)IO znSY3F@cW-XG02Z$`IqC?vbqKbzWXW&-_l^-J~3s|$tTHQdKrW|kG$@8+Zm$ed;Rs- u(Bp;MqSl5L7AAh&wIO7}=i<-d^RKG5{k$Lez?Ff4fx*+&&t;ucLK6TeJiWUB literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/component-icon@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/component-icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..f66349a63b2f3688c2c3c99a226497b3b81dc268 GIT binary patch literal 1107 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4rT@hhJ-tuTNxM_PXzdcxH2#>tN?>wzkaP) zvEs*%AD=&eeg^|9R;+mc{ykWJ#q;OSSFC`N4<9~!^5h9b975i|e;>ktkf%tXOg2;DHq@R$RPzam9)iyTD-2o;?s{D^{#Hbm$O-4VKvjVQ<~K6~b7wXc0v5 zwr$%WB9kUfTCrlqrcImL+S*pESkcqlGiAyY2pdAKUcI`hscFTE71O3oTd`ur1y1F`u;`oI4 z;*w&B6hy?FTWC821H+_}AirP+MrLL=oUa5K7#LT0x;Tb-biTbB8h+S7g6+YBcYKH2Zx=`yKxsx0}%KqGik;{#)s1LX_agiU3bmoT(h z9k5`_xb`XYS!0;S1N%O~-|@$_x9wP|q$}aC8{CsS?ZrFK-j;EVWnh9@BN;tA+zg^|C)$8@j@*}w{e%@^GG(L0ln%XM6JMre5 zbh4K?{L7xg&~S0S+}pLaVTbPoC^)^$=1n_ep{>KmerWOvcaBSi2@@N5naTt@B&1?qoivs2T|UI*E{$xS(+x-W~P@nc(vf5{QPhC=T=nH8QT&(q}NSZA@EIh=0Ez`(%Z M>FVdQ&MBb@04PL6&;S4c literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/control-icon.png b/share/qtcreator/qmldesigner/itemLibrary/images/control-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..fd9e4e8ff3fd207c48a0a30c3e3029511627f006 GIT binary patch literal 293 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|4rT@hhU+WOo?>8NFbnVrab;j&SV1~CvTMgW z1_lPfk|4ie27V1q^T4$9rk2fzPrm>7?FZ*-*3%3O485K%jv*QoTZ7*6wHRL!1`Ivsb%l2;9@Rd(Ny zz*7?B7tFwC=9f8Z!P1lWfBlUPy0@Kyfg#b;#W93KHaQ_7VD46<4n~e6=jTKfFPqTV zxbgX!q}9giED|@)&oL^DD_+*%`25VvImy-MWRf~8w?02J^Ky2d(V56O#m~+duAa=Z x+UFcY<7X`vW?X3U2OmW literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/control-icon@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/control-icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..22604d24925ee67a08bd61b1392c907074440f8f GIT binary patch literal 509 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4rT@hhJ-tuTR~KSPlzi61H%ehf`h6vB^VeO z(7WUgn`8 z@Ox9bVC~NT4f3z1&W(6|SlVXQx;MSqqCbC>JlJlyL~p)6Q)YDG?k8sTlji4?Td#Z& z+@Stz#fjK^p?@S^$I96G${zg`RcW&0kIBxihcoYb+1O>zi_|{%DD>-x@Lx-l&Stx= zS<+m7KKr!lRNwF2n!(prM_s>tYIgG9)kh{ZYUDS`34YoBH|dG{PtDXPS_==IsDBf> z;>L%?3yyu!{>*7~`CyyX!}b0OHt#cdJ_Z?Hl6lgaw2XI8zyqcq(;v7V&TQ>%_GZ?z z+p*^(FH3&l4$cR;Yv0H5(qgx7W+~t$BR@X!)O? atYM~y-PtayykKBpVDNPHb6Mw<&;$TVm;i$S literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/default-icon.png b/share/qtcreator/qmldesigner/itemLibrary/images/default-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..564226e949b294850a82250f3b79e26adefa2d45 GIT binary patch literal 813 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|4mJh`h6m-gKNuJoBuiW)N`mv#O3D+9QW+dm z@{>{(JaZG%Q-e|yQz{EjrrIztFrD{waSW-rwI$THN61m6PC!gWBb$+hrDZ|uF{kqy z0^G&nnTy531+{Ydq@}dAC+$A=&8ew1P}y)sjJIBR#EcmUJ*|cpFK09dp3>j~wEhY<~F6zW3GlXICa`PW3YS<1F2O++eO>s-wWAt65)L%WS8gUir){ za+=~)uZf&}H{G6Ryxb9?<76~*ONiFgAQLIktgTTOdnS~c&3-A9JvVuCgwByZH|36_ zNfSI&7TkVo$KC3*U7Sf`V`}8)Vit#)N)tJbJT81uV&(s*u0Ac1<;Km8+b)~3%&|LtP`9>0(I z<#&!F%4M5&Y%CJoWS#%e>jr!ND|^K^We2}rcC7gFPkPR0x%RJ9Ri_#+=9nU5Q}LJ4 zugiU5nZB#ei67PpT?{YQ+I5&i3Pwz{-E?pAE&;LFb}M-W4Zb^N zjz3?_IK0g6T79qLfjtR=VH)1CuO=D@29)bJT#RR%n(Wj*ImLC+R`2u&kNd@R?C)N) zOr1ONK*l7;YqDI2Rgy(!o=)fFkUM>KZMXujdx35llimHjOD*o4y%Tb5{gRtn zlWxB?`>&>4wEnvC(bI3sR!5jh@m@Ca^Y6d>@(J?;H=To9uH~L(t2m3 zcZo~mZ7w9)wZ_^USIc|wZ$kfeqiy2Bp}r4f(K;G zl-thLP5C!2<{p=kQ~!A>;$70#m9JRNJv>qO?}3=y3%gC>FI0B^t)B5jcim%KIn~H3 gxz{=b|2M=t=L8ygIc8NbFfcH9y85}Sb4q9e0OmfV`~Uy| literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/default3d16.png b/share/qtcreator/qmldesigner/itemLibrary/images/default3d16.png new file mode 100644 index 0000000000000000000000000000000000000000..de8906a724d67a95b221c5d11f2d20cd5bd42a0c GIT binary patch literal 253 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7j(EB_hFJ8zJ-=7$SfRwR zkLQ)yTqBRPh)hmikg}!oU2=l4)RadmG6$yyO^LKMo9tS|RkoBTqOSSh`O};QzTaPe z-e6$d9}zR#i4smn%8e^ShXv=ng7ONQ@eHvUZzPpTORg* zJER;}I46ZE`|jLMz5>~Hi=)EtR=<)Cw>Yeo=N|hx{44XL<(6}kx4hD0U|?YIboFyt I=akR{01ByT?EnA( literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/default3d@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/default3d@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..7ca04a01eaa5a247ee96d385f0ad7b188af6cd71 GIT binary patch literal 499 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANNfyF6VSLn2z=UbW8<2^47f z7~jLyC1C4P#CTLhDT-5!OUYvu2iL6^n*}dAd1#txUiC9r%GjBp?4uo#p)8c6`zCkF zkN%bwDvlPy$s6^HWNtp5w!rMS>F;~z4BkIB-0bb^>w7j{O^MI-NZ+O(&*ZwlEbbJ0 z7UZ>G%fjceQe248iFUig2Hz4TZ*PzgoGjt5xi&-~a=LG7W6U2uzr_=SZq0K&R-kPE zpI`8!iPF51&hJ~-rYk2c(b>LSE~35i%`1x%?!1RGd@e_r>`I>HMkpw;iTajqR{kPz zyg)Wz2E*R z!=cM2IY_S;;oWBa-ah&Kei4g)*E3K5R`a;KEY4b(H~$gioPe0OJxSlbKjK;UAkZt* zbh&?e=Wap6w)tHrcQ!QUG2P8u(0RL}FY?a;>pO>&d)BKin-aC7CphAO_|X}953R$d zUI-VWPwlK9u>$mhXP02$wUYRSW-!e5dHQmq1VbJnsz0Sis3=9kmp00i_>zopr E0N$S0dH?_b literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/delaybutton-icon.png b/share/qtcreator/qmldesigner/itemLibrary/images/delaybutton-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..5a55bd9f77a7ce1712cdcde0b71f75c6d4f8fde8 GIT binary patch literal 189 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|7G?$p2BAoW(+ms@iUB?$t_%zeD^{#HxNqMI zFgSYX(5Yj`z|0jF&YZb;_Uy||&;KwmFmRLv`2{nCPndZ8!slO0>aTY&FfcfKx;TbN zgeNB~&r#=yY9;OXk;vd$@?2>@oHMtlGO literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/delaybutton-icon16.png b/share/qtcreator/qmldesigner/itemLibrary/images/delaybutton-icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..cd21394e4659d1a942abe0cc1797bd18fa716ac6 GIT binary patch literal 160 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd7G?$phPQVgfdr%jd_r6q7#LQpSh08a?iDLm z96oSh#flZ@PMun@V#UR?XD?*`u47SSQ>@sh3O)($FXU|?YI MboFyt=akR{0L!;HKmY&$ literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/delaybutton-icon@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/delaybutton-icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..7beee2fab0e2f51609d027e16cc92bbe9ec17487 GIT binary patch literal 286 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4rT@hhJ-tuTNxM_>;rs4Tp1V`R;*aDclT~M zxdH|b?%%&+#fpRb_d^&*4^lx8>&Z7cc*+J-hdgfq|jg)5S5w{~=J2_FU-z_}-{R60G%4*~lg$H`_f^HukE_fT^jp67(O0EUZVB5$MT&mzIbb%= z{T}xn+3&m^4|si>IBYI-arQB(RS5Y!sOS2y`H(z&IJ2I7Onyzn2f^0q2ii>+Yp~v6 Ze9;~ldATi|g@J*A!PC{xWt~$(69CiWfvo@l literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/dial-icon.png b/share/qtcreator/qmldesigner/itemLibrary/images/dial-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b3b63e35235dd797157aaca4cd31f9a11de42daa GIT binary patch literal 267 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|7G?$p2BAoW(+ms@Dgizrt_%zeD+qz<)ead9 z3=G^QL4Lsu(i&+sO`8tC|M7QKve7>V28M1=7sn8Z@YoBALQMf8tq<#GOccGLQxf)I z!WpsIjZWsXBbY9p5b|l__>q5Z!Flz0CHrR@mp_YZcyhz%IS<36$*&%-UArodA*`;5 zfvfi2weYu!nP2&;=NbonWs}~j)c-<**OWc$Fq6B~{+S=ty>+rmk9bHLPH-<-A}H!9 p{mimiX~BQ{yuxpv6hDUQFVUIlYIwqPDgy%pgQu&X%Q~loCIC+(Yvlj{ literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/dial-icon16.png b/share/qtcreator/qmldesigner/itemLibrary/images/dial-icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..8d8c7c09b05c2887c856770cc47b145793989155 GIT binary patch literal 243 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd7G?$phPQVgfdq5|d_r6q7#LO%4Gx?sJj1}i zz*iFF7tFvXVdhoSG;8U}OZVUXN?p3cpMilP-P6S}gd^JZ+(urP1O|tP?k-cjmg<%y zP0-F`-@BnhFvlUCtJ6)<@7>vgqw~Apy}rl1sxq6A>nh`-i|3zAo&3`J>b$7a5&TxU zLbj15wJ*=ETXmn^`+%=_K@hu43Zw1C6;)6DzwH0Wyv`)-%;J=VHVh0544$rjF6*2U Fng9_k9UckV>XzJ9m{MnhiKyH)hRUb!=sn?f?ICYhOIzb&AUSp4=e1%%Q^~^Xaclwr4BLL%rlZUNOo! z&t-!%ti!Zj@tWkUOnrLB6$?W=H z$5)^E{(qKz-{;lx$E`n|IDdZHewLl}$A6s4dmMlFZFu>%Ggqi8d0l(Yd#$A-YhJ-!)dgyc8s%R(GBXEOX{?+T#^ABi%B(TSg2BPnFJtyv zZNKHt*$fOaUkxr^PBEU5u}$&}i*gzBX76VU&SsXHF{^KN>+s6hr?B$EJw>*cjIAq{ zs!JvKE>RR%AbPy#T=AI?j>|5eSRC}!R_muhScFmOozqkH>xle5wMpOg_pPn^WxuaK hl%GF2f1iDM;HsVNpMwv6Wnf@n@O1TaS?83{1OSPN@_PUP literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/drop-area-16px.png b/share/qtcreator/qmldesigner/itemLibrary/images/drop-area-16px.png new file mode 100644 index 0000000000000000000000000000000000000000..278690f07f20fc8bd139360ae8ec63b7ccd46c37 GIT binary patch literal 205 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd7G?$phPQVgfdq5|d_r6q7#LQpTuCHyvufvO zU|`@c3GxeOVCLlIlhn}C);n_P!Rv3k&a8aTz`)?(>Eal|5uKdiH|L8Nm=oX=;>y6lumTONTnQ3@ zlUNxmSFV7d6)QjtkOmlrs7Dir2*S0%)PiV;Af|$qD>pO$v1edlFe(Z13ua(oWaE^O zkX2Ar(a_Q{FtM<5NJuPcXl-lnnJ{g^g2hYLZ`gg{$l3E3Z{2(F;?4UHzy7wmd|J-H zz;N2r#W6(VeCeduLQM(+Zr6>Bb%RYd>%3hQS=n3rzucQ$SpVbSga+e+gfhl87VP4N zS#8oT9ePhTvL=K^^f`7;Wi?FWThStMb^~Lxfan~arbzB=4aBg&KEzrQT@H&iAg2C)Bd3w!b!;G1*wQLAE$jx* dqHjN$&SohnwH8koVPIfj@O1TaS?83{1OUtXR2n1sYhn z5+n!_hmb2)pol;OAtXc!Vgj;8h&WsgL;-|^D1#_~h(Op7#c)}O6kHIj<_F{484L^z zmL)-c!3<2ytn54j0^$KR z=S!TMW7bG6rQ7h784ywYf! zmf7e3@@Ymtg3ewz+3bD(bV}>yM30121`p4`@GWa}v%MyAzmYs}g*DqNv~;4w;y6#s z&AbL%rrAD^zZq~~-3*Ofiv;gm`?;rSK24L%_L#`MiSfV%R%5TF-swII9;DuDT>I+G z%}9r{-}yfXA3VQeq0zA=0!yZ>5&h$AJ3%e+8fSHE|0V;4saUMeJf!|TEcoy zZ*N0@D$|F;J+D_zQYh&<6n&ZD{PRy&XIz=eu*dq=%`@dUUsq-phUbQ6mDDd{`Ct{q z)FJiY>9i$`MLRDaU-E<9V3%OSjJ-*Z?}`6j&i(QAhZ3v&-)Zbi{-tHrXfW4)`&EDO zI=_r;i|IS>@cZ|Fzi<3dze`r&)T`j&Yxk#DEm+|`tx99n@pV-jUib68I?VZ^&wl=g Yk_ks*xu#n%FfcH9y85}Sb4q9e03=aEZU6uP literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/extended-view3d-16px.png b/share/qtcreator/qmldesigner/itemLibrary/images/extended-view3d-16px.png new file mode 100644 index 0000000000000000000000000000000000000000..d9027813d0516ac77fa72e7c6f5e78116ccd8132 GIT binary patch literal 311 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7{&~7MhFJ9WUNrP_b`)uS zsQ-!UVn$GgI!D$*1;ZWe(al$L4zup)cyqw4d8)NXs%-N?ClifpP6E=d$qzXmPW1V7 zxKYxg_+4UNs*T)i_ntytYtP>q4@AsePevSM|TY)YJib*9==-9N>>WgH8+4Ug%ayBM{_ zk^5_Bb?vRkG0YjuPF|RkdWvnq^mBV7m&F`NdHOcy0LSVdCt7?Lw|$R)n_|oM`@qIp Q1_lNOPgg&ebxsLQ0ElgbE&u=k literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/extended-view3d-24px.png b/share/qtcreator/qmldesigner/itemLibrary/images/extended-view3d-24px.png new file mode 100644 index 0000000000000000000000000000000000000000..a8a0bf65a4652596c8b2c268c59e413d520b5e87 GIT binary patch literal 384 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|4mJh`h6m-gKNuJo-923#Lp(aSPPO-IaS&;@ z=Mf8=k+HNjL`-VBZIkZP}Dzu~>3T)`7}T+rfB(i$BR;pEhft6S$r7^rv%r2YKt z9d}~F!{2hok_De^(lbK7GVEYVJ0Qc+Ftei~kN<`zYtS{z4cATn$2YEe$1HAoLB`_N zQpM{B`mz@=?8wPJ5Vc@c{(+YP6Hc;Em=)!GF;V^p|A!O>{%5ngPE^+}IxAEYwoB`E zpv%5otCigIvKMUaNHJSrc2M_d_A;eUrYzS))Vq}Gb220aPsTh*)na^LGH;*Z=G6(= zSw;uJt+7kljTyb$RV_3C|VV$O3on67T@C}TIgks!Wk`%(OIxA!f zm*@sEsIYxe7p=I!w&7+oLxkC#__Bk!tj$TkojpUYvWKPZSrjpE_eK_O1r{4)zQ!BR n9HS5V=Dsn#lYQn%^#|@!=SV*-VTEP}1_lOCS3j3^P6-6fu7>FJwvpYS`UmG7VX^KW@tnRW!5vy_t)R@D`B2=>p=3d`ET9|yk$34cq#T_j?x>e0^1b(%1Y@8CDRR4 z*#0O8glFoSD7@7A(DCwTmAYP(r6`ZbE>4@pKT_s1nNRBv^UG!MRBEl|*dnDmYrXve z^Mna=Pq;lORkry2b+$+eLrvj+`!jEJET&-m3y z_hcrxyzAWc>F$|-ZEsYjG%BkloPDDaaJxA4>H*2eniJY(TiLW%SF-udU|{7t^QJ^H zA$6*xMVi#N3upeBvTnPS8p!Q%DebV#)YP z*0RRf@I_kJ@=920O1_#q2(6r-p}ZkGO?mstZY!>hJSFK@nzh>+*UjYJ%pCsS?FBnW z{sR7zbC&lDuQ#187vyjBU(#LraK4l|S2V*Up<|WHS#BItUOD^ZbqA(5vu`FOK53bA z(eA?ZEsnpYeVX=O#H(Q*^M^^hx4WMW&zWobAXiDm=1TmLslF{O?FRV^S#GQr*zbNo zzj(#8Nunh1Cab;j&SV0zW+J3B- zfq_A&B*-tAfsvC-Kvyp~G%LGu=B%sN-v5w3yLc@F14ERji(`n!`MVdRxf%?3TrRF& zwlDhf&HqV%Cj?#0b9k;VbAqMGi(x@ue9NAY8%3?k0SBG51lZp>$g3*OXFQ{4@Ven< yYt5snCtH{7tzUD?yHe`*)1bK9&;LD;SKlEl&A0aY`ECXV1_n=8KbLh*2~7a_y=#{M literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/flickable-icon16.png b/share/qtcreator/qmldesigner/itemLibrary/images/flickable-icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..d7ab9de8a7d76186708ac6459d6c1742d00851b9 GIT binary patch literal 209 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd7G?$phPQVgfdq5|d_r6q7#LO%4Gx?sJj1}i zz*iFF7tFvfAZTosQ?O?J-TOa(@#ZVLFfcHv^;qEvLDLcwxQuJA2Xk`%-p=YhgGaX^TR6kQ|AwET+Du>U($d_nIWz1fOY~8 z!z9*8#_THGD$kjw=uDXN+A~tY(p-46L(#jonJjB6*jmnd`q}M_-}UK&WByB>=b`eC zZDez;LQc=xoOW6(=l7$aYb;0RRAnDoqj*-1 e_aDn2Oo6u1%@LE;A22X5FnGH9xvXlqjrBuiW)N`mv#O3D+9QW+dm z@{>{(JaZG%Q-e|yQz{EjrrIztFnM^oIEGZ*nlsgRb!DK)vH!)K}F%81}XF_|!5Rr)DT=GAp+3;pYiBar>#=w_V?CWv`#zdH%tT^!eBF?X%x* zH9z_OyZ59o{)_ddpJu=QwQ6a2+k6+Lk4ZL%rhIfdn)H!v*4ec60e;IbD?4@A`jzgE z?LPWx{^2MU##Pr}Z*5#)uE#U&v}(cLxO^Kq zc|P{lSH1jFBkMR@VqRpL&Az|$!>*{cY|V^2-j-QQ@T_twIbX)^>ewgVnqwBNA>ySr zSt`g{u0Q2f=|=g_Odi?Yn~pCD2w7WJ$8eB4V#T@pj#vIUBrNdijSAfL+Wf*)?z<1m zcF#WhtZx5u$zNRwYZ{mLdDjFv&D?aXqu*_D-1_ppaf^etyzcN^e)($F){7Z$%B;Vb zH-=nG*?sq2(M|&<*2KSc_EWu%J}x{X-Fxh8+U5wI0^1)o`#e;bthYw-#=a}C;BYzr zvqtZs9asBd4c^8F*;aF(eXex56jYFCG_ygV`>{69BECf_U#tF}7R(jnToAUp^$k-a zLo(0K4xJ4VX0usYV^ZHfe8E;^HP=Gmkoq~1m=deGK`UE4YL03i{gYyJGR0^|ibK2X z!C!v7>J!Rjh00%azCP2+y&-7jlOoGAm)AP!lx9uXpuCUsd4%NlpoIYkBr=Wk9(Dw^ zDZ5GQ{CZlZc*i!mfnkTn^wS~vGrc>1yi;D)C!n~(cLQ6}hJ6|KcVk!O?b%ypBggXR k)Aoh4D$88#SS1)PzFuK)yZEsb0|Nttr>mdKI;Vst0H{7N9{>OV literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/flipable-icon16.png b/share/qtcreator/qmldesigner/itemLibrary/images/flipable-icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..cf252f08b8c45ca10c08ac810c8ccfdf895b8efa GIT binary patch literal 466 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4kiW$h6xih%orFLBuiW)N`mv#O3D+9QW+dm z@{>{(JaZG%Q-e|yQz{EjrrIztFqU|_IEGZ*>IpLJYYGsFdB7bw<;Kht)6B9vrm|nC zV6pB`^7YSu%W2W1b}KsL080{MROAZpj&*hEmAgGI_?B@U`aiR>{@xz-E3ZpEC*Awk zc>C=!%k)JW-+uqy?~%|@zBjIt>Gn3syu$`HC;xdd#C@Ce)T8h56tAU88#PWBEexnw zH%a`#SHI<(ZCnv3vNVS&)ho#`n~gb8gth5Biw9@C z%ujteCJVN(=wtX_QW&MLd_zn4tj8@E1e zwXd2m#|Gg9uUX3y=U#evK5g^cvhIk|*yT}IvqaakIx-y0+n&57G_%{Fx$5)JJ$LhF zFXP;p8hJx@&$r1!Kb6h0Or&_D6yKaEv6Ag|`@uf@PtEn#`EN8T*mf3hmNT+3{N-f7 V+@$hVj)8%J!PC{xWt~$(69B=u$xi?P literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/flow-positioner-icon-16px.png b/share/qtcreator/qmldesigner/itemLibrary/images/flow-positioner-icon-16px.png new file mode 100644 index 0000000000000000000000000000000000000000..67be7b474d619e36129382656abe16a435afbb61 GIT binary patch literal 98 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRdMrH;E236Z!B?bltwg8_H*Q%dGajmMVI=Fwoz=iXV z85kI(JY5_^IHHq({Qqyy%ppHPz2{+;%Uh literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/flow-positioner-icon@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/flow-positioner-icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..fc3deff8499f82eab74fb5da8bcb5412cd84964e GIT binary patch literal 129 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-CT0c(2CW=H5e5bZ&H$ef*Z=?jS5;M=I&mU% zjkO&E0|R48kY6x^!?PP{3=9lvo-U3d8WWQf6!;9*HZ`-EoV#-2!i5cj2M@}#B?y?^ hv9vhcV8OspW_^42qn@jK85kHCJYD@<);T3K0RW~RD2f08 literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/focusscope-icon.png b/share/qtcreator/qmldesigner/itemLibrary/images/focusscope-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..0dff9a075d122cbde167c458fd42c74434a5af1f GIT binary patch literal 143 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|CT0c(hPjegvl$o|cmjMvTp1V`R=|KUn_xc! z0|RqOkY6wZqrksgPk%EoFz9=_IEHXUCnp?We)IPJ|9{`x4?1OE+cedNM@(UAffwtM p#|@coQN{0`dyNce8sK7#K`FT^vI=qLUL2@ZOko`ApE|GeQzKczRoVdt4iMHdquh uF>B8_DAFL%XguQ}j~la2PyurxGef5n$L`LC%2^By3=E#GelF{r5}E+X{xVts literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/focusscope-icon@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/focusscope-icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..b3ffdea6ebb7756ea2a22ce54f136833645afee6 GIT binary patch literal 182 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-7G?$phNU`BwlFX-@CW#WxH2#>tUvTuot!Lw6 z5LTTfDIp;dG2up&%B$M`BnKClBkVyX>mxQcH8wVK#YvbZTwz!xalqoiPC5R-G1-(UW2JN#nSr}Fy XTNQR*YnM0!0|SGntDnm{r-UW|Z0R2v literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/grid-positioner-icon-16px.png b/share/qtcreator/qmldesigner/itemLibrary/images/grid-positioner-icon-16px.png new file mode 100644 index 0000000000000000000000000000000000000000..47b34f9d14b0c680f2f37ae81e1253f6b5bba34e GIT binary patch literal 93 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRdMrH;E236Z!B?bltwg8_H*Q%dGajmMVI=Fwoz=iXV z85kJEJY5_^IAoLmoIh}Y;}E-2obZv96|4*&bG1C@YVF8kU|?YIboFyt=akR{0J4Z2 Ac>n+a literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/grid-positioner-icon@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/grid-positioner-icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..4374b60e7a6aeb3cafd0812fbe96745b4ebb7174 GIT binary patch literal 125 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-CT0c(2CW=H5e5bZ&H$ef*Z=?jS5;M=I&mU% zjkO&E0|R48kY6x^!?PP{3=9lPo-U3d8o|j43Va4@o0{27&Rw~1;evsI;T_Axi4E#J c9n1`_HOKxQT=%hxfq{X+)78&qol`;+0PZIyaR2}S literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/gridview-icon.png b/share/qtcreator/qmldesigner/itemLibrary/images/gridview-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..7457fbd7e19db77934eb430a5080a1efa920fd95 GIT binary patch literal 127 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|CT0c(hPjegvl$o|cmjMvTp1V`R=|KUn_xc! z0|RqOkY6wZqrksgPk%EoFvxqlIEHY{O#boz;6M5Q^=wKSYZ@xvt`^H!`y|SBT^Ym1 Y7kTuot!Gma z5EMLk;=q9e4r?rgnOhSc@Jdu1u#nmdKI;Vst E0NrF8s{jB1 literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/gridview-icon@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/gridview-icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..af233a0df3c94d9bded8a3b7249637b1ec8f0749 GIT binary patch literal 137 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-CT0c(2CW=H5e5bZo&cW^R|W=#6)<4TCfLuw zz`$G*mdKI;Vst0LK_DMgRZ+ literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/groupbox-icon.png b/share/qtcreator/qmldesigner/itemLibrary/images/groupbox-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..5542ecf8bf7ae6b433f2b8cdeaa4529ccb251c5f GIT binary patch literal 133 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|CT0c(hPjegvl$o|cmjMvTp1V`R=|KUn_xc! z0|RqOkY6x^fV}OV+f@t<462?kjv*Y;$v^)8|If(A#umb*{ez#4$KSb8Va`J)kB*sq dB1fVac=XjIPt^BmF)%PNc)I$ztaD0e0suJ;BX0lz literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/groupbox-icon16.png b/share/qtcreator/qmldesigner/itemLibrary/images/groupbox-icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..9cf4324819f919c5b24a1063cebfa5643827ddc2 GIT binary patch literal 125 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7Ts&PILo9leV>Tuot!HCW zP*Q3VoGv4*c(z&IKuDPF2Ltz%u!Mw!A0BQ0z8f&xxw*L1D1?6R64ln|Zw+K_Yh@Hs bPi0VT6nOjM$oJC>3=9mOu6{1-oD!Ms1=sKkJL|y^{Q=wB>ex g(Tfv`dYc&570r@R{m3Q5z`(%Z>FVdQ&MBb@0Gq2O>i_@% literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/image-icon.png b/share/qtcreator/qmldesigner/itemLibrary/images/image-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..318ce0874a5fca9222ab3b7f3d7edaf85f4858ee GIT binary patch literal 434 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|4rT@hhU+WOo?>8N=nC)&ab;j&STQ(2;PKPW z3=9mqB|(0{42(?7EIhmdLb9foL5X<&-4mwIn!9Mp^3?~9oH%#>!oAlYK7INA z^WXpbpEL6aNj|EZGj-1F zo(#`9N#2@D)0Q#_uHIg;XS*}^8AdZP3#L6*^$IyNKYiF2U_Wm{jpHQC2WcsJvOa`o97#KWV{an^L HB{Ts5xJAw- literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/image-icon16.png b/share/qtcreator/qmldesigner/itemLibrary/images/image-icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..3aa46b61062c8ee766f3c9b15f6c92fc20e125ec GIT binary patch literal 296 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4rT@h1`S>QU= zz`!6~666=mz{tiWAR@2f;vJDt)7mj}>%L2u?>~F-?)~>)9?n~385kHcJY5_^B*Jsg zNAfi&@VH)FrD4(3#k}Y5`(SAWt&er{ws?AmdKI;Vst04Mu>`v3p{ literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/image-icon@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/image-icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..cc849189756efbb115f8b61890e97c4e644501be GIT binary patch literal 596 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4rT@hhJ-tuTNxM_76tf(xH2#>tQg#2L&1JY z1_lPxk|4ie21X`UHZDE^5ivP=bqy^;TNm%hgtW4n+Paq3wrMjLEMK>N>%PM$E?v2L z`~Jfh@4kNj_51HX2fd%q85kIKJY5_^JdWR;`quA|gMce{2=@!FqfUQyyrwSN8esqK z_x@S-(+bmbW}j(mk$8W9_T)F`W~MMzs9A6oh-GeOU*s^mx#8Z^;=ezugfkmIT>SU) ztrO>+%Oarc$o@28ZXZG(%+h*~Oz?3hD~i8IQd*k3Ep0v@~!N--7*d z-&;*i#lBK&x{OnFInGZ{L`o7l?xM2 tF)r$?;7a7uF6&-!<-fi{XaE<(LRRg5hGkbc7#J8BJYD@<);T3K0RT7XEMNct literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/item-icon16.png b/share/qtcreator/qmldesigner/itemLibrary/images/item-icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..46d3ec1dbc1188ce08dc766c4f522b424bbe41eb GIT binary patch literal 135 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRdCT0c(hNQXTpBNYzcmjMvTp1V`R=|KUn_xc! z0|RqOkY6x^fV}OV+f@t<4Cv*0tcALk!1FfcH9y85}Sb4q9e0Q5&FH2?qr literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/item-icon@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/item-icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..f05aa57c2f5bdfdfdd75c182a9f4e7ef821885a8 GIT binary patch literal 167 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-7G?$phNU`BwlFX-@CW#WxH2#>tUv;oseviY zV1mlSH7u-J8A5&>+3u=rn^{bSm{ksHDl#@coLaad`~(97%Og*NuXp!-VPIfj@O1Ta JS?83{1ORuTE!qG8 literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/itemdelegate-icon.png b/share/qtcreator/qmldesigner/itemLibrary/images/itemdelegate-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..822cf3e7b8bd78fbe03c6d022b08cce8c47327ff GIT binary patch literal 127 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|CT0c(hPjegvl$o|cmjMvTp1V`R=|KUn_xc! z0|RqOkY6x^fV}OV+f@t<4Dy~Xjv*Yf$q)YXv&c*QaB5u8&^VougXebP0l+XkKVHqL_ literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/itemdelegate-icon16.png b/share/qtcreator/qmldesigner/itemLibrary/images/itemdelegate-icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..b3ed007a0e3c034fe2176569e378f9e72195fef5 GIT binary patch literal 124 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRdCT0c(hNQXTpBNYzcmjMvTp1V`R=|KUn_xc! z0|RqOkY6x^fV}OV+f@t<3^JZBjv*Yf$y@9Xe3*VQsI77C#V0(>{K_-D8zmURJVht6 UOP|}oz`(%Z>FVdQ&MBb@04CBR1ONa4 literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/itemdelegate-icon@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/itemdelegate-icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..cb81308ff8d271d4f55f3f8a44174eee3d83969c GIT binary patch literal 133 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-CT0c(2CW=H5e5bZo&cW^R|W=#6)<4TCfLuw zz`$G*NYeN(2tK6bv1fq@~&)5S4_BU<&uM&1So9%cu79f!&Dx@pD@W;ayE@y>rSQPGj^T;>STmP_#0jm$U3%5&~ a{69Sr#gm)Y++|>3VDNPHb6Mw<&;$SnPDAGa literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/label-icon.png b/share/qtcreator/qmldesigner/itemLibrary/images/label-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..788bef078ce768da67697e1d7ebdc1545307ab58 GIT binary patch literal 206 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|7G?$p2BAoW(+ms@x&b~Rt_%zeD~JXM&J>d(Ny zz*7?B7tFvXAR(_4-PL#S`JbucQ?wWu7>qn!978ywlM@u!Hk`9$X5#6&o37Ba#yGuC yLsN5ZqD62Fv!G>iLbDoEIP-N@b9QqE2E~4@eD{ot8yFZE7(8A5T-G@yGywn?hdtT= literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/label-icon@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/label-icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..7001413d3b01d2cac59688ea17fc628f8c614ff5 GIT binary patch literal 284 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-7G?$phNU`BwtzSRJ|V6Q3=Ats0jqW;S~4&& z@RtPn1v4=63CQc{xkY#P9en=l&!c|bR0aly9#0p?5R22v2@($*O8?iJFbJkceV1R& zl6=1YjnKva8@)639@kSW{AVA=xubcX#9>AcriTm8b6js$5#V#0%$z3&f@r7uYzUTcQgw{6c`zveLNSb87AD%z`(%Z>FVdQ&MBb@0Fp0qf&c&j literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/listview-icon.png b/share/qtcreator/qmldesigner/itemLibrary/images/listview-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..5a2f3c203b49bfa894dba155feb7ab821bc2f239 GIT binary patch literal 148 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|CT0c(hPjegvl$o|cmjMvTp1V`R=|KUn_xc! z0|RqOkY6x^fV}OV+f@t<3?`l~jv*Y;$v^)8|1ZzR#umcGHKXwk{|k|_1N>8L4jecj tE+xgaq@q!YMPmiqn*(whonDL#@$!1tyx3#~85kHCJYD@<);T3K0RZ0GDVG2M literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/listview-icon16.png b/share/qtcreator/qmldesigner/itemLibrary/images/listview-icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..2657bf218149af0639199a6ebb2496f1aff22309 GIT binary patch literal 136 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRdCT0c(hNQXTpBNYzcmjMvTp1V`R=|KUn_xc! z0|RqOkY6x^fV}OV+f@t<3>uy;jv*Y;$v^)8XJ%%e!NC0Q|Nn(7LJZ#ayt)YqI}Ri; gs0u^{d|_nxzDInzW{CMw1_lNOPgg&ebxsLQ0NyVqO#lD@ literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/listview-icon@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/listview-icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..b1d3fb67d27be02b04ab3a9ad3bac6c0cbff43fb GIT binary patch literal 158 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-7G?$phNU`BwlFX-@CW#WxH2#>tUv8NNDlA`ab;j&SV3cu?|uIb z0|SFXNswPK0~0eFpMa=>nx28ZTUcg8W9!5j3pQ*%c>TtMN3Y+1`yCchIER6OA=lHz zF+?NyZl60}gMtX_e9@zE8>ZL)57wF}adcC`%bbRp9~f_Vc6i2uP_xB$xyfq_eUGu`n9zVrq h^sPUnD}P9sJKaz;)Gw)$w zF7fWWtSwp7w4br1?BF-H8OI+htP5Yjw#`9x<{cYHjQ4;TI4Z9h;C+SW;To(m7@RlBJur?LU6z z+KmTKUwrub?fb94@(DQw3=9mLJzX3_JdP(PEZ{OQVP|X%^z7uHG)<+$zh=<_lSL;S zm?jvTho`H!GB>Z{W9O4BG&sOvXBH=;C8@x8$g^dsz|_MY%^MgJ8x}QI7#NvO;9@B3 XnDE-}{YM)H1_lOCS3j3^P6aH! i5#wvg$JVx#;mH$6>t_%ze|NsAAK?wBv zx9?zJVBjeU@(X5QU=g#=$(wWO{fB?`oHI%p7#Q?DT^vIsrX~xxF!8t)ol{`kWa(Ws z!GinBGlfElGv5^oC6WqGD7{Z$>D9*-kfByW(&x$nz7#J8BJYD@<);T3K0RWaI BK{fyY literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/media-player-24px@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/media-player-24px@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..86ae5914acab5d2860d9bf6f9115c757a9923d8a GIT binary patch literal 260 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4rT@hhJ-tuTNxM_oC178Tp1V`{{R2Kf&$RP zeW;p&fkCt+$S;_IfsIRANy{UoqIvnIJ+~h`{PN@1Z#8$9p9~BPF`h1tAs)xyUUlbd zFyLXmxUk%5@BdKC!#We1HhlN*daK{=B(|Sr`x=IwO`j(zZ0%;bkS`R_+s=I9jF7{2 zF9*vvd=9pIx!+7qxm>iStk6ixtj6kIq3J%xNAu4y*(^TM@c$A|=w}871_n=8KbLh* G2~7Y@?Q(Sh literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/mouse-area-icon.png b/share/qtcreator/qmldesigner/itemLibrary/images/mouse-area-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..fe316caf8d823cabf8265da52d95d802d97bbf85 GIT binary patch literal 358 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|4mJh`h6m-gKNuJobv<1iLp(aSUfu6?#6jTr z$M>ruI+yA{;Jy(N{hwys7lxM9;rRvka-qsn6C zjT@%lNiEk8RyCgdGlye#Y301f?$c7=ACTUl{jz*fqSuelVp+egHRe7zcxIY2gZ--p zrAZSb9gHrqcsR{sy6C|n(AvUu(1S;y1C_HG4lJA>;U)2nN5&xP$tw%<8R{EY|9Jjlv+GW|{P{q?oxztP?F0ALu+L_Q z^^|?EaZ%YEt`gO`ee0`5zxo>A?qGb^u=!Gf%3g-`qI>6NHmtV#GI#b3eTEq~ISZaH zWj`=$iG0WZ6Rvld;+Otb{J?VM--o7q3(m$w*4go P0|SGntDnm{r-UW|J!G0~ literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/mouse-area-icon16.png b/share/qtcreator/qmldesigner/itemLibrary/images/mouse-area-icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..bc8725fb5ff92bb7ff015b8cbfb71bb968118e7c GIT binary patch literal 263 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7&Uv~xhFJ8@y|kC>kb%H~ zkM)0MxXK(oc)XzOqeDqy@fXfJH4NqlFWyz!vE%+T?H?Li9;KW3mI-*@J|dTLBKF;M z?iLkKkLj!Kg_QQG->AS|0hL~z3D24)+C0!-!olHoszKohd&NWn1>5c6S3N@+ z7VympDGd>2$Y46Ib$~77_SItU-ljJ80`^^H_RL{QpW5Ds>&C?D%@@wY9duDGs zz$(#wLb9V_b#U|(#$&R_B|Zm-T-DvM<+k_*!HGA%vzV7{`^doKqH<2|2gAe<0T1K) T7jI=?U|{fc^>bP0l+XkKp6hI+ literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/mouse-area-icon@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/mouse-area-icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..04a25e13db34f237ca4f1500cc91cd59ac80499f GIT binary patch literal 755 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4rT@hhJ-tuTNxM_MFM<6Tp1V`{{R2KeH0*t zz}u(SofsGxqDzAOf*BYXnV4DFxcG&HrDf$66qQuf)OGa@Ow2899G#q9+&%n)LZaj1 z(=sx13QH=g>zZ3SI=gzNPM`{db+*Kgmw|M2n4x9>lG z{`&LxpWzFJdkhSW#-1*YAs*gSFFg-CefO_=#}>(jdQbLS$xShvRQeY^YzS5nr*D%N+#LII2)OjKAZ?-i+UtDCkrusLWt+ZxeWM zL!imw1EZg>qQgBAruYtqb3BR}dtx{?=(Elf5n1#=dDbqe4Y}0@vt>{6&JGOdZ2I08 zw*RYqgQ%v)rUP47yY?~4ymo2+obn)I%Fmz$_olLZ3VZx1X!{KhhJXIcGH-+9aVw)uv6!+wRg4nMc7W?*1o@O1TaS?83{1OTTslD+@{ literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/page-icon.png b/share/qtcreator/qmldesigner/itemLibrary/images/page-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b5ac87e899124276f452c1709ffeaa4488857a26 GIT binary patch literal 190 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|7G?$p2BAoW(+ms@1_3@Ht_%zedw1{Nzh}>Z zefuEj(Ek004<0;r`0&YN$IhHMaqiTq6)RS(UbX7NnKKv9o|V_U!^XhCz*7?B7tFvT zZxvND@4%gRKmKNZJeSA7z+mX<;usc~x^Oywnlb|e1B0ilpUXO@geCxFy-T?O literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/page-icon16.png b/share/qtcreator/qmldesigner/itemLibrary/images/page-icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..bc6810b6053c29e358e0e7fe7717b695f07c29c8 GIT binary patch literal 148 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd7G?$phPQVgfdnK1d_r6q7#LQpSh08a?!yNT z96osP=%GU^R;;*q_N?RL)|U(n3~VJqe!&ckOy@3K6|LX5gn@xU)zif>gd;jR!Nc3z zJ2ZiTNkQ3`tXQ#P@9y1) z4;(mh=+MzahgPguaq;X~_jemp7#JAXN`m}?85o()UAn67c}IKX-u>zF%km!>7#J8lUHx3vIVCg!02c~N A)Bpeg literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/pageindicator-icon.png b/share/qtcreator/qmldesigner/itemLibrary/images/pageindicator-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..edb6b377bbde35765c6982742927d65c2c83c6a9 GIT binary patch literal 179 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|7G?$p2BAoW(+ms@asfUet_%zeD`4R8fdfYl z9XfjG(25l+Ri9KQ*%p4~YM2sJ#EYM(Mm?)?@>kE598v_FagQu&X J%Q~loCID3rGE4vf literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/pageindicator-icon@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/pageindicator-icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..7be0ee813ba27559bd1823f23c4640509aedb4d9 GIT binary patch literal 207 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-7G?$phNU`BwlFX-NC)_YxH2#>tXQ#P@9y0z zR;)OD;J}eXhmIaPv|`1Ii)YWizU2Lvfq{X&B*-tAfsskz+@-5o>Fj+B3=A=zE{-7< zr;`&B5;WSIjab=I8YZmpIHY=US%dQ;WzOj=U1BMmBDxwvC!zvWCTv~crI6`3^##Wf z)r(3E&WjEUPG^bX-o+Bb-R8pG#=_I%#S_|@q)~pui)UH_JA>lET-jMobw?N&7#KWV L{an^LB{Ts5hRjF> literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/pane-icon.png b/share/qtcreator/qmldesigner/itemLibrary/images/pane-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..62ebe487ffe431d595456f891f6a75e93f8d821b GIT binary patch literal 93 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|MrH;EhFd$=?`B|NU<>dGab2-u#l^E{`?K1A vFfcF(dAc};a6~63I52(sUmw7#%*gP2hiWhHWaA131_lOCS3j3^P6KB&) literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/pane-icon@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/pane-icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..55bb116a699d71b38eaf1c909f9ad6edac4625a2 GIT binary patch literal 96 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-MrH;EhTJpRM;I6w*aCb)Tvx1Eaq;Zg{;c*N w3=9mSo-U3d65+`S3Q`9D*_)V^Co4%YFg)GA>aO?pRFDD&Pgg&ebxsLQ0B|ZBi~s-t literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/pathview-icon.png b/share/qtcreator/qmldesigner/itemLibrary/images/pathview-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..8dc82b8196a6789ce7282ab54d63da193e02a7fc GIT binary patch literal 457 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|4mJh`h6m-gKNuJoCwRIzhIn*dy}Cbpile}> zkLP98=K5Z7YM!LTwq+wn1V@vT*Tu36S`q?|6B{1PPnhbaBgCQ@cDZA1M*>$gXGDg8 zXo!H|S^j`GJxBK*dirVKy}diVA2RFjK78)Y_qmZ}%Nb|>oIe2ptVe@6G)y6G?e zvwtaK%br`hc5&?a1?#tS7!|X9|6rr!SHAB1RL8V=M`W%U#y;D;_}tW)hj%pH{?W)S z_;K5r?b)41n>(GO%&VA|7sRbLw>h%4<42I_e%1*yoh`aG=Nz!$OI~sIt+rgEqSCb8 ze-ou{)%StiqLC2S6!)NtKPWi>c4p|E_2z}1=TZb4a)c{o?&Lj Qz`(%Z>FVdQ&MBb@0GaX5TL1t6 literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/pathview-icon16.png b/share/qtcreator/qmldesigner/itemLibrary/images/pathview-icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..a6a61f61b21f55200878d68cbd48c79836b6feef GIT binary patch literal 320 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7**#qxLo9mNUe@(K;wWd0mVHeI*yBT7mfZF5^w z@BZJ_!LD{@?#%nP=`$E-R!*H6{ck}d(+!75Ow)I`GoP>M?C77@z%=uiS(rh0l;mO7 zil)UKKVEUI2ui!g^+n*h;Ku9ud;Bg0rI~3dPw{n*Sd-9`Ae$~AXm0X(Yc|jIir0y0 z;*Zxpo6FE@7g=g$tp4%VEdFmVu1w!$r4hTzR0YQ71- amwxxyJx+9$X&(av1B0ilpUXO@geCy{Zi$Wn literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/pathview-icon@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/pathview-icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d654a8e7e62d6c015bc1d57c63660cd26792f324 GIT binary patch literal 864 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4mJh`hH$2z?FYgr+AsLNtZ~Ax0xQZPA zc>Z11ln3jCnuL0l{&fnuw94$!a1jvo|HoJ<=j=DjnUC#Haln-o;pfz;zIY%S~+Qd>bZ>DUu%CXUi&X7El;M%X6sLX2bY?@x4F5`AO1aa$K=b3EQ3p# zC(gXBkW60sl4ahZ&w?L+3nZ^TV#7FLX7op`bNR;e57h1H_@TAkyCCQ5#MMjym%S+|@x!^{-{$D&F&MN4Ed9SfR6E@);ZE>kqhM+PDk#u059V zd62Va?Sniv|Dm5joX_Y?x+V9>0TS3F^<3;gqX zdx?pyoM3<0*UK{+#Ag2Fd6#c|_Q9u)ANx-IxENd>StNJh;I>~S-m|J^P7 zZ1$?Y{1}ImDeil3v%No6Bv`Oz@xFDh4&0h;Vf=rkb=m}%BXedRhsVdYCd|COVfk4TNx|#klDB{Gq&%y#xtliY=($yor9~&qOun)F z1l!gMB_+F+uNE6JrhZue;F{Uz**9X3`zu#3@R55LEOU8#1$+5b-xi)whnZh*Hntnw zyS9b>yW0_;*!hPo;^dE?-LY3EeXFT|a=Oc#x7YtWu%tX&k)#|KGR?Q^$Z@fl%ztsS zkH*=^2-@pEyH^_QGyOr9Yud9*+ZxPQeT)3mt@Qh+lZ(tc*<5ikkqI+j7ntV-=j`~{ z`s@1xiF?6IGVXVE*#G`;adKScqq7wPjhC~3%nn)-XMcG44-WR%&-T8$pd0*u&LdAl zv;Dg@YgT$puWOb+{Ykh_=jqP?{atHsJ3nKqXITIEZpV)-jlio5t_xN9Yzopr06dc( AZ2$lO literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/progressbar-icon16.png b/share/qtcreator/qmldesigner/itemLibrary/images/progressbar-icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..6fede21d8c25587b5fbd68f10760931a80a84e28 GIT binary patch literal 92 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7ls#P>Lo9le6C_vKYO zXm)h?Q|;wARY_@s0KY+g&<5=XcAN|h41X>ce!r*1eQeIuk2@#XEag3V?k@Abq6Bkx c1_lO(xX3CNHuh5!FGhoSp00i_>zopr0FOc^4gdfE literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/radiobutton-icon.png b/share/qtcreator/qmldesigner/itemLibrary/images/radiobutton-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d38170e22f79cab05291702503bae084452c6857 GIT binary patch literal 279 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|7G?$p2BAoW(+ms@x&b~Rt_%zeD~JXM&J>68|+gA5)u+vy!NJ9e1CU0cmi+X`+Iv68NBS73cob)gz$A4i7*HBxNQ_U rYN^5J`jKhX^MF0QQVJalm>8-uHZ=Ey`7UK(U|{fc^>bP0l+XkK0Bdar literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/radiobutton-icon16.png b/share/qtcreator/qmldesigner/itemLibrary/images/radiobutton-icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..07b46a8ab084593f3a78e8694b53ce694d8dbbaf GIT binary patch literal 218 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd7G?$phPQVgfdo_nd_r6q7#LO%0@JGw_cjb$l+4X?5M!ZoNU^$ zF}9(x)LeqaAmaVKy*k<7-<{>Tx4T@!p!EH{Lk88~mDt$U-nEttd+(Q!kZ^l1_rV75 ngQADG9_DR{X523FjE7-O3=9mOu6{1-oD!MEamT zaeQvraj`=NJi+2~r||7M`2YV*nHgmgg1b~F-rFpDlrLdo$Xx?{rPDSgp-u7~uLDJn zS^aamzg4}kAgf&dMdId@(K)_9`sVw85)6L#sXKIEg04=zzWoOEpKgI2>~V`uh07oO zb;4%O5$?--clA~*+Ijc=GwmDe%I|fVM=^BB{-5*PsC&_xH|P7-CoycjYrE`w))M#X zFlK|gY0e+klouGZD^G~nx2+*mWrdGTqZo_YDz3Tb%(t%dDSal(w)I`?{qJ1{31P1< ziB9P^#3nUCKx2iSoPuBs|~GS6|~-v(H|9yXE?m*U#8N@DA_^ab;j&Sg~To#j|Hu zU;`^xu7n7~84xZqxpL*o)vH%uJbU&)@~toi1_sfRAirP+0Ree6O#?HlRqHqJ+{l30^vl;^fLyV`3V~9rZ-9Asg1_KU;g;#ey4AMUJKRzqEN6zP0!@0~uDhv*` z7F>;UshFnWHJ|>SlYOhwfvXG*3=E#GelF{r5}E*jbayKN literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/rangeslider-icon16.png b/share/qtcreator/qmldesigner/itemLibrary/images/rangeslider-icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..3be4624ddd42a7cf81f6fc446cf4cb3c2701142a GIT binary patch literal 231 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4rT@h1`S>QU666=mz{$nUFCecwWyak33m32Y{^Mr}^Cwe~ zU7jwEArj$Rd(4Fz6c`R9If`&gJ@_AAT99Iv{7&dcYvSqimWQ|8x9mNzs_NjvSqnmw r1Fj0__J+m&VCi1^)zkiOqqy5n7Wapjvga}|Ffe$!`njxgN@xNA1?gaw literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/rangeslider-icon@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/rangeslider-icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..aee69b3302f3d2e198bf43205bed9593c06cca74 GIT binary patch literal 282 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-7G?$phNU`BwlFX-Xa)F$xH2#>tiTIau3Wix z?b?fH&u&~}G>d_OfvY6QFPMQ(!)(^l11InQdfVK$l7WF?s;7%%h=qT0f&^IYB{)WB1ZV$1X)?=HLh^ktJFN9m@_n zDl|(rO+NFW?ZFa*C5ntuMFBdlg?wGkfqNPrSw3Q5xU(w2l0SUkM+OE422WQ%mvv4F FO#pUhH@N@+ literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/rect-icon16.png b/share/qtcreator/qmldesigner/itemLibrary/images/rect-icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..72893106ae9ca6d29b2171d7c243b3c54b87063a GIT binary patch literal 135 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRdCT0c(hNQXTpBNYzcmjMvTp1V`R=|KUn_xc! z0|RqOkY6xE)J<1i=>i4@26ayt#}JO_tiTIuKij4- zFfeeH1o;IsNNYN_uR8VS%danwD(n~-7z#aI978NlCnrd(aZpVb<;o~->gZl~h@nxT z@R^oR=TkNZ&eNe02U6JBj%noxoo1DY2v1~XpTnzgdwM5cv TUY})PU|{fc^>bP0l+XkKm3Tid literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/repeater-icon16.png b/share/qtcreator/qmldesigner/itemLibrary/images/repeater-icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..775a57a38c3c305f559ffbfbb7435a48d6635f00 GIT binary patch literal 187 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd7G?$phPQVgfdq5|d_r6q7#LO%4Gx?sJj1}i zz*iFF7tCM~&@f}ggJ-WkeE$05-?cw!+6)W~`kpS1Aso@kPhO`b{5ySM(wP%KWsfBM zZcQlYZk9Bc*m>h%&-92Z2bMnQIvLDd%*LSlPD8LL&DxBCfq}u()z4*}Q$iB}$wN^- literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/repeater-icon@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/repeater-icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..bb541b671122f8150b2a0c22788a4a85cd5a103f GIT binary patch literal 196 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-7G?$phNU`BwlFX-$OZU>xH2#>tiTPdr+>W8 zz`(#+666=mFk!>p2hTr!`}6TZ-&+O-1|Lrs#}JFt$q5NFI#~@23?59@2x?-usvyCv z_3!`#qYszCu?tf&3L6=|3rR3*P1N9EJk(}z%;2--OCgJ9mUM-_wyP(&wdc69WiI7s aShcC==fYV(J~A*cFnGH9xvX=8*@T4}20&xD+fi3{kSU zESaXn7{b?OB*Glfqn+l&ja@KN-15fq}u()z4*} HQ$iB}V)9g( literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/roundbutton-icon16.png b/share/qtcreator/qmldesigner/itemLibrary/images/roundbutton-icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..f6f3666639ec5310c11dff47949f7863df668792 GIT binary patch literal 186 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd7G?$phPQVgfdr%jd_r6q7#LRI0FJGqEDQ_` z93?@1!3=z6vzDH`|4Y!X)_QJ&%S*B`76-(-75wLhO3?~jv*e$=S~afJ8Zz? zJoRP8k?8OHvo3d<1;6>N#>n3(ba9s7PD{0CBI(Pmb}hI%ul~a>+lk+|eQL}4&0Lwx z^*+dF^KyA6d7-wob!TdvT{&vS9&A)YRov;vdlwwX$49a9XPmDmpQewg^c z`|x?LdYKrhf{zEE_L>XaSyaIBz>t|;Qf_L2#N)z#evz7#ht7iXyMH7c4{x`hCVcW& bG4sEflmEWjeCam>0|SGntDnm{r-UW|{k^e- literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/row-positioner-icon-16px.png b/share/qtcreator/qmldesigner/itemLibrary/images/row-positioner-icon-16px.png new file mode 100644 index 0000000000000000000000000000000000000000..1c5be822459c59606c817724d97adc2073935f71 GIT binary patch literal 115 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRdCT0c(hNQXTpBNYzI0Jk_T>t<7UsY9gaR2_( z&+Zp8FfcHd1o;IsI6S+N#=yWJ;pyTS!V#UE(9j&}aKOOS)TqaVX$>zQL%}c6*RPKJ Se#gMTz~JfX=d#Wzp$Py3h$FxN literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/row-positioner-icon.png b/share/qtcreator/qmldesigner/itemLibrary/images/row-positioner-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a7e654c9183d6e77f3a67d2b2fb4b29c5983a7de GIT binary patch literal 120 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|CT0c(hPjegvl$o|I0Jk_T>t<7UsY9gaR2_( z&+Zp8FfcHd1o;IsI6S+N#=yWJ>*?Yc!V#UEpun>wjPW2B+tmvz6Bi0zS-QYYn2jOR YLjLTz&zxr&7#J8lUHx3vIVCg!0KG#c8UO$Q literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/row-positioner-icon@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/row-positioner-icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..48c99f8c0e05d1e4b21244068048f1ad5b707a37 GIT binary patch literal 126 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-CT0c(2CW=H5e5bZ&H$ef*Z=?jS5;LV+`s?y zv-^b%3=E7VL4Lsu4$p3+F)%PFd%8G=XapxGNSsL!l`&aRIIXQMt*yJ~CEFvxnkIEHXUC;#AQVwczup#IY5U~W6pve4CGYa0p~PX4T(5TkoJi-CcG N!PC{xWt~$(697~oB8>n5 literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/scrollview-icon16.png b/share/qtcreator/qmldesigner/itemLibrary/images/scrollview-icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..f8ca7a368537023c3d2aab9034a7b7d582e63687 GIT binary patch literal 116 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd7G?$phPQVgfdu#id_r6+D=TYiYL+ftx?;tO zi)YXBlukX$z`&s3>Eal|A)8#l$jr{hrs2$R%wU0;Gh>tHR##Wo)YP;z zH_x9xf6<~vD^{$yc=qg5>u-t-3=AfoE{-7$FpQ*E2s x(ybg?JcW^&&taO26qm=)SEcO5Z literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/slider-icon.png b/share/qtcreator/qmldesigner/itemLibrary/images/slider-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..bd0a9729bea5b8c361644c46bf9755b9e25aa10b GIT binary patch literal 190 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|7G?$p2BAoW(+ms@>H$6>t_%zeD^{$yc=qfH zB(QSjN;q%j%9R(-p1qko>mmaK16N6qUoeA!fV{!Zy?ej?68qrufPsO*#?!?yL?S#n zVS#c&=bTpz&nwnMU1dtsV065?D)clhyVZp literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/slider-icon16.png b/share/qtcreator/qmldesigner/itemLibrary/images/slider-icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..a08622df89c7e26c95037576756b0438b963784f GIT binary patch literal 156 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd7G?$phPQVgfdr%jd_r6q7#LPyfR!s(UOaoY zu>9gY1_lPUk|4ie1}*{ldEeLP<(y<-V9@e(aSY*zPEJtZHQ3e0(IBalnaSs%rK_tO qxpvWF<|7_S%!UtwJi0pB84lm%ytU!k?PLZ91_n=8KbLh*2~7Y4@igNA literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/slider-icon@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/slider-icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..93842e4cddee2520e82323ed034328f7e29b39f0 GIT binary patch literal 227 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-7G?$phNU`BwlFX-Xa)F$xH2#>tiTIau3Wix z?b?fH&u&~}G>d_OfvY6QFPMQ(!)(^l11InQdfVK$l7WFC&eO#)#G*GjL4vheWJbxk zf@5lWySO-*43A2#b`UzvYM>Gk-ss5C)*|YXWZ2ds?vlhA^FU8zPSjQ{&skgw#?mJy4)ZXUGbJ#KNayWf1_Q95qRjY0V zUyXZwP_5wpfxPz&bqew4Cx!*j*4#0F&#Z9fso!I%3f4NMx6%e*#k-hv!~JLXo_~Mm`R_gNtLHtRb9?sOxmzc^ zI$LUJeEm_VP4Dpnd$EUX>mJ$cJFYJH@$w$7lxJmg4ov?cB|PI;sgCARx#Qe7TEmqd zZ8lqOf1GXJA^i&e`>sd!%XVAzdalviJtw@ed?U~NNBhpqIgHAi{u4lUF> zG3Wb&HJjQ^D?(~@ybF2vKX&(VJ90L04(sI$vL;h!>L+&ZD&E)8vCy{eI;+uV<~R8f z>KpQJeEed(`OxzUrt_{xLfC7(x%u>y%TMP1D>L19H1f^L4dT)tdKu?7lz#reFM6%& z&D&(@zSY$~Rx>V`$@s5c_8wo(@%$TMiOPNt*?Ur+9hUV}X6Cwco5$;&N8;Q=$GDPb zXicsXTy5nL&L*xX6?if9e|TB0?7ajut(%<-4gY*)_vw#@CL`{dA-(O<7g zo=^DLaCP7PKI>-*f!$YEfACMfdiL%XQ(Kib`{*^Vr~aC`Y0(UA@0!_0#j^MEZsw;x zo2>iZD^cUjOS^N4&T0Q*&T#X7*v$IJW|deDPr6aD47>89CBGhSS2C6lIDc&?*W*|B z?sj%|{MgO-Q$WA$K>o`1vleWs<%>3yCG6?%|EOm6FoFBS1%`rXezSe@2d#IouMZV0 z*%jD`caUA6?oo{hzW$L;98VN0;`Vu3y>yl84Xz$WJ}D{DuFw b{o#*k+A7TBwfPqV0|SGntDnm{r-UW|Mx;QE literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/spatial-audio-24@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/spatial-audio-24@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a518cada63b0903703fcba02155c674e2a6b3abd GIT binary patch literal 1536 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4mJh`hH$2z?F+_gmEe+8gQX`;A4{ zu3dZQ)C!Gup-@={A2vB=H-_fMI*0i!FCrhP6o^b+Q2ut^ zblGD8178=u`)_#PFwWcX&h3G6!sZT>=T|N>mw$Q2w7KVeVnRzt4EM!})y!r0{%?7= zS!t8q`aY4pj~ipJ$!aS7cE0ASdc%r2X3Kr=kgZ{%-WM;kh0f*B+5E2b!TJkZm&z@y zdT{v0FZ%}H9qK#eEtHg&2VJ}8`cPJa-G{a9h>KX4hqK1?b9bDA8`vIP6q%^KWBQLd zU*8|tdu0346Xs0bE>B+D*jT+~4=I)Tz+=EF;rTmSmHT?b{07|vYzebFo_t%CT9_jD zOuCKfJ9BO0?z3*J?`GMyTiP`ozI*cB{Y&N9YZ$IEZEj3- zXkN8%V)x#|pDk13c|=Y0crEfaH*P()^DW0&2DOKC&Tco`781I;Sb(Ww#hp#Z5?7XP ze`Mizs4(xg!``j)8I2o1?Xt3ju?I3|T@%`U^R|>F`6bsspTK+eo(=o? zOzXaEQ3ExmW4DgT3GU3>Uz--Pc-8l33Z;wYY)ad$cJ8FbyNw$+3bOiF9?%z=D10FE zH6M54;#ILHzVm(A@I;OKUfJZrPak61-FJKte~^&$QJ+&)cA>PMI>*t&2byJ1RNU_E zEIePD!SZGMxs5Fo{O`QyOjK6-|6q=fh90whnZjlLgGUb+zm*SMF>kI9$Im(2bCBI02ytjlzZG)LQ`{%zUDlZy6+I>G}nC24W2*K4D027m;{7>pYqXc-ACRK zF~JrmL2Zt($Qx4?KRF+``F+O(qkG3p53#xzJdXVSb#~CE<*y}+EB9=@KG}wKUbeT( z!NjjV9ACG6%i(L5i#+Kyul!Vc?p~qCbEE9~^|mrj(DPqXUB%f>Eh(S|xK z{~oHX|HOFB%vEI0p#v=!6ter-rJO^v8{ePco4UH;>?$ort-Z|BQzX8Byt&b9PV~B` z$0i)y{WGQGBA*y@FZ(lQ#U%%1ub&V7n)*OCCURqhM%{^9D(3S0WqUM^NG?uV()OTh zBZGbVg!$dtqVG;P+Vxq*g#MngN>9h=eeU;=tL#aS=Vd3eyxDdx!#nSQ>fHygrn{b& zUu7lvJyp%0Wmem~14f~9h2>3d|6{SqEL&YDFU(lxejsj6@yo8&SDU@>9#G}a)KGo- zp5tgq-sWq|xBEZ7xA^mcdnZ<d%&|nqQdt5p`w~6ubWoi^*QkO z^}&QWtKa)7Dt_G2#m(2xy#MjK4|5NEOLA9d5#9c4|Ls5Ar@EFLSn#LiG6MqxgQu&X J%Q~loCIGcv+4BGZ literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/spinbox-icon.png b/share/qtcreator/qmldesigner/itemLibrary/images/spinbox-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..37277c5e43cc5d1da5c539329739159c9016234b GIT binary patch literal 144 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|CT0c(hPjegvl$o|cmjMvTp1V`R=|KUn_xc! z0|RqOkY6x^fV}OV+f@t<3L5L+wznlvQ>KXhV@_9GB7YOc)I$ztaD0e0ssj9DnI}L literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/spinbox-icon16.png b/share/qtcreator/qmldesigner/itemLibrary/images/spinbox-icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..f88711dd25f81cc0083234d51cba9f1bbc521e10 GIT binary patch literal 151 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd7G?$phPQVgfdu#id_r6q7#LO{fn!=iQ49N7@};wdIT;ujj6Gc(LpY+76BL+sI50MEp0s(=q|S+4&6AsinVY*NaWqe6 r?pU~}=|}>Dp-6-P&yF%B1_rU8Tx-ixwf{0OFfe$!`njxgN@xNA(}65T literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/spinbox-icon@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/spinbox-icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..b62a3bad512120bcd00c1e7e6aa1c94540de2374 GIT binary patch literal 178 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-7G?$phNU`BwlFX-@CW#WxH2#>tUv}&lgEaJ3k}{6O7v_GakaC26*{c3K!$-~*3N+1 U($@Fo3=9kmp00i_>zopr08^|qYybcN literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/stackview-icon.png b/share/qtcreator/qmldesigner/itemLibrary/images/stackview-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a6ced34925bbd38f191393cf9f9b4e53a18f010a GIT binary patch literal 162 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|7G?$p2BAoW(+ms@`~f~8t_%zeE0Dl3Eukm| z1_qXrAirP+MgjTK*}|L*3=DRjE{-7*;mHXJ2?dPItZW7aOoBQr7EWFQeS#g!A2G4= zDe$@IG7Ha?5m!0zU=0hqmcVPphSoe85pjjxybSt$BCRgX=NB?CFfe$!`njxgN@xNA Dp*1Js literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/stackview-icon16.png b/share/qtcreator/qmldesigner/itemLibrary/images/stackview-icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..0f19d0efa3e7202c191d6b4102899c27258e0074 GIT binary patch literal 151 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRdCT0c(hNQXTpBNYzcmjMvTp1V`R=|KUn_xc! z0|RqOkY6x^fV}OV+f@t<4CbCLjv*Yf$q9e{&u?t}$Yecb!vFvOxtnt`UWl;M1& literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/swipeview-icon.png b/share/qtcreator/qmldesigner/itemLibrary/images/swipeview-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..031cb27c367c05b5853f5229142b77ebbdefcc62 GIT binary patch literal 163 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|7G?$p2BAoW(+ms@`~f~8t_%zeE0Dl3Eukm| z1_qXrAirP+MgjTK*}|L*3=H<3E{-7*;mHMz%<60g4FNkW1Evb}ZJs35^qA$DvW3G` zjy_>#?!T7jytFv@W)&+)^fteN7@};wdIT;ujOgvp2LpY+73mBQy*%TNA&v|tTuto4FF{N5C9$mnrE+!;# rPV2?3xl;wgEVMN>8*|$&nHe0U`OU4(uIex_Ffe$!`njxgN@xNA@!uw@ literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/swipeview-icon@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/swipeview-icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0ccb978c46996706aa482cba92f28a43da013256 GIT binary patch literal 184 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-7G?$phNU`BwlFX-@CW#WxH2#>tUv|9C}GZ$aJ@%Wd(gyjhg3=F=WE{-7*;mHXL^a57i;$=HMS3+XO?sDgY ze9UaUcY~D|?onfVpUc#9Q{bFf+pA61jDsMG5EaKejFn7|eExXpdI=0f?*}mr0$7}i%>Ri9NJJhGCP5xiWxV*(` VVynn4c?Jds22WQ%mvv4FO#p!qjFbQX literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/text-edit-icon.png b/share/qtcreator/qmldesigner/itemLibrary/images/text-edit-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..068ebeef0f1f0d63062f063a48ccecc8415cced6 GIT binary patch literal 150 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|CT0c(hPjegvl$o|cmjMvTp1V`R=|KUn_xc! z0|RqOkY6x^fV}OV+f@t<3}&7#jv*W~lM@mWHvIoTpV86qgYkmE|NsBnH!P^T=Fc7& v|7Pw)4Yq1_wn-WbQu#QzR&UkNV`g|8t>`KE#(FaY0|SGntDnm{r-UW|&IB#* literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/text-edit-icon16.png b/share/qtcreator/qmldesigner/itemLibrary/images/text-edit-icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..b96ed468cbe2ab1f9c20d615d5c32fee9ac274d9 GIT binary patch literal 169 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd7G?$phPQVgfdu#id_r6q7#LO{fn!=iQ49N7@};wdIT;ujTs>VJLpWw8r!X*yv9n1OBqTH$M#eN8na|9_A*j~I@6d53 zVdwYGhxeHcnpqDyXru)t2nabO9`OBF-p{y3{tN@7+YDod`?f;9*XFEgV_;xl@O1Ta JS?83{1OU)XGA#fA literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/text-edit-icon@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/text-edit-icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..3b7cb6d5da21d3171d560ace8980d253db22b487 GIT binary patch literal 193 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-7G?$phNU`BwlFX-@CW#WxH2#>tUvX(4$~(u^@=S Y!e3{L5+nB>1_lNOPgg&ebxsLQ0Msxa5C8xG literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/text-icon16.png b/share/qtcreator/qmldesigner/itemLibrary/images/text-icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..ee7cc512cb01c6470423beca7e94fe0f0bd40646 GIT binary patch literal 141 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd7G?$phPQVgfdu#id_r6q7#LO{fn!=iQ49N7@};wdIT;ujG(BA$LpY+73mTc&SPpZZ2unD4jDbmk*(LBqLQqkmVGxT^ gghQB>q6EWYcEP?AEuqgD7#J8lUHx3vIVCg!0JS709RL6T literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/text-icon@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/text-icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..1df8f765de423fb281f311f311d019c86651ca8f GIT binary patch literal 156 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-7G?$phNU`BwlFX-@CW#WxH2#>tUvF>p-{uB(489Ld4SQ0iMPkA x$LoT3lP4#Kf{>fTG^LoPqpYkJygOJJzDGgTe~DWM4f*=;Jt literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/text-input-icon16.png b/share/qtcreator/qmldesigner/itemLibrary/images/text-input-icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..3ceef6d03711e720d60cc938ddaf9af9b0f4e5eb GIT binary patch literal 140 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRdCT0c(hNQXTpBNYzI0Jk_Tp1V`R)E2EnLb$t z1_q{*AirP+fozq#F$@e02A(dCAso@kNeoQOLLCmUr2UQC%jRWZ=9AFSQE24h5XtfA nebP0l+XkK0eK}T literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/text-input-icon@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/text-input-icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..666644d2d300b9afbfd8588e64bc13cff6eccf01 GIT binary patch literal 170 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-7G?$phNU`BwlFX-@CW#WxH2#>tUvzE{-7*Q5V2ILozP_00Im6ct q3R5&U?3i%MP$=P0s#KXc0|S?w-EWsyC7BEi3=E#GelF{r5}E+3Jt_tO literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/textarea-icon16.png b/share/qtcreator/qmldesigner/itemLibrary/images/textarea-icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..4afc1fbab56a1466a0bab865a88d16c252da0a0d GIT binary patch literal 133 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd7G?$phPQVgfdu#id_r6q7#LO{fn!=iQ49N7@};wdIT;uj6g^!WLpZJ{7c?@l>F}hhDg-FTy0B*THF4{;ZEoNy^JZMT XSe_|6TK6Rb0|SGntDnm{r-UW|dgdcK literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/textarea-icon@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/textarea-icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c32ecc71a9a1d084ad0989bd4def630eaecefe9e GIT binary patch literal 163 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-7G?$phNU`BwlFX-@CW#WxH2#>tUv!cjQ zS0;E_D85k%I&{>vk@4=PMAe6DR1O?)Si9j63$qp@LkTnMTj3bBFANL}44$rjF6*2U Fng9>qFs; vlC$&iA}5jKksOi7SgqNQvTDezVq!QgD0c6;PuETc1_lOCS3j3^P6N7@};wdIT;uj^gUf1LpY+76BL+sI50ME?(W_^Y0@OF=E<8U2?sQ_9z5yF nI`^Rti*RA1!Gl|!3=9Hqxh6O09l686z`)??>gTe~DWM4fV)`vK literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/textfield-icon@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/textfield-icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..e05fd41b9a4b4e4d751171d16b79cf2582a0d016 GIT binary patch literal 172 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-7G?$phNU`BwlFX-@CW#WxH2#>tUv@muZmUOjp&o9LdQT(jeq#;OpmM zwpm~TgLIQGla9-zmhNt*?$Zk<7la8VESkmc$s7Ad=!!Sb;)Vpw}{{LH|> Oz~JfX=d#Wzp$P!dJu$ri literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/timeline-16px.png b/share/qtcreator/qmldesigner/itemLibrary/images/timeline-16px.png new file mode 100644 index 0000000000000000000000000000000000000000..d4ecf00031f09ee20882ac301eb663031c71c0e4 GIT binary patch literal 389 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7eLP(pLo9lGFK_gl5-7m> zAm2P9VbN`eCLy7W4a*J$1O<5X9u<+|;+Br`sA$an#>msDds5d#U;%^XngxyF24Zce z92lj1R8}AT=dD*>-?MzynKPE(&-mmyH%T4sd?=SXwPh&i1gEOU`%q8>dg$Z$4(V>D1XU zua~~@3l?wRaQD=w?^2gk^SbWro^q*Vv(;6bxUg4?BQJ-aadqKVzHuUpe?!>3LfN-E zrqY7TBlk8Yp9x*LdD(7>^N+gc@Cbf%&wsf6@6rRIV)lNTwniQ5ku|YgPxSc~#@>9_ zZ_pebdERqpbNi2`jXJe0&NfH0dD6E&wPu=nLdfn)g#JfK?x!3|kCg98?Wy*R>*{#d u(0t~k@tUNKo9fqo7r&QmthDQccx^zkwfvLoZy6XE7(8A5T-G@yGywoYpQ&j8 literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/timeline-animation-16px.png b/share/qtcreator/qmldesigner/itemLibrary/images/timeline-animation-16px.png new file mode 100644 index 0000000000000000000000000000000000000000..31b8fed6668c63248c1edb37846174ed1895d85e GIT binary patch literal 296 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4rT@h1`S>QUe*XHm?$^HA3=9kvo-U3d65+9a-eN5V z9M0KWdoI6p&wf`Q>|NM6nOC22k^rxdOmw%+#b(oFh4ts{7PCEfa=kX2|CLVhx`dhk z94=gL(AdM^$J%c8BenIM_a=SLqnl0ts`}sl+4nYnxvrX?^m6X@uz0KQ&R<{c`CnLc iztv4F(qq~qwvNXfL2QfvykKBpVDNPHb6Mw<&;$VJje)iR literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/timer-16px.png b/share/qtcreator/qmldesigner/itemLibrary/images/timer-16px.png new file mode 100644 index 0000000000000000000000000000000000000000..c675d5a70721541633f825d38b0c44dbbfc19d59 GIT binary patch literal 339 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4i*LmhONKMUokK+N_o0ChG+z*Uf8egS}1Y+ z<9XiH9n7aS*(9I1%d#)s5qV?Dm5DxP-9OtV%?axAWfSqttNUVRSB8D@w2DNQ(f^l0jwIt~YQ={GJ4cgou=IUJ13ef}~Z*V!o3 z5XF_UWBscA8!MHC=UE;Xj(?Dzd`309 uVouN_8E-EJ&qee2*dz}vG(6K&$KaR!(LQ=k61_n=8KbLh*2~7a$?~Gpn literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/timer-24px.png b/share/qtcreator/qmldesigner/itemLibrary/images/timer-24px.png new file mode 100644 index 0000000000000000000000000000000000000000..bd9419aaa0c481f9ed10931643f5684bec463f06 GIT binary patch literal 712 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|4mJh`h6m-gKNuL8`aNA7Lp(aSUiDrP8Y*-A z;A?L=wGVsw-DG}C-hkc}7_yvCQ7lkd+xZ%uG#j9n&-dp7$nAm?XJ|jRv zbZRPdbCcV*MIA;Hmgp!?io2D!;o8>X?|0Mh%Y11+99g|~Z{7W`|JU!W%f4X3E5XBd zYvN4tL~Xxy``1F zVYO1ybD#I-zQ)E1#>Y2uX%`qC+bF;LqnVLXon4kgT3;fw+<}@u4gIhGckXbwIlcKt zkFH?h3f>u}Nmq+MYRMg4zJrZhIY~3Guj8o&Z}NcOTVOyW5`R>5u zh|b#un(z8Ljz@9tV)cKdoLXP^{`7-7ox@T#;fc4kK2-EwsxQ z7As|?Z<_Pp`7UO7p4k3rufD{>;yXNZ%r_bsv;VlRm35qHD zPrhcz-M)Lz3-dEC8P}XW!>fGE?9u0keafm`9m~wb6aD!Y@b6<^e!a|?HQ=#!OyBp9 z-u(gLGRMwY{daKrbC>l--)hnIm9I1y^&bhWJ1#wWZtj80ryQ#U_%p9=R{y5O{zY%j zf*ahs_P*d;bF}n_%gL-o?th|VR;T>c+va}6%dPsN+R@kXhkkY)yqo;%SpD)FUs(JO zzTTRD_Jj9j2EoFBkMs3Ec0HOTDcD^grg!9~#o}p6n~Pfib)*VztdCg#{?N>J_bEla znqhwuMXfl!Mb=9_V7((JXwRGd=eqXim^)ohBYKiQith{f_WWnZk2TrvL=OBH{m-Cv X;kNHBohUU11_lOCS3j3^P6{Qv)d#V8mw zA+XwD#wG>^hSriGzhDLiMkZz!RyIy9ZhiqFQE>@LDQP)*1w|!g6;*W&9X)*mLnBi& zb4x30XID3O4=>-4h{&jz*tn#WwDgR;{KBHripqwjj?SLG36rMJm^pjif`yBgu3Ecp z!=`Q9ckJDF;NYRdM~n;L@5qQwKmQU#Yf zI9};`ld%8)c77ApSfQE^7XFszpDlNm`@QS2k?Zek`mKDahPwqHI6jH#z2wln)hTpNEriuu6yVrt3X&kqBubAG>Kz4xTSZ$Z4#yuWJl zE976CVOlC}koV%qX`jEF8w~lRmzEhY+5fr3(AxONKH&&k)9(vptgRzuirjS2eyDo6(r2`Eb(w39}N9F&!3|=oWLJL!>d0yCpGj*JGx49^C@l z928i?Q>@-=CcffY!Zh=KwNTrV4aywe&IXkw@`pAAt0mu3tiKg-ldq+}prL1nt8dDf`%Nw3%%NYEE9-3a>-WZ4?dRL<78bv211INx zku$c(O9DQrCwzADd%l}}O^eFn(xcP%UCQ!UDEc5S&bW?AMOt;{cDCt!4b4jrMlV}( zOy$E8hu!sD0^A;x{N{1bwbu0HI>IndOh)5u^nr5@Ti;#_;eW#reU?vo^{dMdd%Cv7 zXn&s?pWP|rx4}QO&-h%;58d@^?#s=}Oa2~mJSOZ{%<+}U?A5OBId+u?9E>FfcH9y85}Sb4q9e0P)GAEdT%j literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/toolbar-icon.png b/share/qtcreator/qmldesigner/itemLibrary/images/toolbar-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..5cb5b2e1af1bd7122ccb300ddb41aaad305a53cd GIT binary patch literal 131 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|4mJh`h6m-gKNuJoygXeTLp(Z@6C_v{Hv}En zk#w}{|CTKcozvPbA7v6V*~i4peAr#%x2@@~ literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/toolbar-icon@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/toolbar-icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..fd9e6ceebccdd430910b93bb78c2d7a0c427fe42 GIT binary patch literal 140 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-CT0c(2CW=H5e5bZo&cW^R|W=#6)<4TCfLuw zz`$G*k&;S4c literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/toolbutton-icon.png b/share/qtcreator/qmldesigner/itemLibrary/images/toolbutton-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3298f6951907969aa9cd16392f8f6e6b7cfac28f GIT binary patch literal 141 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|7G?$p2BAoW(+ms@`~f~8t_%zeD`4Q_*|R$$ zyf-i~Fff+{`2{lw$lLC@UB$q_pylb}7$Om#oUlO4z>VpLgtbl>lt5#h+ zd$wj%)^7#|2Ii6=zhDLddD}g=s~8vTbe=n5^tVE5;CkV%nWH$Q_N;YdOu)bU|{fc^>bP0l+XkK D(*`dd literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/toolseparator-icon.png b/share/qtcreator/qmldesigner/itemLibrary/images/toolseparator-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..5e99f06f2eff39c0532c1b2075134f2232271d72 GIT binary patch literal 111 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|MrH;EhFd$=?`B|NU<>dGab;j&Sg~Tonfohr z7#J8BOM?7@862M7NMm4N5chO(4B?1QPH%r;RAXRZVDNPHb6Mw<&;$UA$s!N{ literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/tumbler-icon.png b/share/qtcreator/qmldesigner/itemLibrary/images/tumbler-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..98eb8232a268efdc2df72a346f17c181f0bb157e GIT binary patch literal 132 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|CT0c(hPjegvl$o|cmjMvTp1V`R=|KUn_xc! z0|RqOkY6x^fV}OV+f@t<3@V;3jv*Y;$q5QlfBq-`KmO02EmgAFRbb{X$>jgXjpTOA cTx4MQ!(&{Z_pVNqfq{X+)78&qol`;+0DPw=y#N3J literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/tumbler-icon16.png b/share/qtcreator/qmldesigner/itemLibrary/images/tumbler-icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..ff5f95cf327e4213256393bdb3d2afef20afb85f GIT binary patch literal 127 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRdCT0c(hNQXTpBNYzcmjMvTp1V`R=|KUn_xc! z0|RqOkY6x^fV}OV+f@t<4Dy~Xjv*Y;$q5IT{>}gYN8W3W_P_oke=Gleu2InwV_@ih XAS<=LY1Ju_!Ry;B#2M@VnYe*Zd8!4Au!37%Ui*J*#gECouXnzZJ4b&)dV~cHqs-@4GzR z($2hYy&wPZbpvm~8s>ElQ#m6(?Kot0R#o|HqvHFsA&l&vw?j6>dhKfYU%$-SHC!`$ UW9FV-1_lNOPgg&ebxsLQ0J)`A*Z=?k literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/video-24px.png b/share/qtcreator/qmldesigner/itemLibrary/images/video-24px.png new file mode 100644 index 0000000000000000000000000000000000000000..df1b84e5c99654bc885875e39a7db24db89f20b0 GIT binary patch literal 286 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|4mJh`h6m-gKNuJoo_e}ChIn+oy}HoL#ZjdF zaUTMeiw<@guf*YG-ZN3QK>DBb;=+M4q4;Wsz?Y=0jr~GKq q^^T76IjL#v+yQ6zop~up{WcE3=E#GelF{r5}E*sDSRFP literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/video-24px@2x.png b/share/qtcreator/qmldesigner/itemLibrary/images/video-24px@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..4b9f31faf390bcf63565bd4405cf088cd9c26c70 GIT binary patch literal 399 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4rT@hhJ-tuTNxM_8UuVnTp1V`{{R2KV&H&& zMK&h`1A|sckY6wZ0}BVAps1LHl(ek2jh$y|c0pn3#7UE<&D_4@>Vt=mpFDs4_TBpr zfBycP8pD5yfq`MNr;B5V$MLsQJ^7m)B%HOm@*cEVE9kAb@&7-wt%sAr#D(gfGs=HY z+LxmDc7ZyR_7-Obx7kmQ%Fo%BTqArZ!re@Jar4nrM^%z-zh?@TPYeIlA?qt+_p{Px zl4a210!9XVExijq^7sCEI!dgQe|phn#hzWA4%!h9;}1oOKCugtRxgi=<1L)7RQS^= i{C?Q))KZrU=A+`5reqtLeqdl=VDNPHb6Mw<&;$SyR=YU> literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/video-output-16px.png b/share/qtcreator/qmldesigner/itemLibrary/images/video-output-16px.png new file mode 100644 index 0000000000000000000000000000000000000000..f00afc52e97f09e6d4991e73c286c72b4d83cb95 GIT binary patch literal 289 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7UU<4VhFJ7oy=>^k;waGi zFn^k0t8J@7hr+xAK0Q97yZRFrt=!nDAhBkm`HQ#@T+7m)P;wlDn_ z-l1soNW?t0Vbe>4r#Ai0Ic1%*%`}3~vG2$dyf4lEm-$1?>)$sH=bS#!qMDrj;o{+Q tEKAnv8FVP#IVP}eL)YI&|Ld3evujyz=kgPp%fP_E;OXk;vd$@?2>=Z4d#V5c literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/video-output-24px.png b/share/qtcreator/qmldesigner/itemLibrary/images/video-output-24px.png new file mode 100644 index 0000000000000000000000000000000000000000..fd3c89c08177c783140aa57f295011e9b1be878f GIT binary patch literal 387 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|4mJh`h6m-gKNuJoy*ynULp(a)Ufq}#;waMo z@qVm0^Fb#jE+v()rq;X1+%COl)<{=SWYN)aHCW=Kwed42%jG+f9@MhfM?3-!KzHG&+Q>n{}SANNMoLo}8zg~R)hdoO_uvk~j zRr)tae~RV(gEd_@-aay(7yG1uL+0UC0h{jfh~qgkix(Vn{J_}}8K z(s!kP={GJnbs8kqCSeGZd$EWg!e^qfimBO`b2gJ9OwbG9`{E^m7f_$taI{Mnw~c-{y9 zeIy$Aj@5GXZ@l!R{Qv)d`HB_8 z5?SYn&Sqd>@GJ@P3ua(oWMbjq&svlDTeg)SH<)lp`iDHj z1HC%!N1b(EayD8v!d0`}LteXoy3F@J=0*0y-M7xZTq%${rPXI^x4oT(ID5~M13z;3 zOV)|D#E4mFRWD@BiMrKbygQ^jI-&E11NV;CzvLP$O{aeO&cLwkP#-fx{bZJgjQK1H zUVau2Ow8h=`52631l|Pc?tH~n%;ueU^v)zh!zK3=uPk;ud^=!9lqjrBuiW)N`mv#O3D+9QW+dm z@{>{(JaZG%Q-e|yQz{EjrrIztFdg%BaSW-rwPdQNf0m;_Yihv(0g;J;LLy9^oQbYI z57j>u-`vqC$}ObOBHL)-GQrW6sd0hPzVo+l#ISv?soc8b&h6OJS65cPa(n#!x2e?G zgZ8(QHqOxA7Nd7``MmiVCcjQs9J=!HQqsm7;sL9#-s-)mz3gfhYrK-Z=xo2`)x|H$ zrRt*;D7wpAUFeY=7bPYweRC zPdxwpwd!ut#(>9vZs+CYwPY<{zWn<2>-zfoEji4Mx)qZqs9!nu*l^+rMahFAv%kY`og`srmCazYrg_a(dJJ@iu8J0q$v?uE&c zU3c}48rNmnCtlGDo1wO2HUG*uIi(aS9=0B#dmVRQmTglX-JVEdEaODE-QJN~$EXH1jM z_SjS(*T3LFfrK1T`yB=znTD5##heZ8Zq>E^PcDtC8*CSm&G-M>QiXvsf42V>Okj8%5x+%FXongD P0|SGntDnm{r-UW|19fr< literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/itemLibrary/images/webview-icon16.png b/share/qtcreator/qmldesigner/itemLibrary/images/webview-icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..ac7be01bb71ab272906754f6fe38ee15548e5d2e GIT binary patch literal 519 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4kiW$h6xih%orFLBuiW)N`mv#O3D+9QW+dm z@{>{(JaZG%Q-e|yQz{EjrrIztFs}4;aSW-r)icR^{lNf{*8J7?T9j1sJR}5+m0rq! z^#99YXyoXmWcZQG=}7d&-&=QYU-oT*ZTkGJ*Y9P|UHs&8<^1#CKfCU^pMQFjNoZ*F z+G*3Tnpit?H0`+me*N{+MT_p{Wy|P@aqs)W$=1C5a^~inbIzytIWL^B?)AltkgOQD zpN-#s|NZ=PPQKafwO6xDcE+ur>zBU&zOk9a%8k+5T}OylabnFPA6aWfS{Ej z23(iz^bU%5AKlQgaz>(s3?Cb_RM_fJt`_b}`RNgkciw+r*s}6wj#Hz{XZsg3Pd~L| zUGnJTkGEygy>4qVCwZtia8%jI6)PHSDsw;NsIhbX&78I)K`W2g$Sh8=n(K5#aJsIM ziDc)23rQPy+::max(); +// QTC_TEMP -ComponentVersion::ComponentVersion() - : _major(NoVersion), _minor(NoVersion) +ComponentVersion::ComponentVersion(QStringView versionString) + : _major(NoVersion) + , _minor(NoVersion) { -} - -ComponentVersion::ComponentVersion(int major, int minor) - : _major(major), _minor(minor) -{ -} - -ComponentVersion::ComponentVersion(const QString &versionString) - : _major(NoVersion), _minor(NoVersion) -{ - int dotIdx = versionString.indexOf(QLatin1Char('.')); + auto dotIdx = versionString.indexOf(QLatin1Char('.')); if (dotIdx == -1) return; bool ok = false; - int maybeMajor = versionString.left(dotIdx).toInt(&ok); + auto maybeMajor = versionString.left(dotIdx).toInt(&ok); if (!ok) return; int maybeMinor = versionString.mid(dotIdx + 1).toInt(&ok); @@ -40,15 +30,6 @@ ComponentVersion::ComponentVersion(const QString &versionString) _minor = maybeMinor; } -ComponentVersion::~ComponentVersion() -{ -} - -bool ComponentVersion::isValid() const -{ - return _major >= 0 && _minor >= 0; -} - QString ComponentVersion::toString() const { QByteArray temp; @@ -65,36 +46,4 @@ void ComponentVersion::addToHash(QCryptographicHash &hash) const hash.addData(reinterpret_cast(&_minor), sizeof(_minor)); } -bool operator<(const ComponentVersion &lhs, const ComponentVersion &rhs) -{ - return lhs.majorVersion() < rhs.majorVersion() - || (lhs.majorVersion() == rhs.majorVersion() && lhs.minorVersion() < rhs.minorVersion()); -} - -bool operator<=(const ComponentVersion &lhs, const ComponentVersion &rhs) -{ - return lhs.majorVersion() < rhs.majorVersion() - || (lhs.majorVersion() == rhs.majorVersion() && lhs.minorVersion() <= rhs.minorVersion()); -} - -bool operator>(const ComponentVersion &lhs, const ComponentVersion &rhs) -{ - return rhs < lhs; -} - -bool operator>=(const ComponentVersion &lhs, const ComponentVersion &rhs) -{ - return rhs <= lhs; -} - -bool operator==(const ComponentVersion &lhs, const ComponentVersion &rhs) -{ - return lhs.majorVersion() == rhs.majorVersion() && lhs.minorVersion() == rhs.minorVersion(); -} - -bool operator!=(const ComponentVersion &lhs, const ComponentVersion &rhs) -{ - return !(lhs == rhs); -} - } // namespace LanguageUtils diff --git a/src/libs/languageutils/componentversion.h b/src/libs/languageutils/componentversion.h index 4b39e8402a1..0ba332103ac 100644 --- a/src/libs/languageutils/componentversion.h +++ b/src/libs/languageutils/componentversion.h @@ -9,6 +9,8 @@ QT_BEGIN_NAMESPACE class QCryptographicHash; QT_END_NAMESPACE +#include + namespace LanguageUtils { class LANGUAGEUTILS_EXPORT ComponentVersion @@ -17,25 +19,56 @@ class LANGUAGEUTILS_EXPORT ComponentVersion int _minor; public: - static const int NoVersion; - static const int MaxVersion; + static const int NoVersion = -1; + static const int MaxVersion = -1; - ComponentVersion(); - ComponentVersion(int major, int minor); - explicit ComponentVersion(const QString &versionString); - ~ComponentVersion(); + ComponentVersion() + : _major(NoVersion) + , _minor(NoVersion) + {} + + ComponentVersion(int major, int minor) + : _major(major) + , _minor(minor) + {} + + explicit ComponentVersion(QStringView versionString); + ~ComponentVersion() = default; int majorVersion() const { return _major; } int minorVersion() const { return _minor; } - friend bool LANGUAGEUTILS_EXPORT operator<(const ComponentVersion &lhs, const ComponentVersion &rhs); - friend bool LANGUAGEUTILS_EXPORT operator<=(const ComponentVersion &lhs, const ComponentVersion &rhs); - friend bool LANGUAGEUTILS_EXPORT operator>(const ComponentVersion &lhs, const ComponentVersion &rhs); - friend bool LANGUAGEUTILS_EXPORT operator>=(const ComponentVersion &lhs, const ComponentVersion &rhs); - friend bool LANGUAGEUTILS_EXPORT operator==(const ComponentVersion &lhs, const ComponentVersion &rhs); - friend bool LANGUAGEUTILS_EXPORT operator!=(const ComponentVersion &lhs, const ComponentVersion &rhs); + friend bool operator<(const ComponentVersion &lhs, const ComponentVersion &rhs) + { + return std::tie(lhs._major, lhs._minor) < std::tie(rhs._major, rhs._minor); + } - bool isValid() const; + friend bool operator<=(const ComponentVersion &lhs, const ComponentVersion &rhs) + { + return std::tie(lhs._major, lhs._minor) <= std::tie(rhs._major, rhs._minor); + } + + friend bool operator>(const ComponentVersion &lhs, const ComponentVersion &rhs) + { + return rhs < lhs; + } + + friend bool operator>=(const ComponentVersion &lhs, const ComponentVersion &rhs) + { + return rhs <= lhs; + } + + friend bool operator==(const ComponentVersion &lhs, const ComponentVersion &rhs) + { + return lhs.majorVersion() == rhs.majorVersion() && lhs.minorVersion() == rhs.minorVersion(); + } + + friend bool operator!=(const ComponentVersion &lhs, const ComponentVersion &rhs) + { + return !(lhs == rhs); + } + + bool isValid() const { return _major >= 0 && _minor >= 0; } QString toString() const; void addToHash(QCryptographicHash &hash) const; }; diff --git a/src/libs/sqlite/sqlitevalue.h b/src/libs/sqlite/sqlitevalue.h index ec4120139ab..725682494bd 100644 --- a/src/libs/sqlite/sqlitevalue.h +++ b/src/libs/sqlite/sqlitevalue.h @@ -34,7 +34,7 @@ public: explicit ValueBase(NullValue) {} explicit ValueBase(VariantType &&value) - : value(value) + : value(std::move(value)) {} explicit ValueBase(const char *value) @@ -44,6 +44,7 @@ public: explicit ValueBase(long long value) : value(value) {} + explicit ValueBase(int value) : value(static_cast(value)) {} @@ -61,11 +62,6 @@ public: {} - explicit ValueBase(StringType &&value) - : value(std::move(value)) - - {} - explicit ValueBase(BlobView value) : value(value) @@ -230,14 +226,42 @@ public: class ValueView : public ValueBase { public: + ValueView() = default; + + explicit ValueView(NullValue) {} + explicit ValueView(ValueBase &&base) : ValueBase(std::move(base)) {} + explicit ValueView(Utils::SmallStringView value) + : ValueBase(value) + {} + + explicit ValueView(BlobView value) + : ValueBase(value) + {} + + explicit ValueView(long long value) + : ValueBase(value) + {} + + explicit ValueView(int value) + : ValueBase(static_cast(value)) + {} + + explicit ValueView(uint value) + : ValueBase(static_cast(value)) + {} + + explicit ValueView(double value) + : ValueBase(value) + {} + template static ValueView create(Type &&value_) { - return ValueView{ValueBase{value_}}; + return ValueView(std::forward(value_)); } }; diff --git a/src/libs/utils/smallstring.h b/src/libs/utils/smallstring.h index a8869b4ccf8..52dd10602ce 100644 --- a/src/libs/utils/smallstring.h +++ b/src/libs/utils/smallstring.h @@ -356,6 +356,14 @@ public: return false; } + bool startsWith(QStringView subStringToSearch) const noexcept + { + if (size() >= Utils::usize(subStringToSearch)) + return subStringToSearch == QLatin1StringView{data(), subStringToSearch.size()}; + + return false; + } + bool startsWith(char characterToSearch) const noexcept { return data()[0] == characterToSearch; diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp index 50bb01a5bbb..3bff2105209 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp @@ -296,15 +296,16 @@ void ItemLibraryModel::setSearchText(const QString &searchText) Import ItemLibraryModel::entryToImport(const ItemLibraryEntry &entry) { +#ifndef QDS_USE_PROJECTSTORAGE if (entry.majorVersion() == -1 && entry.minorVersion() == -1) return Import::createFileImport(entry.requiredImport()); - +#endif return Import::createLibraryImport(entry.requiredImport(), QString::number(entry.majorVersion()) + QLatin1Char('.') + QString::number(entry.minorVersion())); } -void ItemLibraryModel::update([[maybe_unused]] ItemLibraryInfo *itemLibraryInfo, Model *model) +void ItemLibraryModel::update(Model *model) { if (!model) return; diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h index 212ddf8e040..b04ea492d2a 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h @@ -37,7 +37,7 @@ public: QString searchText() const; ItemLibraryImport *importByUrl(const QString &importName) const; - void update(ItemLibraryInfo *itemLibraryInfo, Model *model); + void update(Model *model); void updateUsedImports(const Imports &usedImports); QMimeData *getMimeData(const ItemLibraryEntry &itemLibraryEntry); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp index 587489bc8ca..ffefc431782 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp @@ -333,9 +333,7 @@ void ItemLibraryWidget::updateModel() m_compressionTimer.stop(); } -#ifndef QDS_USE_PROJECTSTORAGE - m_itemLibraryModel->update(m_itemLibraryInfo.data(), m_model.data()); -#endif + m_itemLibraryModel->update(m_model.data()); if (m_itemLibraryModel->rowCount() == 0 && !m_updateRetry) { m_updateRetry = true; // Only retry once to avoid endless loops diff --git a/src/plugins/qmldesigner/designercore/include/projectstorageids.h b/src/plugins/qmldesigner/designercore/include/projectstorageids.h index 90c174e04e0..0b157e55e77 100644 --- a/src/plugins/qmldesigner/designercore/include/projectstorageids.h +++ b/src/plugins/qmldesigner/designercore/include/projectstorageids.h @@ -51,6 +51,8 @@ using SourceContextIds = std::vector; using SourceId = Sqlite::BasicId; using SourceIds = std::vector; +template +using SmallSourceIds = QVarLengthArray; using ModuleId = Sqlite::BasicId; using ModuleIds = std::vector; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index ad606bcb90c..70d5e92b256 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -484,6 +484,34 @@ public: return typeHints; } + SmallSourceIds<4> typeAnnotationSourceIds(SourceId directoryId) const override + { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get type annotaion source ids"_t, + projectStorageCategory(), + keyValue("source id", directoryId)}; + + auto sourceIds = selectTypeAnnotationSourceIdsStatement + .template valuesWithTransaction>(directoryId); + + tracer.end(keyValue("source ids", sourceIds)); + + return sourceIds; + } + + SmallSourceIds<64> typeAnnotationDirectorySourceIds() const override + { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get type annotaion source ids"_t, projectStorageCategory()}; + + auto sourceIds = selectTypeAnnotationDirectorySourceIdsStatement + .template valuesWithTransaction>(); + + tracer.end(keyValue("source ids", sourceIds)); + + return sourceIds; + } + Storage::Info::ItemLibraryEntries itemLibraryEntries(TypeId typeId) const override { using NanotraceHR::keyValue; @@ -518,6 +546,40 @@ public: return entries; } + Storage::Info::ItemLibraryEntries itemLibraryEntries(ImportId importId) const + { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get item library entries by import id"_t, + projectStorageCategory(), + keyValue("import id", importId)}; + + using Storage::Info::ItemLibraryProperties; + Storage::Info::ItemLibraryEntries entries; + + auto callback = [&](TypeId typeId_, + Utils::SmallStringView name, + Utils::SmallStringView iconPath, + Utils::SmallStringView category, + Utils::SmallStringView import, + Utils::SmallStringView toolTip, + Utils::SmallStringView properties, + Utils::SmallStringView extraFilePaths, + Utils::SmallStringView templatePath) { + auto &last = entries.emplace_back( + typeId_, name, iconPath, category, import, toolTip, templatePath); + if (properties.size()) + selectItemLibraryPropertiesStatement.readTo(last.properties, properties); + if (extraFilePaths.size()) + selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths); + }; + + selectItemLibraryEntriesByTypeIdStatement.readCallbackWithTransaction(callback, importId); + + tracer.end(keyValue("item library entries", entries)); + + return entries; + } + Storage::Info::ItemLibraryEntries itemLibraryEntries(SourceId sourceId) const override { using NanotraceHR::keyValue; @@ -1479,6 +1541,28 @@ private: annotation.typeId = fetchTypeIdByModuleIdAndExportedName(annotation.moduleId, annotation.typeName); } + + for (auto &annotation : typeAnnotations) { + if (!annotation.typeId) + qWarning() << moduleName(annotation.moduleId).toQString() + << annotation.typeName.toQString(); + } + + typeAnnotations.erase(std::remove_if(typeAnnotations.begin(), + typeAnnotations.end(), + [](const auto &annotation) { + return !annotation.typeId; + }), + typeAnnotations.end()); + } + + template + static Sqlite::ValueView createEmptyAsNull(const Value &value) + { + if (value.size()) + return Sqlite::ValueView::create(value); + + return Sqlite::ValueView{}; } void synchronizeTypeAnnotations(Storage::Synchronization::TypeAnnotations &typeAnnotations, @@ -1512,9 +1596,10 @@ private: insertTypeAnnotationStatement.write(annotation.typeId, annotation.sourceId, + annotation.directorySourceId, annotation.iconPath, - annotation.itemLibraryJson, - annotation.hintsJson); + createEmptyAsNull(annotation.itemLibraryJson), + createEmptyAsNull(annotation.hintsJson)); }; auto update = [&](const TypeAnnotationView &annotationFromDatabase, @@ -1533,8 +1618,8 @@ private: updateTypeAnnotationStatement.write(annotation.typeId, annotation.iconPath, - annotation.itemLibraryJson, - annotation.hintsJson); + createEmptyAsNull(annotation.itemLibraryJson), + createEmptyAsNull(annotation.hintsJson)); return Sqlite::UpdateChange::Update; } @@ -4285,11 +4370,15 @@ private: Sqlite::StrictColumnType::Integer, {Sqlite::PrimaryKey{}}); auto &sourceIdColumn = table.addColumn("sourceId", Sqlite::StrictColumnType::Integer); + auto &directorySourceIdColumn = table.addColumn("directorySourceId", + Sqlite::StrictColumnType::Integer); + table.addColumn("iconPath", Sqlite::StrictColumnType::Text); table.addColumn("itemLibrary", Sqlite::StrictColumnType::Text); table.addColumn("hints", Sqlite::StrictColumnType::Text); table.addUniqueIndex({sourceIdColumn, typeIdColumn}); + table.addIndex({directorySourceIdColumn}); table.initialize(database); } @@ -4926,9 +5015,10 @@ public: "SELECT typeId, iconPath, itemLibrary, hints FROM typeAnnotations WHERE " "sourceId IN carray(?1) ORDER BY typeId", database}; - WriteStatement<5> insertTypeAnnotationStatement{ - "INSERT INTO typeAnnotations(typeId, sourceId, iconPath, itemLibrary, hints) VALUES(?1, " - "?2, ?3, ?4, ?5)", + WriteStatement<6> insertTypeAnnotationStatement{ + "INSERT INTO " + " typeAnnotations(typeId, sourceId, directorySourceId, iconPath, itemLibrary, hints) " + "VALUES(?1, ?2, ?3, ?4, ?5, ?6)", database}; WriteStatement<4> updateTypeAnnotationStatement{ "UPDATE typeAnnotations SET iconPath=?2, itemLibrary=?3, hints=?4 WHERE typeId=?1", database}; @@ -4939,28 +5029,35 @@ public: mutable ReadStatement<2, 1> selectTypeHintsStatement{ "SELECT hints.key, hints.value " "FROM typeAnnotations, json_each(typeAnnotations.hints) AS hints " - "WHERE typeId=?1", + "WHERE typeId=?1 AND hints IS NOT NULL", database}; + mutable ReadStatement<1, 1> selectTypeAnnotationSourceIdsStatement{ + "SELECT sourceId FROM typeAnnotations WHERE directorySourceId=?1 ORDER BY sourceId", database}; + mutable ReadStatement<1, 0> selectTypeAnnotationDirectorySourceIdsStatement{ + "SELECT DISTINCT directorySourceId FROM typeAnnotations ORDER BY directorySourceId", database}; mutable ReadStatement<9> selectItemLibraryEntriesStatement{ "SELECT typeId, i.value->>'$.name', i.value->>'$.iconPath', i.value->>'$.category', " " i.value->>'$.import', i.value->>'$.toolTip', i.value->>'$.properties', " " i.value->>'$.extraFilePaths', i.value->>'$.templatePath' " - "FROM typeAnnotations, json_each(typeAnnotations.itemLibrary) AS i", + "FROM typeAnnotations AS ta , json_each(ta.itemLibrary) AS i " + "WHERE ta.itemLibrary IS NOT NULL", database}; mutable ReadStatement<9, 1> selectItemLibraryEntriesByTypeIdStatement{ "SELECT typeId, i.value->>'$.name', i.value->>'$.iconPath', i.value->>'$.category', " " i.value->>'$.import', i.value->>'$.toolTip', i.value->>'$.properties', " " i.value->>'$.extraFilePaths', i.value->>'$.templatePath' " - "FROM typeAnnotations, json_each(typeAnnotations.itemLibrary) AS i " - "WHERE typeId=?1", + "FROM typeAnnotations AS ta, json_each(ta.itemLibrary) AS i " + "WHERE typeId=?1 AND ta.itemLibrary IS NOT NULL", database}; mutable ReadStatement<9, 1> selectItemLibraryEntriesBySourceIdStatement{ - "SELECT typeId, i.value->>'$.name', i.value->>'$.iconPath', i.value->>'$.category', " + "SELECT typeId, i.value->>'$.name', i.value->>'$.iconPath', " + "i.value->>'$.category', " " i.value->>'$.import', i.value->>'$.toolTip', i.value->>'$.properties', " " i.value->>'$.extraFilePaths', i.value->>'$.templatePath' " "FROM typeAnnotations, json_each(typeAnnotations.itemLibrary) AS i " "WHERE typeId IN (SELECT DISTINCT typeId " - " FROM documentImports AS di JOIN exportedTypeNames USING(moduleId) " + " FROM documentImports AS di JOIN exportedTypeNames " + " USING(moduleId) " " WHERE di.sourceId=?)", database}; mutable ReadStatement<3, 1> selectItemLibraryPropertiesStatement{ diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h index a67b6296083..20d988f7aab 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h @@ -57,6 +57,8 @@ public: = 0; virtual PropertyDeclarationId defaultPropertyDeclarationId(TypeId typeId) const = 0; virtual std::optional type(TypeId typeId) const = 0; + virtual SmallSourceIds<4> typeAnnotationSourceIds(SourceId directoryId) const = 0; + virtual SmallSourceIds<64> typeAnnotationDirectorySourceIds() const = 0; virtual Utils::PathString typeIconPath(TypeId typeId) const = 0; virtual Storage::Info::TypeHints typeHints(TypeId typeId) const = 0; virtual Storage::Info::ItemLibraryEntries itemLibraryEntries(TypeId typeId) const = 0; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h index 5f80505da3d..8d810d94bd2 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h @@ -1202,10 +1202,13 @@ using ProjectDatas = std::vector; class TypeAnnotation { public: - TypeAnnotation(SourceId sourceId) + TypeAnnotation(SourceId sourceId, SourceId directorySourceId) : sourceId{sourceId} + , directorySourceId{directorySourceId} {} + TypeAnnotation(SourceId sourceId, + SourceId directorySourceId, Utils::SmallStringView typeName, ModuleId moduleId, Utils::SmallStringView iconPath, @@ -1219,6 +1222,7 @@ public: , sourceId{sourceId} , moduleId{moduleId} , traits{traits} + , directorySourceId{directorySourceId} {} template @@ -1247,6 +1251,7 @@ public: SourceId sourceId; ModuleId moduleId; TypeTraits traits; + SourceId directorySourceId; }; using TypeAnnotations = std::vector; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp index 6948117db69..761d6371efe 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp @@ -11,6 +11,7 @@ #include "qmltypesparserinterface.h" #include "sourcepath.h" #include "sourcepathcache.h" +#include "typeannotationreader.h" #include @@ -246,7 +247,8 @@ std::vector createIdPaths(ProjectStorageUpdater::WatchedSourceIdsIds wa void ProjectStorageUpdater::update(QStringList directories, QStringList qmlTypesPaths, - const QString &propertyEditorResourcesPath) + const QString &propertyEditorResourcesPath, + const QStringList &typeAnnotationPaths) { NanotraceHR::Tracer tracer{"update"_t, category(), @@ -260,6 +262,7 @@ void ProjectStorageUpdater::update(QStringList directories, updateDirectories(directories, package, notUpdatedSourceIds, watchedSourceIds); updateQmlTypes(qmlTypesPaths, package, notUpdatedSourceIds, watchedSourceIds); updatePropertyEditorPaths(propertyEditorResourcesPath, package, notUpdatedSourceIds); + updateTypeAnnotations(typeAnnotationPaths, package, notUpdatedSourceIds); package.updatedSourceIds = filterNotUpdatedSourceIds(std::move(package.updatedSourceIds), std::move(notUpdatedSourceIds.sourceIds)); @@ -511,12 +514,134 @@ void ProjectStorageUpdater::updatePropertyEditorPaths( } } -void ProjectStorageUpdater::updateTypeAnnotations( - const QString & /*propertyEditorResourcesPath*/, - Storage::Synchronization::SynchronizationPackage & /*package*/, - NotUpdatedSourceIds & /*notUpdatedSourceIds*/) +namespace { + +template +SmallSourceIds<16> mergedSourceIds(const SourceIds1 &sourceIds1, const SourceIds2 &sourceIds2) { - // const auto typeAnnotations = dir.entryInfoList({"*.metainfo"}, QDir::Files); + SmallSourceIds<16> mergedSourceIds; + + std::set_union(sourceIds1.begin(), + sourceIds1.end(), + sourceIds2.begin(), + sourceIds2.end(), + std::back_inserter(mergedSourceIds)); + + return mergedSourceIds; +} +} // namespace + +void ProjectStorageUpdater::updateTypeAnnotations(const QStringList &directoryPaths, + Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds) +{ + NanotraceHR::Tracer tracer("update type annotations"_t, category()); + + std::map> updatedSourceIdsDictonary; + + for (SourceId directoryId : m_projectStorage.typeAnnotationDirectorySourceIds()) + updatedSourceIdsDictonary[directoryId] = {}; + + for (const auto &directoryPath : directoryPaths) + updateTypeAnnotations(directoryPath, package, notUpdatedSourceIds, updatedSourceIdsDictonary); + + updateTypeAnnotationDirectories(package, notUpdatedSourceIds, updatedSourceIdsDictonary); +} + +void ProjectStorageUpdater::updateTypeAnnotations( + const QString &rootDirectoryPath, + Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds, + std::map> &updatedSourceIdsDictonary) +{ + NanotraceHR::Tracer tracer("update type annotation directory"_t, + category(), + keyValue("path", rootDirectoryPath)); + + if (rootDirectoryPath.isEmpty()) + return; + + QDirIterator directoryIterator{rootDirectoryPath, + {"*.metainfo"}, + QDir::NoDotAndDotDot | QDir::Files, + QDirIterator::Subdirectories}; + + while (directoryIterator.hasNext()) { + auto fileInfo = directoryIterator.nextFileInfo(); + auto filePath = fileInfo.filePath(); + SourceId sourceId = m_pathCache.sourceId(SourcePath{filePath}); + + auto directoryPath = fileInfo.canonicalPath(); + + SourceId directorySourceId = m_pathCache.sourceId(SourcePath{directoryPath + "/."}); + + auto state = fileState(sourceId, package, notUpdatedSourceIds); + if (state == FileState::Changed) + updateTypeAnnotation(directoryPath, fileInfo.filePath(), sourceId, directorySourceId, package); + + if (state != FileState::NotChanged) + updatedSourceIdsDictonary[directorySourceId].push_back(sourceId); + } +} + +void ProjectStorageUpdater::updateTypeAnnotationDirectories( + Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds, + std::map> &updatedSourceIdsDictonary) +{ + for (auto &[directorySourceId, updatedSourceIds] : updatedSourceIdsDictonary) { + auto directoryState = fileState(directorySourceId, package, notUpdatedSourceIds); + + if (directoryState != FileState::NotChanged) { + auto existingTypeAnnotationSourceIds = m_projectStorage.typeAnnotationSourceIds( + directorySourceId); + + std::sort(updatedSourceIds.begin(), updatedSourceIds.end()); + + auto changedSourceIds = mergedSourceIds(existingTypeAnnotationSourceIds, updatedSourceIds); + package.updatedTypeAnnotationSourceIds.insert(package.updatedTypeAnnotationSourceIds.end(), + changedSourceIds.begin(), + changedSourceIds.end()); + } else { + package.updatedTypeAnnotationSourceIds.insert(package.updatedTypeAnnotationSourceIds.end(), + updatedSourceIds.begin(), + updatedSourceIds.end()); + } + } +} + +namespace { +QString contentFromFile(const QString &path) +{ + QFile file{path}; + if (file.open(QIODevice::ReadOnly)) + return QString::fromUtf8(file.readAll()); + + return {}; +} +} // namespace + +void ProjectStorageUpdater::updateTypeAnnotation(const QString &directoryPath, + const QString &filePath, + SourceId sourceId, + SourceId directorySourceId, + Storage::Synchronization::SynchronizationPackage &package) +{ + NanotraceHR::Tracer tracer{"update type annotation path"_t, + category(), + keyValue("path", filePath), + keyValue("directory path", directoryPath)}; + + Storage::TypeAnnotationReader reader{m_projectStorage}; + + auto annotations = reader.parseTypeAnnotation(contentFromFile(filePath), + directoryPath, + sourceId, + directorySourceId); + auto &typeAnnotations = package.typeAnnotations; + package.typeAnnotations.insert(typeAnnotations.end(), + std::make_move_iterator(annotations.begin()), + std::make_move_iterator(annotations.end())); } void ProjectStorageUpdater::updatePropertyEditorPath( @@ -752,6 +877,7 @@ void ProjectStorageUpdater::parseProjectDatas(const Storage::Synchronization::Pr watchedSourceIds.qmlSourceIds.push_back(projectData.sourceId); parseQmlComponent(projectData.sourceId, package, notUpdatedSourceIds); + break; } } } diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h index c7794035659..e21531deea0 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h @@ -11,12 +11,15 @@ #include "projectstoragetypes.h" #include "sourcepath.h" +#include + #include #include #include +#include #include namespace Sqlite { @@ -40,7 +43,7 @@ public: using PathCache = SourcePathCache, NonLockingMutex>; ProjectStorageUpdater(FileSystemInterface &fileSystem, - ProjectStorageInterface &projectStorage, + ProjectStorageType &projectStorage, FileStatusCache &fileStatusCache, PathCache &pathCache, QmlDocumentParserInterface &qmlDocumentParser, @@ -59,7 +62,8 @@ public: void update(QStringList directories, QStringList qmlTypesPaths, - const QString &propertyEditorResourcesPath); + const QString &propertyEditorResourcesPath, + const QStringList &typeAnnotationPaths); void pathsWithIdsChanged(const std::vector &idPaths) override; void pathsChanged(const SourceIds &filePathIds) override; @@ -157,9 +161,21 @@ private: void updatePropertyEditorPaths(const QString &propertyEditorResourcesPath, Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds); - void updateTypeAnnotations(const QString &propertyEditorResourcesPath, + void updateTypeAnnotations(const QString &directoryPath, + Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds, + std::map> &updatedSourceIdsDictonary); + void updateTypeAnnotationDirectories(Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds, + std::map> &updatedSourceIdsDictonary); + void updateTypeAnnotations(const QStringList &directoryPath, Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds); + void updateTypeAnnotation(const QString &directoryPath, + const QString &filePath, + SourceId sourceId, + SourceId directorySourceId, + Storage::Synchronization::SynchronizationPackage &package); void updatePropertyEditorPath(const QString &path, Storage::Synchronization::SynchronizationPackage &package, SourceId directorySourceId); @@ -209,7 +225,7 @@ private: private: std::vector m_changedIdPaths; FileSystemInterface &m_fileSystem; - ProjectStorageInterface &m_projectStorage; + ProjectStorageType &m_projectStorage; FileStatusCache &m_fileStatusCache; PathCache &m_pathCache; QmlDocumentParserInterface &m_qmlDocumentParser; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp b/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp index b829e9db36d..67a63542bcb 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp @@ -27,11 +27,11 @@ constexpr auto propertyElementName = "Property"_L1; constexpr auto extraFileElementName = "ExtraFile"_L1; } // namespace -Synchronization::TypeAnnotations TypeAnnotationReader::parseTypeAnnotation(const QString &content, - const QString &directoryPath, - SourceId sourceId) +Synchronization::TypeAnnotations TypeAnnotationReader::parseTypeAnnotation( + const QString &content, const QString &directoryPath, SourceId sourceId, SourceId directorySourceId) { m_sourceId = sourceId; + m_directorySourceId = directorySourceId; m_directoryPath = directoryPath; m_parserState = ParsingDocument; if (!SimpleAbstractStreamReader::readFromSource(content)) { @@ -178,7 +178,7 @@ TypeAnnotationReader::ParserSate TypeAnnotationReader::readDocument(const QStrin TypeAnnotationReader::ParserSate TypeAnnotationReader::readMetaInfoRootElement(const QString &name) { if (name == typeElementName) { - m_typeAnnotations.emplace_back(m_sourceId); + m_typeAnnotations.emplace_back(m_sourceId, m_directorySourceId); m_itemLibraryEntries = json::array(); return ParsingType; } else { @@ -277,7 +277,7 @@ void TypeAnnotationReader::readItemLibraryEntryProperty(QStringView name, const } else if (name == "category"_L1) { m_itemLibraryEntries.back()["category"] = value; } else if (name == "libraryIcon"_L1) { - m_itemLibraryEntries.back()["iconPath"] = value; + m_itemLibraryEntries.back()["iconPath"] = absoluteFilePathForDocument(variant.toString()); } else if (name == "version"_L1) { // setVersion(value.toString()); } else if (name == "requiredImport"_L1) { @@ -427,8 +427,8 @@ void TypeAnnotationReader::setVersion(const QString &versionNumber) int minor = 0; if (!versionNumber.isEmpty()) { - int val; - bool ok; + int val = -1; + bool ok = false; if (versionNumber.contains('.'_L1)) { val = versionNumber.split('.'_L1).constFirst().toInt(&ok); major = ok ? val : major; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.h b/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.h index 9332d5bed94..a320493ee27 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.h @@ -49,7 +49,8 @@ public: Synchronization::TypeAnnotations parseTypeAnnotation(const QString &content, const QString &directoryPath, - SourceId sourceId); + SourceId sourceId, + SourceId directorySourceId); QStringList errors(); @@ -124,6 +125,7 @@ private: json m_itemLibraryEntries; Property m_currentProperty; SourceId m_sourceId; + SourceId m_directorySourceId; }; } // namespace QmlDesigner::Storage diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp index c415863e8a5..2aadc45f24a 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp @@ -490,6 +490,11 @@ QString propertyEditorResourcesPath() return Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources").toString(); } +QString qtCreatorItemLibraryPath() +{ + return Core::ICore::resourcePath("qmldesigner/itemLibrary").toString(); +} + } // namespace void QmlDesignerProjectManager::projectAdded(::ProjectExplorer::Project *project) @@ -626,11 +631,13 @@ void QmlDesignerProjectManager::update() if constexpr (isUsingQmlDesignerLite()) { m_projectData->projectStorageData->updater.update(directoriesForLiteDesigner(), qmlTypesForLiteDesigner(), - propertyEditorResourcesPath()); + propertyEditorResourcesPath(), + {qtCreatorItemLibraryPath()}); } else { m_projectData->projectStorageData->updater.update(directories(m_projectData->activeTarget), qmlTypes(m_projectData->activeTarget), - propertyEditorResourcesPath()); + propertyEditorResourcesPath(), + {qtCreatorItemLibraryPath()}); } } diff --git a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo index 3f3cdb7910c..46cb42b60d2 100644 --- a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo +++ b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo @@ -374,7 +374,6 @@ MetaInfo { name: "Keyframe" category: "none" version: "1.0" - requiredImport: "none" } } @@ -386,7 +385,6 @@ MetaInfo { name: "KeyframeGroup" category: "none" version: "1.0" - requiredImport: "none" } } diff --git a/tests/unit/tests/matchers/projectstorage-matcher.h b/tests/unit/tests/matchers/projectstorage-matcher.h index 5ce6512c14e..02861d7eea3 100644 --- a/tests/unit/tests/matchers/projectstorage-matcher.h +++ b/tests/unit/tests/matchers/projectstorage-matcher.h @@ -53,3 +53,24 @@ MATCHER_P3(IsItemLibraryProperty, return property.name == name && property.type == type && property.value == value; } + +template +auto IsTypeAnnotation(QmlDesigner::SourceId sourceId, + QmlDesigner::SourceId directorySourceId, + Utils::SmallStringView typeName, + QmlDesigner::ModuleId moduleId, + IconPathMatcher iconPath, + TypeTraitsMatcher traits, + HintsJsonMatcher hintsJsonMatcher, + ItemLibraryJsonMatcher itemLibraryJsonMatcher) +{ + using QmlDesigner::Storage::Synchronization::TypeAnnotation; + return AllOf(Field("sourceId", &TypeAnnotation::sourceId, sourceId), + Field("sourceId", &TypeAnnotation::directorySourceId, directorySourceId), + Field("typeName", &TypeAnnotation::typeName, typeName), + Field("moduleId", &TypeAnnotation::moduleId, moduleId), + Field("iconPath", &TypeAnnotation::iconPath, iconPath), + Field("traits", &TypeAnnotation::traits, traits), + Field("hintsJson", &TypeAnnotation::hintsJson, hintsJsonMatcher), + Field("itemLibraryJson", &TypeAnnotation::itemLibraryJson, itemLibraryJsonMatcher)); +} diff --git a/tests/unit/tests/matchers/unittest-matchers.h b/tests/unit/tests/matchers/unittest-matchers.h index 7c52d973b88..faa99a48a2f 100644 --- a/tests/unit/tests/matchers/unittest-matchers.h +++ b/tests/unit/tests/matchers/unittest-matchers.h @@ -95,6 +95,64 @@ private: const QString m_suffix; }; +template +class StartsWithMatcher +{ +public: + explicit StartsWithMatcher(const StringType &prefix) + : m_prefix(prefix) + {} + + template + bool MatchAndExplain(CharType *s, testing::MatchResultListener *listener) const + { + return s != NULL && MatchAndExplain(StringType(s), listener); + } + + template + bool MatchAndExplain(const MatcheeStringType &s, testing::MatchResultListener * /* listener */) const + { + return s.startsWith(m_prefix); + } + + void DescribeTo(::std::ostream *os) const { *os << "ends with " << m_prefix; } + + void DescribeNegationTo(::std::ostream *os) const { *os << "doesn't end with " << m_prefix; } + + StartsWithMatcher(const StartsWithMatcher &) = default; + StartsWithMatcher &operator=(const StartsWithMatcher &) = delete; + +private: + const StringType m_prefix; +}; + +class QStringStartsWithMatcher +{ +public: + explicit QStringStartsWithMatcher(const QString &prefix) + : m_prefix(prefix) + {} + + template + bool MatchAndExplain(const MatcheeStringType &s, testing::MatchResultListener * /* listener */) const + { + return s.startsWith(m_prefix); + } + + void DescribeTo(::std::ostream *os) const + { + *os << "ends with " << testing::PrintToString(m_prefix); + } + + void DescribeNegationTo(::std::ostream *os) const + { + *os << "doesn't end with " << testing::PrintToString(m_prefix); + } + +private: + const QString m_prefix; +}; + class IsEmptyMatcher { public: @@ -157,6 +215,16 @@ inline auto EndsWith(const QStringView &suffix) return ::testing::PolymorphicMatcher(Internal::QStringEndsWithMatcher(suffix.toString())); } +inline auto StartsWith(const Utils::SmallStringView &prefix) +{ + return ::testing::PolymorphicMatcher(Internal::StartsWithMatcher(prefix)); +} + +inline auto StartsWith(const QStringView &prefix) +{ + return ::testing::PolymorphicMatcher(Internal::QStringStartsWithMatcher(prefix.toString())); +} + inline auto IsEmpty() { return ::testing::PolymorphicMatcher(Internal::IsEmptyMatcher()); diff --git a/tests/unit/tests/mocks/projectstoragemock.h b/tests/unit/tests/mocks/projectstoragemock.h index b534814c1d3..8aa5979ddbe 100644 --- a/tests/unit/tests/mocks/projectstoragemock.h +++ b/tests/unit/tests/mocks/projectstoragemock.h @@ -195,6 +195,11 @@ public: type, (QmlDesigner::TypeId typeId), (const, override)); + MOCK_METHOD(QmlDesigner::SmallSourceIds<4>, + typeAnnotationSourceIds, + (QmlDesigner::SourceId directoryId), + (const, override)); + MOCK_METHOD(QmlDesigner::SmallSourceIds<64>, typeAnnotationDirectorySourceIds, (), (const, override)); MOCK_METHOD(Utils::PathString, typeIconPath, (QmlDesigner::TypeId typeId), (const, override)); MOCK_METHOD(QmlDesigner::Storage::Info::TypeHints, typeHints, diff --git a/tests/unit/tests/printers/gtest-creator-printing.cpp b/tests/unit/tests/printers/gtest-creator-printing.cpp index 40f9c8db6da..6bc78e99367 100644 --- a/tests/unit/tests/printers/gtest-creator-printing.cpp +++ b/tests/unit/tests/printers/gtest-creator-printing.cpp @@ -795,7 +795,10 @@ std::ostream &operator<<(std::ostream &out, const SynchronizationPackage &packag << ", projectDatas: " << package.projectDatas << ", propertyEditorQmlPaths: " << package.propertyEditorQmlPaths << ", updatedPropertyEditorQmlPathSourceIds: " - << package.updatedPropertyEditorQmlPathSourceIds << ")"; + << package.updatedPropertyEditorQmlPathSourceIds + << ", typeAnnotations: " << package.typeAnnotations + << ", updatedTypeAnnotationSourceIds: " << package.updatedTypeAnnotationSourceIds + << ")"; } std::ostream &operator<<(std::ostream &out, const ProjectData &data) diff --git a/tests/unit/tests/unittests/projectstorage/data/qml/QtQuick/Controls/designer/qtquickcontrols2.metainfo b/tests/unit/tests/unittests/projectstorage/data/qml/QtQuick/Controls/designer/qtquickcontrols2.metainfo new file mode 100644 index 00000000000..0cd3959cf8a --- /dev/null +++ b/tests/unit/tests/unittests/projectstorage/data/qml/QtQuick/Controls/designer/qtquickcontrols2.metainfo @@ -0,0 +1,575 @@ +MetaInfo { + Type { + name: "QtQuick.Controls.BusyIndicator" + icon: "images/busyindicator-icon16.png" + + ItemLibraryEntry { + name: "Busy Indicator" + category: "Qt Quick - Controls 2" + libraryIcon: "images/busyindicator-icon.png" + version: "2.0" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("Indicates activity while, for example, content is being loaded.") + } + } + + Type { + name: "QtQuick.Controls.Button" + icon: "images/button-icon16.png" + + ItemLibraryEntry { + name: "Button" + category: "Qt Quick - Controls 2" + libraryIcon: "images/button-icon.png" + version: "2.0" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("A button with text.") + + Property { name: "text"; type: "binding"; value: "qsTr(\"Button\")" } + } + } + + Type { + name: "QtQuick.Controls.CheckBox" + icon: "images/checkbox-icon16.png" + + ItemLibraryEntry { + name: "Check Box" + category: "Qt Quick - Controls 2" + libraryIcon: "images/checkbox-icon.png" + version: "2.0" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("A checkbox with a text label.") + + Property { name: "text"; type: "binding"; value: "qsTr(\"Check Box\")" } + } + } + + Type { + name: "QtQuick.Controls.CheckDelegate" + icon: "images/checkbox-icon16.png" + + ItemLibraryEntry { + name: "Check Delegate" + category: "Qt Quick - Controls 2" + libraryIcon: "images/checkbox-icon.png" + version: "2.0" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("Presents items from a model as checkboxes.") + + Property { name: "text"; type: "binding"; value: "qsTr(\"Check Delegate\")" } + } + } + + Type { + name: "QtQuick.Controls.ComboBox" + icon: "images/combobox-icon16.png" + + ItemLibraryEntry { + name: "Combo Box" + category: "Qt Quick - Controls 2" + libraryIcon: "images/combobox-icon.png" + version: "2.0" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("An editable drop-down list.") + } + } + + Type { + name: "QtQuick.Controls.Control" + icon: "images/control-icon16.png" + + ItemLibraryEntry { + name: "Control" + category: "Qt Quick - Controls 2" + libraryIcon: "images/control-icon.png" + version: "2.0" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("An abstract base type for UI controls.") + } + } + + Type { + name: "QtQuick.Controls.DelayButton" + icon: "images/button-icon16.png" + + ItemLibraryEntry { + name: "Delay Button" + category: "Qt Quick - Controls 2" + libraryIcon: "images/delaybutton-icon.png" + version: "2.2" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("A button with a delay preventing accidental presses.") + + Property { name: "text"; type: "binding"; value: "qsTr(\"Delay Button\")" } + } + } + + Type { + name: "QtQuick.Controls.Dial" + icon: "images/dial-icon16.png" + + ItemLibraryEntry { + name: "Dial" + category: "Qt Quick - Controls 2" + libraryIcon: "images/dial-icon.png" + version: "2.0" + requiredImport: "QtQuick.Controls" + + toolTip: qsTr("A circular dial that is rotated to set a value.") + } + } + + Type { + name: "QtQuick.Controls.Frame" + icon: "images/frame-icon16.png" + + ItemLibraryEntry { + name: "Frame" + category: "Qt Quick - Controls 2" + libraryIcon: "images/frame-icon.png" + version: "2.0" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("An untitled container for a group of controls.") + + Property { name: "width"; type: "int"; value: 200 } + Property { name: "height"; type: "int"; value: 200 } + } + } + + Type { + name: "QtQuick.Controls.GroupBox" + icon: "images/groupbox-icon16.png" + + ItemLibraryEntry { + name: "Group Box" + category: "Qt Quick - Controls 2" + libraryIcon: "images/groupbox-icon.png" + version: "2.0" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("A titled container for a group of controls.") + + Property { name: "width"; type: "int"; value: 200 } + Property { name: "height"; type: "int"; value: 200 } + Property { name: "title"; type: "binding"; value: "qsTr(\"Group Box\")" } + } + } + + Type { + name: "QtQuick.Controls.ItemDelegate" + icon: "images/itemdelegate-icon16.png" + + ItemLibraryEntry { + name: "Item Delegate" + category: "Qt Quick - Controls 2" + libraryIcon: "images/itemdelegate-icon.png" + version: "2.0" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("Presents a standard view item. It can be used as a delegate in various views and controls, such as ListView and ComboBox.") + + Property { name: "text"; type: "binding"; value: "qsTr(\"Item Delegate\")" } + } + } + + Type { + name: "QtQuick.Controls.Label" + icon: "images/label-icon16.png" + + ItemLibraryEntry { + name: "Label" + category: "Qt Quick - Controls 2" + libraryIcon: "images/label-icon.png" + version: "2.0" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("A text label.") + + Property { name: "text"; type: "binding"; value: "qsTr(\"Label\")" } + } + } + + Type { + name: "QtQuick.Controls.Page" + icon: "images/page-icon16.png" + + ItemLibraryEntry { + name: "Page" + category: "Qt Quick - Controls 2" + libraryIcon: "images/page-icon.png" + version: "2.0" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("A page with header and footer.") + + Property { name: "width"; type: "int"; value: 200 } + Property { name: "height"; type: "int"; value: 200 } + } + } + + Type { + name: "QtQuick.Controls.PageIndicator" + icon: "images/pageindicator-icon16.png" + + ItemLibraryEntry { + name: "Page Indicator" + category: "Qt Quick - Controls 2" + libraryIcon: "images/pageindicator-icon.png" + version: "2.0" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("Indicates the currently active page.") + + Property { name: "count"; type: "int"; value: 3 } + } + } + + Type { + name: "QtQuick.Controls.Pane" + icon: "images/pane-icon16.png" + + ItemLibraryEntry { + name: "Pane" + category: "Qt Quick - Controls 2" + libraryIcon: "images/pane-icon.png" + version: "2.0" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("Provides a background matching the application style and theme.") + + Property { name: "width"; type: "int"; value: 200 } + Property { name: "height"; type: "int"; value: 200 } + } + } + + Type { + name: "QtQuick.Controls.ProgressBar" + icon: "images/progressbar-icon16.png" + + ItemLibraryEntry { + name: "Progress Bar" + category: "Qt Quick - Controls 2" + libraryIcon: "images/progressbar-icon.png" + version: "2.0" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("A bar indicating the progress of an operation.") + + Property { name: "value"; type: "real"; value: 0.5 } + } + } + + Type { + name: "QtQuick.Controls.RadioButton" + icon: "images/radiobutton-icon16.png" + + ItemLibraryEntry { + name: "Radio Button" + category: "Qt Quick - Controls 2" + libraryIcon: "images/radiobutton-icon.png" + version: "2.0" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("An option button that you can toggle on or off.") + + Property { name: "text"; type: "binding"; value: "qsTr(\"Radio Button\")" } + } + } + + Type { + name: "QtQuick.Controls.RadioDelegate" + icon: "images/radiobutton-icon16.png" + + ItemLibraryEntry { + name: "Radio Delegate" + category: "Qt Quick - Controls 2" + libraryIcon: "images/radiobutton-icon.png" + version: "2.0" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("Presents items from a model as radio buttons.") + + Property { name: "text"; type: "binding"; value: "qsTr(\"Radio Delegate\")" } + } + } + + Type { + name: "QtQuick.Controls.RangeSlider" + icon: "images/rangeslider-icon16.png" + + ItemLibraryEntry { + name: "Range Slider" + category: "Qt Quick - Controls 2" + libraryIcon: "images/rangeslider-icon.png" + version: "2.0" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("A bar with adjustable start and end points.") + + Property { name: "first.value"; type: "real"; value: 0.25 } + Property { name: "second.value"; type: "real"; value: 0.75 } + } + } + + Type { + name: "QtQuick.Controls.RoundButton" + icon: "images/roundbutton-icon16.png" + + ItemLibraryEntry { + name: "Round Button" + category: "Qt Quick - Controls 2" + libraryIcon: "images/roundbutton-icon.png" + version: "2.1" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("A round button with text.") + + Property { name: "text"; type: "string"; value: "+" } + } + } + + Type { + name: "QtQuick.Controls.Slider" + icon: "images/slider-icon16.png" + + ItemLibraryEntry { + name: "Slider" + category: "Qt Quick - Controls 2" + libraryIcon: "images/slider-icon.png" + version: "2.0" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("An adjustable slider.") + + Property { name: "value"; type: "real"; value: 0.5 } + } + } + + Type { + name: "QtQuick.Controls.SpinBox" + icon: "images/spinbox-icon16.png" + + ItemLibraryEntry { + name: "Spin Box" + category: "Qt Quick - Controls 2" + libraryIcon: "images/spinbox-icon.png" + version: "2.0" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("A box with an adjustable number.") + } + } + + Type { + name: "QtQuick.Controls.ScrollView" + icon: "images/scrollview-icon16.png" + + ItemLibraryEntry { + name: "Scroll View" + category: "Qt Quick - Controls 2" + libraryIcon: "images/scrollview-icon.png" + version: "2.2" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("A scrollable area.") + + Property { name: "width"; type: "int"; value: 200 } + Property { name: "height"; type: "int"; value: 200 } + } + } + + Type { + name: "QtQuick.Controls.StackView" + icon: "images/stackview-icon16.png" + + ItemLibraryEntry { + name: "Stack View" + category: "Qt Quick - Controls 2" + libraryIcon: "images/stackview-icon.png" + version: "2.0" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("Provides a stack-based navigation for a set of pages.") + + Property { name: "width"; type: "int"; value: 200 } + Property { name: "height"; type: "int"; value: 200 } + } + } + + Type { + name: "QtQuick.Controls.SwipeDelegate" + icon: "images/itemdelegate-icon16.png" + + ItemLibraryEntry { + name: "Swipe Delegate" + category: "Qt Quick - Controls 2" + libraryIcon: "images/itemdelegate-icon.png" + version: "2.0" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("Presents items from a model as items that you can swipe to expose more options.") + + Property { name: "text"; type: "binding"; value: "qsTr(\"Swipe Delegate\")" } + } + } + + Type { + name: "QtQuick.Controls.SwipeView" + icon: "images/swipeview-icon16.png" + + ItemLibraryEntry { + name: "Swipe View" + category: "Qt Quick - Controls 2" + libraryIcon: "images/swipeview-icon.png" + version: "2.0" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("Provides a view where you can navigate pages by swiping.") + + Property { name: "width"; type: "int"; value: 200 } + Property { name: "height"; type: "int"; value: 200 } + } + } + + Type { + name: "QtQuick.Controls.Switch" + icon: "images/switch-icon16.png" + + ItemLibraryEntry { + name: "Switch" + category: "Qt Quick - Controls 2" + libraryIcon: "images/switch-icon.png" + version: "2.0" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("A button that you can toggle on and off.") + + Property { name: "text"; type: "binding"; value: "qsTr(\"Switch\")" } + } + } + + Type { + name: "QtQuick.Controls.SwitchDelegate" + icon: "images/switch-icon16.png" + + ItemLibraryEntry { + name: "Switch Delegate" + category: "Qt Quick - Controls 2" + libraryIcon: "images/switch-icon.png" + version: "2.0" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("Presents items from a model as toggle switches.") + + Property { name: "text"; type: "binding"; value: "qsTr(\"Switch Delegate\")" } + } + } + + Type { + name: "QtQuick.Controls.TabBar" + icon: "images/toolbar-icon16.png" + + ItemLibraryEntry { + name: "Tab Bar" + category: "Qt Quick - Controls 2" + libraryIcon: "images/toolbar-icon.png" + version: "2.0" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("A tab-based navigation model.") + + Property { name: "width"; type: "int"; value: 240 } + } + } + + Type { + name: "QtQuick.Controls.TabButton" + icon: "images/toolbutton-icon16.png" + + ItemLibraryEntry { + name: "Tab Button" + category: "Qt Quick - Controls 2" + libraryIcon: "images/toolbutton-icon.png" + version: "2.0" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("A button suitable for a tab bar.") + + Property { name: "text"; type: "binding"; value: "qsTr(\"Tab Button\")" } + } + } + + Type { + name: "QtQuick.Controls.TextArea" + icon: "images/textarea-icon16.png" + + ItemLibraryEntry { + name: "Text Area" + category: "Qt Quick - Controls 2" + libraryIcon: "images/textarea-icon.png" + version: "2.0" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("A multi-line text box.") + + Property { name: "placeholderText"; type: "binding"; value: "qsTr(\"Text Area\")" } + } + } + + Type { + name: "QtQuick.Controls.TextField" + icon: "images/textfield-icon16.png" + + ItemLibraryEntry { + name: "Text Field" + category: "Qt Quick - Controls 2" + libraryIcon: "images/textfield-icon.png" + version: "2.0" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("A single-line text box.") + + Property { name: "placeholderText"; type: "binding"; value: "qsTr(\"Text Field\")" } + } + } + + Type { + name: "QtQuick.Controls.ToolBar" + icon: "images/toolbar-icon16.png" + + ItemLibraryEntry { + name: "Tool Bar" + category: "Qt Quick - Controls 2" + libraryIcon: "images/toolbar-icon.png" + version: "2.0" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("A row that can hold actions and buttons.") + + Property { name: "width"; type: "int"; value: 360 } + } + } + + Type { + name: "QtQuick.Controls.ToolButton" + icon: "images/toolbutton-icon16.png" + + ItemLibraryEntry { + name: "Tool Button" + category: "Qt Quick - Controls 2" + libraryIcon: "images/toolbutton-icon.png" + version: "2.0" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("A button suitable for a tool bar.") + + Property { name: "text"; type: "binding"; value: "qsTr(\"Tool Button\")" } + } + } + + Type { + name: "QtQuick.Controls.ToolSeparator" + icon: "images/toolseparator-icon16.png" + + ItemLibraryEntry { + name: "Tool Separator" + category: "Qt Quick - Controls 2" + libraryIcon: "images/toolseparator-icon.png" + version: "2.1" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("A line to separate sections in a tool bar.") + } + } + + Type { + name: "QtQuick.Controls.Tumbler" + icon: "images/tumbler-icon16.png" + + ItemLibraryEntry { + name: "Tumbler" + category: "Qt Quick - Controls 2" + libraryIcon: "images/tumbler-icon.png" + version: "2.0" + requiredImport: "QtQuick.Controls" + toolTip: qsTr("A spinnable wheel of selectable items.") + + Property { name: "model"; type: "int"; value: "10" } + } + } +} diff --git a/tests/unit/tests/unittests/projectstorage/data/qml/QtQuick3D/AssetUtils/designer/assetutils.metainfo b/tests/unit/tests/unittests/projectstorage/data/qml/QtQuick3D/AssetUtils/designer/assetutils.metainfo new file mode 100644 index 00000000000..47abeea7a36 --- /dev/null +++ b/tests/unit/tests/unittests/projectstorage/data/qml/QtQuick3D/AssetUtils/designer/assetutils.metainfo @@ -0,0 +1,21 @@ +MetaInfo { + Type { + name: "QtQuick3D.AssetUtils.RuntimeLoader" + icon: "images/runtimeloader16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Runtime Loader" + category: "AssetUtils" + libraryIcon: "images/runtimeloader.png" + version: "6.2" + requiredImport: "QtQuick3D.AssetUtils" + } + } +} diff --git a/tests/unit/tests/unittests/projectstorage/data/qml/QtQuick3D/Effects/designer/effectlib.metainfo b/tests/unit/tests/unittests/projectstorage/data/qml/QtQuick3D/Effects/designer/effectlib.metainfo new file mode 100644 index 00000000000..7ad3357894c --- /dev/null +++ b/tests/unit/tests/unittests/projectstorage/data/qml/QtQuick3D/Effects/designer/effectlib.metainfo @@ -0,0 +1,401 @@ +MetaInfo { + Type { + name: "QtQuick3D.Effects.AdditiveColorGradient" + icon: "images/effect16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: false + } + + ItemLibraryEntry { + name: "Additive Color Gradient" + category: "Qt Quick 3D Effects" + libraryIcon: "images/effect.png" + version: "1.0" + requiredImport: "QtQuick3D.Effects" + } + } + Type { + name: "QtQuick3D.Effects.Blur" + icon: "images/effect16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: false + } + + ItemLibraryEntry { + name: "Blur" + category: "Qt Quick 3D Effects" + libraryIcon: "images/effect.png" + version: "1.0" + requiredImport: "QtQuick3D.Effects" + } + } + Type { + name: "QtQuick3D.Effects.BrushStrokes" + icon: "images/effect16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: false + } + + ItemLibraryEntry { + name: "Brush Strokes" + category: "Qt Quick 3D Effects" + libraryIcon: "images/effect.png" + version: "1.0" + requiredImport: "QtQuick3D.Effects" + } + } + Type { + name: "QtQuick3D.Effects.ChromaticAberration" + icon: "images/effect16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: false + } + + ItemLibraryEntry { + name: "Chromatic Aberration" + category: "Qt Quick 3D Effects" + libraryIcon: "images/effect.png" + version: "1.0" + requiredImport: "QtQuick3D.Effects" + } + } + Type { + name: "QtQuick3D.Effects.ColorMaster" + icon: "images/effect16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: false + } + + ItemLibraryEntry { + name: "Color Master" + category: "Qt Quick 3D Effects" + libraryIcon: "images/effect.png" + version: "1.0" + requiredImport: "QtQuick3D.Effects" + } + } + Type { + name: "QtQuick3D.Effects.DepthOfFieldHQBlur" + icon: "images/effect16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: false + } + + ItemLibraryEntry { + name: "Depth of Field HQ Blur" + category: "Qt Quick 3D Effects" + libraryIcon: "images/effect.png" + version: "1.0" + requiredImport: "QtQuick3D.Effects" + } + } + Type { + name: "QtQuick3D.Effects.Desaturate" + icon: "images/effect16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: false + } + + ItemLibraryEntry { + name: "Desaturate" + category: "Qt Quick 3D Effects" + libraryIcon: "images/effect.png" + version: "1.0" + requiredImport: "QtQuick3D.Effects" + } + } + Type { + name: "QtQuick3D.Effects.DistortionRipple" + icon: "images/effect16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: false + } + + ItemLibraryEntry { + name: "Distortion Ripple" + category: "Qt Quick 3D Effects" + libraryIcon: "images/effect.png" + version: "1.0" + requiredImport: "QtQuick3D.Effects" + } + } + Type { + name: "QtQuick3D.Effects.DistortionSphere" + icon: "images/effect16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: false + } + + ItemLibraryEntry { + name: "Distortion Sphere" + category: "Qt Quick 3D Effects" + libraryIcon: "images/effect.png" + version: "1.0" + requiredImport: "QtQuick3D.Effects" + } + } + Type { + name: "QtQuick3D.Effects.DistortionSpiral" + icon: "images/effect16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: false + } + + ItemLibraryEntry { + name: "Distortion Spiral" + category: "Qt Quick 3D Effects" + libraryIcon: "images/effect.png" + version: "1.0" + requiredImport: "QtQuick3D.Effects" + } + } + Type { + name: "QtQuick3D.Effects.EdgeDetect" + icon: "images/effect16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: false + } + + ItemLibraryEntry { + name: "Edge Detect" + category: "Qt Quick 3D Effects" + libraryIcon: "images/effect.png" + version: "1.0" + requiredImport: "QtQuick3D.Effects" + } + } + Type { + name: "QtQuick3D.Effects.Emboss" + icon: "images/effect16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: false + } + + ItemLibraryEntry { + name: "Emboss" + category: "Qt Quick 3D Effects" + libraryIcon: "images/effect.png" + version: "1.0" + requiredImport: "QtQuick3D.Effects" + } + } + Type { + name: "QtQuick3D.Effects.Flip" + icon: "images/effect16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: false + } + + ItemLibraryEntry { + name: "Flip" + category: "Qt Quick 3D Effects" + libraryIcon: "images/effect.png" + version: "1.0" + requiredImport: "QtQuick3D.Effects" + } + } + Type { + name: "QtQuick3D.Effects.Fxaa" + icon: "images/effect16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: false + } + + ItemLibraryEntry { + name: "Fxaa" + category: "Qt Quick 3D Effects" + libraryIcon: "images/effect.png" + version: "1.0" + requiredImport: "QtQuick3D.Effects" + } + } + Type { + name: "QtQuick3D.Effects.GaussianBlur" + icon: "images/effect16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: false + } + + ItemLibraryEntry { + name: "Gaussian Blur" + category: "Qt Quick 3D Effects" + libraryIcon: "images/effect.png" + version: "1.0" + requiredImport: "QtQuick3D.Effects" + } + } + Type { + name: "QtQuick3D.Effects.HDRBloomTonemap" + icon: "images/effect16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: false + } + + ItemLibraryEntry { + name: "HDR Bloom Tonemap" + category: "Qt Quick 3D Effects" + libraryIcon: "images/effect.png" + version: "1.0" + requiredImport: "QtQuick3D.Effects" + } + } + Type { + name: "QtQuick3D.Effects.MotionBlur" + icon: "images/effect16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: false + } + + ItemLibraryEntry { + name: "Motion Blur" + category: "Qt Quick 3D Effects" + libraryIcon: "images/effect.png" + version: "1.0" + requiredImport: "QtQuick3D.Effects" + } + } + Type { + name: "QtQuick3D.Effects.Scatter" + icon: "images/effect16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: false + } + + ItemLibraryEntry { + name: "Scatter" + category: "Qt Quick 3D Effects" + libraryIcon: "images/effect.png" + version: "1.0" + requiredImport: "QtQuick3D.Effects" + } + } + Type { + name: "QtQuick3D.Effects.SCurveTonemap" + icon: "images/effect16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: false + } + + ItemLibraryEntry { + name: "SCurve Tonemap" + category: "Qt Quick 3D Effects" + libraryIcon: "images/effect.png" + version: "1.0" + requiredImport: "QtQuick3D.Effects" + } + } + Type { + name: "QtQuick3D.Effects.TiltShift" + icon: "images/effect16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: false + } + + ItemLibraryEntry { + name: "Tilt Shift" + category: "Qt Quick 3D Effects" + libraryIcon: "images/effect.png" + version: "1.0" + requiredImport: "QtQuick3D.Effects" + } + } + Type { + name: "QtQuick3D.Effects.Vignette" + icon: "images/effect16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: false + } + + ItemLibraryEntry { + name: "Vignette" + category: "Qt Quick 3D Effects" + libraryIcon: "images/effect.png" + version: "1.0" + requiredImport: "QtQuick3D.Effects" + } + } +} diff --git a/tests/unit/tests/unittests/projectstorage/data/qml/QtQuick3D/Helpers/designer/helpers.metainfo b/tests/unit/tests/unittests/projectstorage/data/qml/QtQuick3D/Helpers/designer/helpers.metainfo new file mode 100644 index 00000000000..83492e2c811 --- /dev/null +++ b/tests/unit/tests/unittests/projectstorage/data/qml/QtQuick3D/Helpers/designer/helpers.metainfo @@ -0,0 +1,261 @@ +MetaInfo { + Type { + name: "QtQuick3D.Helpers.LookAtNode" + icon: "images/lookatnode16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Look-at Node" + category: "Helpers" + libraryIcon: "images/lookatnode.png" + version: "6.4" + requiredImport: "QtQuick3D.Helpers" + } + } + + Type { + name: "QtQuick3D.Helpers.AxisHelper" + icon: "images/axishelper16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Axis Helper" + category: "Helpers" + libraryIcon: "images/axishelper.png" + version: "6.0" + requiredImport: "QtQuick3D.Helpers" + } + } + + Type { + name: "QtQuick3D.Helpers.DebugView" + icon: "images/debugview16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: true + canBeDroppedInView3D: false + } + + ItemLibraryEntry { + name: "Debug View" + category: "Helpers" + libraryIcon: "images/debugview.png" + version: "6.0" + requiredImport: "QtQuick3D.Helpers" + } + } + + Type { + name: "QtQuick3D.Helpers.GridGeometry" + icon: "images/gridgeometry16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Grid Geometry" + category: "Helpers" + libraryIcon: "images/gridgeometry.png" + version: "6.0" + requiredImport: "QtQuick3D.Helpers" + } + } + + Type { + name: "QtQuick3D.Helpers.HeightFieldGeometry" + icon: "images/heightfieldgeometry16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Height Field Geometry" + category: "Helpers" + libraryIcon: "images/heightfieldgeometry.png" + version: "6.4" + requiredImport: "QtQuick3D.Helpers" + } + } + + Type { + name: "QtQuick3D.Helpers.InstanceModel" + icon: "images/instancemodel16.png" + + Hints { + visibleInNavigator: false + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: false + } + + ItemLibraryEntry { + name: "Instance Model" + category: "Helpers" + libraryIcon: "images/instancemodel.png" + version: "6.4" + requiredImport: "QtQuick3D.Helpers" + } + } + + Type { + name: "QtQuick3D.Helpers.InstanceRepeater" + icon: "images/instancerepeater16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Instance Repeater" + category: "Helpers" + libraryIcon: "images/instancerepeater.png" + version: "6.4" + requiredImport: "QtQuick3D.Helpers" + } + } + + Type { + name: "QtQuick3D.Helpers.WasdController" + icon: "images/wasdcontroller16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: true + canBeDroppedInView3D: false + } + + ItemLibraryEntry { + name: "Wasd Controller" + category: "Helpers" + libraryIcon: "images/wasdcontroller.png" + version: "6.0" + requiredImport: "QtQuick3D.Helpers" + } + } + + Type { + name: "QtQuick3D.Helpers.InfiniteGrid" + icon: "images/infinitegrid16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: false + } + + ItemLibraryEntry { + name: "Infinite Grid" + category: "Helpers" + libraryIcon: "images/infinitegrid.png" + version: "6.5" + requiredImport: "QtQuick3D.Helpers" + } + } + + Type { + name: "QtQuick3D.Helpers.OrbitCameraController" + icon: "images/orbitcameracontroller16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: true + canBeDroppedInView3D: false + } + + ItemLibraryEntry { + name: "Orbit Camera Controller" + category: "Helpers" + libraryIcon: "images/orbitcameracontroller.png" + version: "6.4" + requiredImport: "QtQuick3D.Helpers" + } + } + + Type { + name: "QtQuick3D.Helpers.ProceduralSkyTextureData" + icon: "images/proceduralskytexturedata16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: false + } + + ItemLibraryEntry { + name: "Procedural Sky Texture Data" + category: "Helpers" + libraryIcon: "images/proceduralskytexturedata.png" + version: "6.4" + requiredImport: "QtQuick3D.Helpers" + } + } + + Type { + name: "QtQuick3D.Helpers.ExtendedSceneEnvironment" + icon: "images/extendedsceneenvironment16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: false + } + + ItemLibraryEntry { + name: "Extended Scene Environment" + category: "Helpers" + libraryIcon: "images/extendedsceneenvironment.png" + version: "6.5" + requiredImport: "QtQuick3D.Helpers" + } + } + + Type { + name: "QtQuick3D.Helpers.LodManager" + icon: "images/lodmanager16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Lod Manager" + category: "Helpers" + libraryIcon: "images/lodmanager.png" + version: "6.5" + requiredImport: "QtQuick3D.Helpers" + } + } +} diff --git a/tests/unit/tests/unittests/projectstorage/data/qml/QtQuick3D/ParticleEffects/designer/particleeffects.metainfo b/tests/unit/tests/unittests/projectstorage/data/qml/QtQuick3D/ParticleEffects/designer/particleeffects.metainfo new file mode 100644 index 00000000000..d9bc305b4ae --- /dev/null +++ b/tests/unit/tests/unittests/projectstorage/data/qml/QtQuick3D/ParticleEffects/designer/particleeffects.metainfo @@ -0,0 +1,246 @@ +MetaInfo { + Type { + name: "QtQuick3D.Particle3D.ParticleSystem3D" + icon: "images/dummy16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Clouds" + category: "Qt Quick 3D Particle Effects" + libraryIcon: "images/dummy.png" + version: "6.2" + requiredImport: "QtQuick3D.ParticleEffects" + QmlSource { source: "./source/particleeffect_clouds.qml" } + ExtraFile { source: "images/smoke_sprite2.png" } + } + } + + Type { + name: "QtQuick3D.Particle3D.ParticleSystem3D" + icon: "images/dummy16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Dust" + category: "Qt Quick 3D Particle Effects" + libraryIcon: "images/dummy.png" + version: "6.2" + requiredImport: "QtQuick3D.ParticleEffects" + QmlSource { source: "./source/particleeffect_dust.qml" } + ExtraFile { source: "images/sphere.png" } + } + } + + Type { + name: "QtQuick3D.Particle3D.ParticleSystem3D" + icon: "images/dummy16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Exhaust" + category: "Qt Quick 3D Particle Effects" + libraryIcon: "images/dummy.png" + version: "6.2" + requiredImport: "QtQuick3D.ParticleEffects" + QmlSource { source: "./source/particleeffect_exhaust.qml" } + ExtraFile { source: "images/smoke2.png" } + } + } + + Type { + name: "QtQuick3D.Particle3D.ParticleSystem3D" + icon: "images/dummy16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Fire" + category: "Qt Quick 3D Particle Effects" + libraryIcon: "images/dummy.png" + version: "6.2" + requiredImport: "QtQuick3D.ParticleEffects" + QmlSource { source: "./source/particleeffect_fire.qml" } + ExtraFile { source: "images/smoke_sprite.png" } + ExtraFile { source: "images/sphere.png" } + ExtraFile { source: "images/color_table.png" } + ExtraFile { source: "images/color_table2.png" } + } + } + + Type { + name: "QtQuick3D.Particle3D.ParticleSystem3D" + icon: "images/dummy16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Heavy Rain" + category: "Qt Quick 3D Particle Effects" + libraryIcon: "images/dummy.png" + version: "6.2" + requiredImport: "QtQuick3D.ParticleEffects" + QmlSource { source: "./source/particleeffect_heavyrain.qml" } + ExtraFile { source: "images/rain.png" } + ExtraFile { source: "images/sphere.png" } + ExtraFile { source: "images/ripple.png" } + ExtraFile { source: "images/splash7.png" } + } + } + + Type { + name: "QtQuick3D.Particle3D.ParticleSystem3D" + icon: "images/dummy16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Heavy Rain - Tire Spray" + category: "Qt Quick 3D Particle Effects" + libraryIcon: "images/dummy.png" + version: "6.2" + requiredImport: "QtQuick3D.ParticleEffects" + QmlSource { source: "./source/particleeffect_heavyrain_tirespray.qml" } + ExtraFile { source: "images/smoke2.png" } + } + } + + Type { + name: "QtQuick3D.Particle3D.ParticleSystem3D" + icon: "images/dummy16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Light Rain" + category: "Qt Quick 3D Particle Effects" + libraryIcon: "images/dummy.png" + version: "6.2" + requiredImport: "QtQuick3D.ParticleEffects" + QmlSource { source: "./source/particleeffect_lightrain.qml" } + ExtraFile { source: "images/rain.png" } + ExtraFile { source: "images/splash7.png" } + } + } + Type { + name: "QtQuick3D.Particle3D.ParticleSystem3D" + icon: "images/dummy16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Light Rain - Tire Spray" + category: "Qt Quick 3D Particle Effects" + libraryIcon: "images/dummy.png" + version: "6.2" + requiredImport: "QtQuick3D.ParticleEffects" + QmlSource { source: "./source/particleeffect_lightrain_tirespray.qml" } + ExtraFile { source: "images/smoke2.png" } + } + } + Type { + name: "QtQuick3D.Particle3D.ParticleSystem3D" + icon: "images/dummy16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Rain Mist" + category: "Qt Quick 3D Particle Effects" + libraryIcon: "images/dummy.png" + version: "6.2" + requiredImport: "QtQuick3D.ParticleEffects" + QmlSource { source: "./source/particleeffect_rainmist.qml" } + ExtraFile { source: "images/smoke2.png" } + } + } + Type { + name: "QtQuick3D.Particle3D.ParticleSystem3D" + icon: "images/dummy16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Snow" + category: "Qt Quick 3D Particle Effects" + libraryIcon: "images/dummy.png" + version: "6.2" + requiredImport: "QtQuick3D.ParticleEffects" + QmlSource { source: "./source/particleeffect_snow.qml" } + ExtraFile { source: "images/snowflake.png" } + } + } + Type { + name: "QtQuick3D.Particle3D.ParticleSystem3D" + icon: "images/dummy16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Steam" + category: "Qt Quick 3D Particle Effects" + libraryIcon: "images/dummy.png" + version: "6.2" + requiredImport: "QtQuick3D.ParticleEffects" + QmlSource { source: "./source/particleeffect_steam.qml" } + ExtraFile { source: "images/smoke2.png" } + } + } +} diff --git a/tests/unit/tests/unittests/projectstorage/data/qml/QtQuick3D/Particles3D/designer/particles3d.metainfo b/tests/unit/tests/unittests/projectstorage/data/qml/QtQuick3D/Particles3D/designer/particles3d.metainfo new file mode 100644 index 00000000000..d2a2999c50a --- /dev/null +++ b/tests/unit/tests/unittests/projectstorage/data/qml/QtQuick3D/Particles3D/designer/particles3d.metainfo @@ -0,0 +1,562 @@ +MetaInfo { + Type { + name: "QtQuick3D.Particles3D.Attractor3D" + icon: "images/attractor-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Attractor" + category: "Qt Quick 3D Particles 3D" + libraryIcon: "images/attractor-24px.png" + version: "6.2" + requiredImport: "QtQuick3D.Particles3D" + } + } + Type { + name: "QtQuick3D.Particles3D.DynamicBurst3D" + icon: "images/emit-burst-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Dynamic Burst" + category: "Qt Quick 3D Particles 3D" + libraryIcon: "images/emit-burst-24px.png" + version: "6.3" + requiredImport: "QtQuick3D.Particles3D" + } + } + Type { + name: "QtQuick3D.Particles3D.EmitBurst3D" + icon: "images/emit-burst-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Emit Burst" + category: "Qt Quick 3D Particles 3D" + libraryIcon: "images/emit-burst-24px.png" + version: "6.2" + requiredImport: "QtQuick3D.Particles3D" + } + } + Type { + name: "QtQuick3D.Particles3D.ParticleEmitter3D" + icon: "images/emitter-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Emitter" + category: "Qt Quick 3D Particles 3D" + libraryIcon: "images/emitter-24px.png" + version: "6.2" + requiredImport: "QtQuick3D.Particles3D" + } + } + Type { + name: "QtQuick3D.Particles3D.Gravity3D" + icon: "images/gravity-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Gravity" + category: "Qt Quick 3D Particles 3D" + libraryIcon: "images/gravity-24px.png" + version: "6.2" + requiredImport: "QtQuick3D.Particles3D" + } + } + Type { + name: "QtQuick3D.Particles3D.ModelBlendParticle3D" + icon: "images/model-blend-particle-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Model Blend Particle" + category: "Qt Quick 3D Particles 3D" + libraryIcon: "images/model-blend-particle-24px.png" + version: "6.2" + requiredImport: "QtQuick3D.Particles3D" + } + } + Type { + name: "QtQuick3D.Particles3D.ModelParticle3D" + icon: "images/model-particle-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Model Particle" + category: "Qt Quick 3D Particles 3D" + libraryIcon: "images/model-particle-24px.png" + version: "6.2" + requiredImport: "QtQuick3D.Particles3D" + } + } + Type { + name: "QtQuick3D.Particles3D.ParticleCustomShape3D" + icon: "images/particle-custom-shape-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Custom Shape" + category: "Qt Quick 3D Particles 3D" + libraryIcon: "images/particle-custom-shape-24px.png" + version: "6.3" + requiredImport: "QtQuick3D.Particles3D" + } + } + Type { + name: "QtQuick3D.Particles3D.ParticleModelShape3D" + icon: "images/model-shape-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Model Shape" + category: "Qt Quick 3D Particles 3D" + libraryIcon: "images/model-shape-24px.png" + version: "6.2" + requiredImport: "QtQuick3D.Particles3D" + } + } + Type { + name: "QtQuick3D.Particles3D.PointRotator3D" + icon: "images/point-rotator-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Point Rotator" + category: "Qt Quick 3D Particles 3D" + libraryIcon: "images/point-rotator-24px.png" + version: "6.2" + requiredImport: "QtQuick3D.Particles3D" + } + } + Type { + name: "QtQuick3D.Particles3D.ParticleShape3D" + icon: "images/particle-shape-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Particle Shape" + category: "Qt Quick 3D Particles 3D" + libraryIcon: "images/particle-shape-24px.png" + version: "6.2" + requiredImport: "QtQuick3D.Particles3D" + } + } + Type { + name: "QtQuick3D.Particles3D.SpriteParticle3D" + icon: "images/sprite-particle-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Sprite Particle" + category: "Qt Quick 3D Particles 3D" + libraryIcon: "images/sprite-particle-24px.png" + version: "6.2" + requiredImport: "QtQuick3D.Particles3D" + } + } + Type { + name: "QtQuick3D.Particles3D.SpriteSequence3D" + icon: "images/sprite-sequence-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Sprite Sequence" + category: "Qt Quick 3D Particles 3D" + libraryIcon: "images/sprite-sequence-24px.png" + version: "6.2" + requiredImport: "QtQuick3D.Particles3D" + } + } + Type { + name: "QtQuick3D.Particles3D.ParticleSystem3D" + icon: "images/particle-system-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Particle System" + category: "Qt Quick 3D Particles 3D" + libraryIcon: "images/particle-system-24px.png" + version: "6.2" + requiredImport: "QtQuick3D.Particles3D" + QmlSource { source: "./source/particlesystem_template.qml" } + } + } + Type { + name: "QtQuick3D.Particles3D.TargetDirection3D" + icon: "images/target-direction-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Target Direction" + category: "Qt Quick 3D Particles 3D" + libraryIcon: "images/target-direction-24px.png" + version: "6.2" + requiredImport: "QtQuick3D.Particles3D" + } + } + Type { + name: "QtQuick3D.Particles3D.TrailEmitter3D" + icon: "images/trail-emitter-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Trail Emitter" + category: "Qt Quick 3D Particles 3D" + libraryIcon: "images/trail-emitter-24px.png" + version: "6.2" + requiredImport: "QtQuick3D.Particles3D" + } + } + Type { + name: "QtQuick3D.Particles3D.VectorDirection3D" + icon: "images/vector-direction-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Vector Direction" + category: "Qt Quick 3D Particles 3D" + libraryIcon: "images/vector-direction-24px.png" + version: "6.2" + requiredImport: "QtQuick3D.Particles3D" + } + } + Type { + name: "QtQuick3D.Particles3D.Wander3D" + icon: "images/wander-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Wander" + category: "Qt Quick 3D Particles 3D" + libraryIcon: "images/wander-24px.png" + version: "6.2" + requiredImport: "QtQuick3D.Particles3D" + } + } + Type { + name: "QtQuick3D.Particles3D.ParticleSystem3D" + icon: "images/particle-system-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Animated Sprite" + category: "Qt Quick 3D Particle System Templates" + libraryIcon: "images/particle-system-24px.png" + version: "6.2" + requiredImport: "QtQuick3D.Particles3D" + QmlSource { source: "./source/particlesystem_animatedsprite_template.qml" } + } + } + Type { + name: "QtQuick3D.Particles3D.ParticleSystem3D" + icon: "images/particle-system-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Attractor System" + category: "Qt Quick 3D Particle System Templates" + libraryIcon: "images/particle-system-24px.png" + version: "6.2" + requiredImport: "QtQuick3D.Particles3D" + QmlSource { source: "./source/particlesystem_attractor_template.qml" } + } + } + Type { + name: "QtQuick3D.Particles3D.ParticleSystem3D" + icon: "images/particle-system-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Burst" + category: "Qt Quick 3D Particle System Templates" + libraryIcon: "images/particle-system-24px.png" + version: "6.2" + requiredImport: "QtQuick3D.Particles3D" + QmlSource { source: "./source/particlesystem_burst_template.qml" } + } + } + Type { + name: "QtQuick3D.Particles3D.ParticleSystem3D" + icon: "images/particle-system-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Model Blend" + category: "Qt Quick 3D Particle System Templates" + libraryIcon: "images/particle-system-24px.png" + version: "6.2" + requiredImport: "QtQuick3D.Particles3D" + QmlSource { source: "./source/particlesystem_modelblend_template.qml" } + } + } + Type { + name: "QtQuick3D.Particles3D.ParticleSystem3D" + icon: "images/particle-system-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Model Shape" + category: "Qt Quick 3D Particle System Templates" + libraryIcon: "images/particle-system-24px.png" + version: "6.2" + requiredImport: "QtQuick3D.Particles3D" + QmlSource { source: "./source/particlesystem_modelshape_template.qml" } + } + } + Type { + name: "QtQuick3D.Particles3D.ParticleSystem3D" + icon: "images/particle-system-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Particle Trail" + category: "Qt Quick 3D Particle System Templates" + libraryIcon: "images/particle-system-24px.png" + version: "6.2" + requiredImport: "QtQuick3D.Particles3D" + QmlSource { source: "./source/particlesystem_particletrail_template.qml" } + } + } + Type { + name: "QtQuick3D.Particles3D.ParticleSystem3D" + icon: "images/particle-system-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Sprite" + category: "Qt Quick 3D Particle System Templates" + libraryIcon: "images/particle-system-24px.png" + version: "6.2" + requiredImport: "QtQuick3D.Particles3D" + QmlSource { source: "./source/particlesystem_sprite_template.qml" } + } + } + Type { + name: "QtQuick3D.Particles3D.ParticleSystem3D" + icon: "images/particle-system-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Wander" + category: "Qt Quick 3D Particle System Templates" + libraryIcon: "images/particle-system-24px.png" + version: "6.2" + requiredImport: "QtQuick3D.Particles3D" + QmlSource { source: "./source/particlesystem_wander_template.qml" } + } + } + Type { + name: "QtQuick3D.Particles3D.LineParticle3D" + icon: "images/line-particle-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Line Particle" + category: "Qt Quick 3D Particles 3D" + libraryIcon: "images/line-particle-24px.png" + version: "6.4" + requiredImport: "QtQuick3D.Particles3D" + } + } + Type { + name: "QtQuick3D.Particles3D.Repeller3D" + icon: "images/repeller-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Repeller" + category: "Qt Quick 3D Particles 3D" + libraryIcon: "images/repeller-24px.png" + version: "6.4" + requiredImport: "QtQuick3D.Particles3D" + } + } + Type { + name: "QtQuick3D.Particles3D.ScaleAffector3D" + icon: "images/scale-affector-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Scale Affector" + category: "Qt Quick 3D Particles 3D" + libraryIcon: "images/scale-affector-24px.png" + version: "6.4" + requiredImport: "QtQuick3D.Particles3D" + } + } +} diff --git a/tests/unit/tests/unittests/projectstorage/data/qml/QtQuick3D/Physics/designer/physics.metainfo b/tests/unit/tests/unittests/projectstorage/data/qml/QtQuick3D/Physics/designer/physics.metainfo new file mode 100644 index 00000000000..874e209dc52 --- /dev/null +++ b/tests/unit/tests/unittests/projectstorage/data/qml/QtQuick3D/Physics/designer/physics.metainfo @@ -0,0 +1,261 @@ +MetaInfo { + Type { + name: "QtQuick3D.Physics.PhysicsWorld" + icon: "images/physicsworld16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: false + } + + ItemLibraryEntry { + name: "Physics World" + category: "Components" + libraryIcon: "images/physicsworld.png" + version: "6.5" + requiredImport: "QtQuick3D.Physics" + } + } + + Type { + name: "QtQuick3D.Physics.TriggerBody" + icon: "images/triggerbody16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Trigger Body" + category: "Collision Bodies" + libraryIcon: "images/triggerbody.png" + version: "6.5" + requiredImport: "QtQuick3D.Physics" + } + } + + Type { + name: "QtQuick3D.Physics.StaticRigidBody" + icon: "images/staticrigidbody16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Static Rigid Body" + category: "Collision Bodies" + libraryIcon: "images/staticrigidbody.png" + version: "6.5" + requiredImport: "QtQuick3D.Physics" + } + } + + Type { + name: "QtQuick3D.Physics.DynamicRigidBody" + icon: "images/dynamicrigidbody16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Dynamic Rigid Body" + category: "Collision Bodies" + libraryIcon: "images/dynamicrigidbody.png" + version: "6.5" + requiredImport: "QtQuick3D.Physics" + } + } + + Type { + name: "QtQuick3D.Physics.PhysicsMaterial" + icon: "images/physicsmaterial16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: false + } + + ItemLibraryEntry { + name: "Physics Material" + category: "Components" + libraryIcon: "images/physicsmaterial.png" + version: "6.5" + requiredImport: "QtQuick3D.Physics" + } + } + + Type { + name: "QtQuick3D.Physics.BoxShape" + icon: "images/boxshape16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Box Shape" + category: "Collision Shapes" + libraryIcon: "images/boxshape.png" + version: "6.5" + requiredImport: "QtQuick3D.Physics" + } + } + + Type { + name: "QtQuick3D.Physics.CapsuleShape" + icon: "images/capsuleshape16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Capsule Shape" + category: "Collision Shapes" + libraryIcon: "images/capsuleshape.png" + version: "6.5" + requiredImport: "QtQuick3D.Physics" + } + } + + Type { + name: "QtQuick3D.Physics.ConvexMeshShape" + icon: "images/convexmeshshape16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Convex Mesh Shape" + category: "Collision Shapes" + libraryIcon: "images/convexmeshshape.png" + version: "6.5" + requiredImport: "QtQuick3D.Physics" + } + } + + Type { + name: "QtQuick3D.Physics.HeightFieldShape" + icon: "images/heightfieldshape16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Height Field Shape" + category: "Collision Shapes" + libraryIcon: "images/heightfieldshape.png" + version: "6.5" + requiredImport: "QtQuick3D.Physics" + } + } + + Type { + name: "QtQuick3D.Physics.PlaneShape" + icon: "images/planeshape16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Plane Shape" + category: "Collision Shapes" + libraryIcon: "images/planeshape.png" + version: "6.5" + requiredImport: "QtQuick3D.Physics" + } + } + + Type { + name: "QtQuick3D.Physics.SphereShape" + icon: "images/sphereshape16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Sphere Shape" + category: "Collision Shapes" + libraryIcon: "images/sphereshape.png" + version: "6.5" + requiredImport: "QtQuick3D.Physics" + } + } + + Type { + name: "QtQuick3D.Physics.TriangleMeshShape" + icon: "images/trianglemeshshape16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Triangle Mesh Shape" + category: "Collision Shapes" + libraryIcon: "images/trianglemeshshape.png" + version: "6.5" + requiredImport: "QtQuick3D.Physics" + } + } + + Type { + name: "QtQuick3D.Physics.CharacterController" + icon: "images/charactercontroller16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Character Controller" + category: "Collision Bodies" + libraryIcon: "images/charactercontroller.png" + version: "6.5" + requiredImport: "QtQuick3D.Physics" + } + } +} diff --git a/tests/unit/tests/unittests/projectstorage/data/qml/QtQuick3D/designer/quick3d.metainfo b/tests/unit/tests/unittests/projectstorage/data/qml/QtQuick3D/designer/quick3d.metainfo new file mode 100644 index 00000000000..852f9081295 --- /dev/null +++ b/tests/unit/tests/unittests/projectstorage/data/qml/QtQuick3D/designer/quick3d.metainfo @@ -0,0 +1,861 @@ +MetaInfo { + Type { + name: "QtQuick3D.PerspectiveCamera" + icon: "images/camera16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Perspective Camera" + category: "Cameras" + libraryIcon: "images/camera.png" + version: "6.0" + requiredImport: "QtQuick3D" + Property { name: "z"; type: "int"; value: 500; } + toolTip: qsTr("A camera that uses perspective projection.") + } + } + Type { + name: "QtQuick3D.OrthographicCamera" + icon: "images/camera16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Orthographic Camera" + category: "Cameras" + libraryIcon: "images/camera.png" + version: "6.0" + requiredImport: "QtQuick3D" + Property { name: "z"; type: "int"; value: 500; } + toolTip: qsTr("A parallel projection Camera, in which an object's perceived scale is unaffected by its distance from the Camera.") + } + } + Type { + name: "QtQuick3D.FrustumCamera" + icon: "images/camera16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Frustum Camera" + category: "Cameras" + libraryIcon: "images/camera.png" + version: "6.0" + requiredImport: "QtQuick3D" + Property { name: "z"; type: "int"; value: 500; } + toolTip: qsTr("A perspective camera with a custom frustum.") + } + } + Type { + name: "QtQuick3D.CustomCamera" + icon: "images/camera16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Custom Camera" + category: "Cameras" + libraryIcon: "images/camera.png" + version: "6.0" + requiredImport: "QtQuick3D" + Property { name: "z"; type: "int"; value: 500; } + toolTip: qsTr("A camera with a custom projection matrix.") + } + } + Type { + name: "QtQuick3D.CustomMaterial" + icon: "images/custommaterial16.png" + + Hints { + visibleInNavigator: false + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + } + + ItemLibraryEntry { + name: "Custom Material" + category: "Materials" + libraryIcon: "images/custommaterial.png" + version: "6.0" + requiredImport: "QtQuick3D" + Property { name: "fragmentShader"; type: "QUrl"; value: "custom_material_default_shader.frag"; } + ExtraFile { source: "source/custom_material_default_shader.frag" } + toolTip: qsTr("A material with customizable vertex and fragment shaders.") + } + } + Type { + name: "QtQuick3D.DefaultMaterial" + icon: "images/material16.png" + + Hints { + visibleInNavigator: false + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + } + + ItemLibraryEntry { + name: "Default Material" + category: "Materials" + libraryIcon: "images/material.png" + version: "6.0" + requiredImport: "QtQuick3D" + Property { name: "diffuseColor"; type: "color"; value: "#4aee45"; } + toolTip: qsTr("A material with a specular/glossiness properties.") + } + } + Type { + name: "QtQuick3D.PrincipledMaterial" + icon: "images/material16.png" + + Hints { + visibleInNavigator: false + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + } + + ItemLibraryEntry { + name: "Principled Material" + category: "Materials" + libraryIcon: "images/material.png" + version: "6.0" + requiredImport: "QtQuick3D" + Property { name: "baseColor"; type: "color"; value: "#4aee45"; } + toolTip: qsTr("A material with a PBR metal/roughness properties.") + } + } + Type { + name: "QtQuick3D.SpecularGlossyMaterial" + icon: "images/material16.png" + + Hints { + visibleInNavigator: false + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + } + + ItemLibraryEntry { + name: "Specular Glossy" + category: "Materials" + libraryIcon: "images/material.png" + version: "6.4" + requiredImport: "QtQuick3D" + Property { name: "albedoColor"; type: "color"; value: "#4aee45"; } + Property { name: "specularColor"; type: "color"; value: "#000000"; } + toolTip: qsTr("A material with a PBR specular/glossiness properties.") + } + } + Type { + name: "QtQuick3D.Texture" + icon: "images/texture16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeContainer: false + } + + ItemLibraryEntry { + name: "Texture" + category: "Textures" + libraryIcon: "images/texture.png" + version: "6.0" + requiredImport: "QtQuick3D" + toolTip: qsTr("Defines a texture for 3D objects.") + } + } + Type { + name: "QtQuick3D.CubeMapTexture" + icon: "images/cubemaptexture16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeContainer: false + } + + ItemLibraryEntry { + name: "Cube Map Texture" + category: "Textures" + libraryIcon: "images/cubemaptexture.png" + version: "6.4" + requiredImport: "QtQuick3D" + toolTip: qsTr("Defines a cube map texture for 3D objects.") + } + } + Type { + name: "QtQuick3D.DirectionalLight" + icon: "images/lightdirectional16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Directional Light" + category: "Lights" + libraryIcon: "images/lightdirectional.png" + version: "6.0" + requiredImport: "QtQuick3D" + toolTip: qsTr("A light similar to sunlight. It emits light in one direction from an infinitely far away source.") + } + } + Type { + name: "QtQuick3D.PointLight" + icon: "images/lightpoint16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Point Light" + category: "Lights" + libraryIcon: "images/lightpoint.png" + version: "6.0" + requiredImport: "QtQuick3D" + toolTip: qsTr("A light similar to a light bulb. It emits light equally in all directions from a central source.") + } + } + Type { + name: "QtQuick3D.SpotLight" + icon: "images/lightspot16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Spotlight" + category: "Lights" + libraryIcon: "images/lightspot.png" + version: "6.0" + requiredImport: "QtQuick3D" + toolTip: qsTr("A spotlight emits light in one direction in a cone shape.") + } + } + Type { + name: "QtQuick3D.Model" + icon: "images/model16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + visibleNonDefaultProperties: "materials" + } + + ItemLibraryEntry { + name: "Model" + category: "Components" + libraryIcon: "images/group.png" + version: "6.0" + requiredImport: "QtQuick3D" + toolTip: qsTr("Allows you to load 3D mesh data.") + } + } + Type { + name: "QtQuick3D.Model" + icon: "images/model16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + visibleNonDefaultProperties: "materials" + } + + ItemLibraryEntry { + name: "Cube" + category: "Primitives" + libraryIcon: "images/cube.png" + version: "6.0" + requiredImport: "QtQuick3D" + Property { name: "source"; type: "QUrl"; value: "#Cube"; } + toolTip: qsTr("A cube model.") + } + } + Type { + name: "QtQuick3D.Model" + icon: "images/model16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + visibleNonDefaultProperties: "materials" + } + + ItemLibraryEntry { + name: "Sphere" + category: "Primitives" + libraryIcon: "images/sphere.png" + version: "6.0" + requiredImport: "QtQuick3D" + Property { name: "source"; type: "QUrl"; value: "#Sphere"; } + toolTip: qsTr("A sphere model.") + } + } + Type { + name: "QtQuick3D.Model" + icon: "images/model16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + visibleNonDefaultProperties: "materials" + } + + ItemLibraryEntry { + name: "Cylinder" + category: "Primitives" + libraryIcon: "images/cylinder.png" + version: "6.0" + requiredImport: "QtQuick3D" + Property { name: "source"; type: "QUrl"; value: "#Cylinder"; } + toolTip: qsTr("A cylinder model.") + } + } + Type { + name: "QtQuick3D.Model" + icon: "images/model16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + visibleNonDefaultProperties: "materials" + } + + ItemLibraryEntry { + name: "Plane" + category: "Primitives" + libraryIcon: "images/plane.png" + version: "6.0" + requiredImport: "QtQuick3D" + Property { name: "source"; type: "QUrl"; value: "#Rectangle"; } + toolTip: qsTr("A plane model.") + } + } + Type { + name: "QtQuick3D.Model" + icon: "images/model16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + visibleNonDefaultProperties: "materials" + } + + ItemLibraryEntry { + name: "Cone" + category: "Primitives" + libraryIcon: "images/cone.png" + version: "6.0" + requiredImport: "QtQuick3D" + Property { name: "source"; type: "QUrl"; value: "#Cone"; } + toolTip: qsTr("A cone model.") + } + } + Type { + name: "QtQuick3D.Node" + icon: "images/group16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Group" + category: "Components" + libraryIcon: "images/group.png" + version: "6.0" + requiredImport: "QtQuick3D" + toolTip: qsTr("A container to keep several QtQuick3D components or scenes together.") + } + } + Type { + name: "QtQuick3D.SceneEnvironment" + icon: "images/scene16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + } + + ItemLibraryEntry { + name: "Scene Environment" + category: "Components" + libraryIcon: "images/scene.png" + version: "6.0" + requiredImport: "QtQuick3D" + toolTip: qsTr("Configures the render settings for a scene.") + } + } + Type { + name: "QtQuick3D.View3D" + icon: "images/view3D16.png" + + ItemLibraryEntry { + name: "View3D" + category: "Items" + libraryIcon: "images/view3D.png" + version: "6.0" + requiredImport: "QtQuick3D" + QmlSource { source: "./source/view3D_template.qml" } + toolTip: qsTr("A 2D surface where a 3D scene can be rendered.") + } + } + Type { + name: "QtQuick3D.Shader" + icon: "images/shaderutil16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + } + + ItemLibraryEntry { + name: "Shader" + category: "Custom Shader Utils" + libraryIcon: "images/shaderutil.png" + version: "6.0" + requiredImport: "QtQuick3D" + toolTip: qsTr("A container for keeping the vertex or fragment shader codes to be used by post-processing effect.") + } + } + Type { + name: "QtQuick3D.TextureInput" + icon: "images/shaderutil16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + } + + ItemLibraryEntry { + name: "Texture Input" + category: "Custom Shader Utils" + libraryIcon: "images/shaderutil.png" + version: "6.0" + requiredImport: "QtQuick3D" + toolTip: qsTr("Specifies a texture that gets exposed to the shader.") + } + } + Type { + name: "QtQuick3D.Pass" + icon: "images/shaderutil16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + } + + ItemLibraryEntry { + name: "Pass" + category: "Custom Shader Utils" + libraryIcon: "images/shaderutil.png" + version: "6.0" + requiredImport: "QtQuick3D" + toolTip: qsTr("Holds a set of actions combining a list of executable render commands, an output buffer, and a list of shaders to use for rendering effects.") + } + } + Type { + name: "QtQuick3D.BufferInput" + icon: "images/shadercommand16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + } + + ItemLibraryEntry { + name: "Buffer Input" + category: "Custom Shader Utils" + libraryIcon: "images/shadercommand.png" + version: "6.0" + requiredImport: "QtQuick3D" + toolTip: qsTr("A command that gets added to the list of commands in the Pass of an Effect when executed.") + } + } + Type { + name: "QtQuick3D.Buffer" + icon: "images/shaderutil16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + } + + ItemLibraryEntry { + name: "Buffer" + category: "Custom Shader Utils" + libraryIcon: "images/shaderutil.png" + version: "6.0" + requiredImport: "QtQuick3D" + toolTip: qsTr("Creates or references a color buffer to be used for a pass of an Effect.") + } + } + Type { + name: "QtQuick3D.SetUniformValue" + icon: "images/shadercommand16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + } + + ItemLibraryEntry { + name: "Set Uniform Value" + category: "Custom Shader Utils" + libraryIcon: "images/shadercommand.png" + version: "6.0" + requiredImport: "QtQuick3D" + toolTip: qsTr("A value that would be set when a single pass actions takes place.") + } + } + Type { + name: "QtQuick3D.Effect" + icon: "images/effect16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + } + + ItemLibraryEntry { + name: "Effect" + category: "Components" + libraryIcon: "images/effect.png" + version: "6.0" + requiredImport: "QtQuick3D" + QmlSource { source: "./source/effect_template.qml" } + ExtraFile { source: "./source/effect_default_shader.frag" } + toolTip: qsTr("A method to allow the user to implement their post-processing effects on entire View3D.") + } + } + Type { + name: "QtQuick3D.Repeater3D" + icon: "images/repeater3d16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "3D Repeater" + category: "Components" + libraryIcon: "images/repeater3d.png" + version: "6.0" + requiredImport: "QtQuick3D" + toolTip: qsTr("Dynamically creates several copies of the same 3D object.") + } + } + Type { + name: "QtQuick3D.Loader3D" + icon: "images/loader3d16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Loader3D" + category: "Components" + libraryIcon: "images/loader3d.png" + version: "6.0" + requiredImport: "QtQuick3D" + toolTip: qsTr("Allows you to load 3D components dynamically.") + } + } + Type { + name: "QtQuick3D.Skeleton" + icon: "images/skeleton16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + } + + ItemLibraryEntry { + name: "Skeleton" + category: "Components" + libraryIcon: "images/skeleton.png" + version: "6.0" + requiredImport: "QtQuick3D" + toolTip: qsTr("Defines a skeletal animation hierarchy.") + } + } + Type { + name: "QtQuick3D.MorphTarget" + icon: "images/morphtarget16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + } + + ItemLibraryEntry { + name: "Morph Target" + category: "Components" + libraryIcon: "images/morphtarget.png" + version: "6.0" + requiredImport: "QtQuick3D" + toolTip: qsTr("Defines the properties of a morph target.") + } + } + Type { + name: "QtQuick3D.InstanceListEntry" + icon: "images/instancelistentry16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + } + + ItemLibraryEntry { + name: "Instance List Entry" + category: "Components" + libraryIcon: "images/instancelistentry.png" + version: "6.2" + requiredImport: "QtQuick3D" + toolTip: qsTr("One instance in an Instance List. The instance includes a set of property specifications.") + } + } + Type { + name: "QtQuick3D.InstanceList" + icon: "images/instancelist16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + } + + ItemLibraryEntry { + name: "Instance List" + category: "Components" + libraryIcon: "images/instancelist.png" + version: "6.2" + requiredImport: "QtQuick3D" + toolTip: qsTr("Enables 3D model instancing, a lightweight 3D object replication method.") + } + } + Type { + name: "QtQuick3D.FileInstancing" + icon: "images/fileinstancing16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + } + + ItemLibraryEntry { + name: "File Instancing" + category: "Components" + libraryIcon: "images/fileinstancing.png" + version: "6.2" + requiredImport: "QtQuick3D" + toolTip: qsTr("A method that allows reading instance tables from XML or Qt-specific binary files.") + } + } + Type { + name: "QtQuick3D.Joint" + icon: "images/joint16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + } + + ItemLibraryEntry { + name: "Joint" + category: "Components" + libraryIcon: "images/joint.png" + version: "6.0" + requiredImport: "QtQuick3D" + toolTip: qsTr("A transformable node that connects different parts in a skeletal animation.") + } + } + Type { + name: "QtQuick3D.ReflectionProbe" + icon: "images/reflectionProbe16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: true + } + + ItemLibraryEntry { + name: "Reflection Probe" + category: "Components" + libraryIcon: "images/reflectionProbe.png" + version: "6.3" + requiredImport: "QtQuick3D" + toolTip: qsTr("Reflects the current scene to the objects.") + } + } + Type { + name: "QtQuick3D.Fog" + icon: "images/fog16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: false + } + + ItemLibraryEntry { + name: "Fog" + category: "Components" + libraryIcon: "images/fog.png" + version: "6.5" + requiredImport: "QtQuick3D" + } + } + Type { + name: "QtQuick3D.DebugSettings" + icon: "images/debugsettings16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: false + } + + ItemLibraryEntry { + name: "Debug Settings" + category: "Components" + libraryIcon: "images/debugsettings.png" + version: "6.5" + requiredImport: "QtQuick3D" + } + } + + Type { + name: "QtQuick3D.Lightmapper" + icon: "images/lightmapper16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: false + } + + ItemLibraryEntry { + // Split the name to avoid ellipsis in UI + name: "Light Mapper" + category: "Components" + libraryIcon: "images/lightmapper.png" + version: "6.5" + requiredImport: "QtQuick3D" + } + } + + Type { + name: "QtQuick3D.Skin" + icon: "images/skin16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: false + } + + ItemLibraryEntry { + name: "Skin" + category: "Components" + libraryIcon: "images/skin.png" + version: "6.5" + requiredImport: "QtQuick3D" + } + } + + Type { + name: "QtQuick3D.ResourceLoader" + icon: "images/resourceLoader16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: false + } + + ItemLibraryEntry { + name: "Resource Loader" + category: "Components" + libraryIcon: "images/resourceLoader.png" + version: "6.2" + requiredImport: "QtQuick3D" + toolTip: qsTr("Pre-load resources for 3D scene. It makes sure that large resources are available before rendering a frame.") + } + } +} diff --git a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp index 125e8df3aeb..d16e007cc03 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp @@ -996,10 +996,10 @@ protected: package.updatedSourceIds = {sourceId1, sourceId2, sourceId3}; - package.propertyEditorQmlPaths.emplace_back(qtQuickModuleId, "QtObject", sourceId1, sourceIdPath); - package.propertyEditorQmlPaths.emplace_back(qtQuickModuleId, "Item", sourceId2, sourceIdPath); - package.propertyEditorQmlPaths.emplace_back(qtQuickModuleId, "Item3D", sourceId3, sourceIdPath); - package.updatedPropertyEditorQmlPathSourceIds.emplace_back(sourceIdPath); + package.propertyEditorQmlPaths.emplace_back(qtQuickModuleId, "QtObject", sourceId1, sourceIdPath6); + package.propertyEditorQmlPaths.emplace_back(qtQuickModuleId, "Item", sourceId2, sourceIdPath6); + package.propertyEditorQmlPaths.emplace_back(qtQuickModuleId, "Item3D", sourceId3, sourceIdPath6); + package.updatedPropertyEditorQmlPathSourceIds.emplace_back(sourceIdPath6); return package; } @@ -1013,6 +1013,7 @@ protected: traits.visibleInLibrary = FlagIs::True; annotations.emplace_back(sourceId4, + sourceIdPath6, "Object", qmlModuleId, "/path/to/icon.png", @@ -1034,6 +1035,32 @@ protected: "properties":[["color", "color", "#blue"]]}])xy"); annotations.emplace_back(sourceId5, + sourceIdPath6, + "Item", + qtQuickModuleId, + "/path/to/quick.png", + traits, + R"xy({"canBeContainer": "true", "forceClip": "false"})xy", + R"xy([{"name":"Item", + "iconPath":"/path/icon3", + "category":"Advanced Items", + "import":"QtQuick", + "toolTip":"Item is an Object", + "properties":[["x", "double", 1], ["y", "double", 2]]}])xy"); + + return annotations; + } + + auto createExtendedTypeAnnotations() const + { + auto annotations = createTypeAnnotions(); + annotations.pop_back(); + TypeTraits traits{TypeTraitsKind::Reference}; + traits.canBeContainer = FlagIs::True; + traits.visibleInLibrary = FlagIs::True; + + annotations.emplace_back(sourceId5, + sourceIdPath1, "Item", qtQuickModuleId, "/path/to/quick.png", @@ -1109,7 +1136,6 @@ protected: protected: inline static std::unique_ptr static_database; Sqlite::Database &database = *static_database; - //Sqlite::Database database{"/tmp/aaaaa.db", Sqlite::JournalMode::Wal}; inline static std::unique_ptr> static_projectStorage; QmlDesigner::ProjectStorage &storage = *static_projectStorage; QmlDesigner::SourcePathCache> sourcePathCache{ @@ -1120,14 +1146,16 @@ protected: QmlDesigner::SourcePathView path4{"/path4/to"}; QmlDesigner::SourcePathView path5{"/path5/to"}; QmlDesigner::SourcePathView path6{"/path6/to"}; - QmlDesigner::SourcePathView pathPath{"/path6/."}; + QmlDesigner::SourcePathView pathPath1{"/path1/."}; + QmlDesigner::SourcePathView pathPath6{"/path6/."}; SourceId sourceId1{sourcePathCache.sourceId(path1)}; SourceId sourceId2{sourcePathCache.sourceId(path2)}; SourceId sourceId3{sourcePathCache.sourceId(path3)}; SourceId sourceId4{sourcePathCache.sourceId(path4)}; SourceId sourceId5{sourcePathCache.sourceId(path5)}; SourceId sourceId6{sourcePathCache.sourceId(path6)}; - SourceId sourceIdPath{sourcePathCache.sourceId(path6)}; + SourceId sourceIdPath1{sourcePathCache.sourceId(pathPath1)}; + SourceId sourceIdPath6{sourcePathCache.sourceId(pathPath6)}; SourceId qmlProjectSourceId{sourcePathCache.sourceId("/path1/qmldir")}; SourceId qtQuickProjectSourceId{sourcePathCache.sourceId("/path2/qmldir")}; ModuleId qmlModuleId{storage.moduleId("Qml")}; @@ -7276,7 +7304,7 @@ TEST_F(ProjectStorage, synchronize_property_editor_adds_path) auto package{createPropertyEditorPathsSynchronizationPackage()}; package.propertyEditorQmlPaths.pop_back(); storage.synchronize(package); - package.propertyEditorQmlPaths.emplace_back(qtQuickModuleId, "Item3D", sourceId3, sourceIdPath); + package.propertyEditorQmlPaths.emplace_back(qtQuickModuleId, "Item3D", sourceId3, sourceIdPath6); storage.synchronize(package); @@ -7288,7 +7316,7 @@ TEST_F(ProjectStorage, synchronize_property_editor_adds_path) TEST_F(ProjectStorage, synchronize_property_editor_with_non_existing_type_name) { auto package{createPropertyEditorPathsSynchronizationPackage()}; - package.propertyEditorQmlPaths.emplace_back(qtQuickModuleId, "Item4D", sourceId4, sourceIdPath); + package.propertyEditorQmlPaths.emplace_back(qtQuickModuleId, "Item4D", sourceId4, sourceIdPath6); storage.synchronize(package); @@ -7308,6 +7336,21 @@ TEST_F(ProjectStorage, call_remove_type_ids_in_observer_after_synchronization) storage.synchronize(package); } +TEST_F(ProjectStorage, do_not_synchronize_type_annotations_without_type) +{ + SynchronizationPackage package; + package.typeAnnotations = createTypeAnnotions(); + package.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds( + package.typeAnnotations); + TypeTraits traits{TypeTraitsKind::Reference}; + traits.canBeContainer = FlagIs::True; + traits.visibleInLibrary = FlagIs::True; + + storage.synchronize(package); + + ASSERT_THAT(storage.allItemLibraryEntries(), IsEmpty()); +} + TEST_F(ProjectStorage, synchronize_type_annotation_type_traits) { auto package{createSimpleSynchronizationPackage()}; @@ -7460,6 +7503,18 @@ TEST_F(ProjectStorage, synchronize_removes_type_hints) } TEST_F(ProjectStorage, return_empty_type_hints_if_no_type_hints_exists) +{ + auto package{createSimpleSynchronizationPackage()}; + package.typeAnnotations = createTypeAnnotions(); + package.typeAnnotations[0].hintsJson.clear(); + storage.synchronize(package); + + auto typeHints = storage.typeHints(fetchTypeId(sourceId2, "QObject")); + + ASSERT_THAT(typeHints, IsEmpty()); +} + +TEST_F(ProjectStorage, return_empty_type_hints_if_no_type_annotaion_exists) { auto package{createSimpleSynchronizationPackage()}; storage.synchronize(package); @@ -7534,7 +7589,7 @@ TEST_F(ProjectStorage, synchronize_removes_item_library_entries) ASSERT_THAT(storage.allItemLibraryEntries(), IsEmpty()); } -TEST_F(ProjectStorage, synchronize_udpates_item_library_entries) +TEST_F(ProjectStorage, synchronize_updates_item_library_entries) { auto package{createSimpleSynchronizationPackage()}; package.typeAnnotations = createTypeAnnotions(); @@ -7560,6 +7615,59 @@ TEST_F(ProjectStorage, synchronize_udpates_item_library_entries) IsEmpty()))); } +TEST_F(ProjectStorage, synchronize_updates_item_library_entries_with_empty_entries) +{ + auto package{createSimpleSynchronizationPackage()}; + package.typeAnnotations = createTypeAnnotions(); + package.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds( + package.typeAnnotations); + storage.synchronize(package); + package.typeAnnotations[0].itemLibraryJson.clear(); + + storage.synchronize(package); + + ASSERT_THAT(storage.itemLibraryEntries(fetchTypeId(sourceId2, "QObject")), IsEmpty()); +} + +TEST_F(ProjectStorage, synchronize_type_annotation_directory_source_id) +{ + auto package{createSimpleSynchronizationPackage()}; + package.typeAnnotations = createTypeAnnotions(); + package.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds( + package.typeAnnotations); + + storage.synchronize(package); + + ASSERT_THAT(storage.typeAnnotationSourceIds(sourceIdPath6), + UnorderedElementsAre(sourceId4, sourceId5)); +} + +TEST_F(ProjectStorage, get_type_annotation_source_ids) +{ + auto package{createSimpleSynchronizationPackage()}; + package.typeAnnotations = createTypeAnnotions(); + package.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds( + package.typeAnnotations); + storage.synchronize(package); + + auto sourceIds = storage.typeAnnotationSourceIds(sourceIdPath6); + + ASSERT_THAT(sourceIds, UnorderedElementsAre(sourceId4, sourceId5)); +} + +TEST_F(ProjectStorage, get_type_annotation_directory_source_ids) +{ + auto package{createSimpleSynchronizationPackage()}; + package.typeAnnotations = createExtendedTypeAnnotations(); + package.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds( + package.typeAnnotations); + storage.synchronize(package); + + auto sourceIds = storage.typeAnnotationDirectorySourceIds(); + + ASSERT_THAT(sourceIds, ElementsAre(sourceIdPath1, sourceIdPath6)); +} + TEST_F(ProjectStorage, get_all_item_library_entries) { auto package{createSimpleSynchronizationPackage()}; @@ -7605,6 +7713,31 @@ TEST_F(ProjectStorage, get_all_item_library_entries) IsEmpty()))); } +TEST_F(ProjectStorage, get_all_item_library_entries_handles_no_entries) +{ + auto package{createSimpleSynchronizationPackage()}; + package.typeAnnotations = createTypeAnnotions(); + package.typeAnnotations[0].itemLibraryJson.clear(); + package.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds( + package.typeAnnotations); + storage.synchronize(package); + + auto entries = storage.allItemLibraryEntries(); + + ASSERT_THAT(entries, + UnorderedElementsAre( + IsItemLibraryEntry(fetchTypeId(sourceId1, "QQuickItem"), + "Item", + "/path/icon3", + "Advanced Items", + "QtQuick", + "Item is an Object", + "", + UnorderedElementsAre(IsItemLibraryProperty("x", "double", 1), + IsItemLibraryProperty("y", "double", 2)), + IsEmpty()))); +} + TEST_F(ProjectStorage, get_item_library_entries_by_type_id) { auto package{createSimpleSynchronizationPackage()}; @@ -7654,6 +7787,21 @@ TEST_F(ProjectStorage, get_no_item_library_entries_if_type_id_is_invalid) ASSERT_THAT(entries, IsEmpty()); } +TEST_F(ProjectStorage, get_no_item_library_entries_by_type_id_for_no_entries) +{ + auto package{createSimpleSynchronizationPackage()}; + package.typeAnnotations = createTypeAnnotions(); + package.typeAnnotations[0].itemLibraryJson.clear(); + package.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds( + package.typeAnnotations); + storage.synchronize(package); + auto typeId = fetchTypeId(sourceId2, "QObject"); + + auto entries = storage.itemLibraryEntries(typeId); + + ASSERT_THAT(entries, IsEmpty()); +} + TEST_F(ProjectStorage, get_item_library_entries_by_source_id) { auto package{createSimpleSynchronizationPackage()}; @@ -7689,6 +7837,20 @@ TEST_F(ProjectStorage, get_item_library_entries_by_source_id) IsEmpty()))); } +TEST_F(ProjectStorage, get_no_item_library_entries_by_source_id_for_no_entries) +{ + auto package{createSimpleSynchronizationPackage()}; + package.typeAnnotations = createTypeAnnotions(); + package.typeAnnotations[0].itemLibraryJson.clear(); + package.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds( + package.typeAnnotations); + storage.synchronize(package); + + auto entries = storage.itemLibraryEntries(sourceId2); + + ASSERT_THAT(entries, IsEmpty()); +} + TEST_F(ProjectStorage, return_type_ids_for_module_id) { auto package{createBuiltinSynchronizationPackage()}; diff --git a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp index a28c5e4935d..a0ca9012629 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp @@ -9,6 +9,8 @@ #include "../mocks/qmldocumentparsermock.h" #include "../mocks/qmltypesparsermock.h" +#include + #include #include #include @@ -120,7 +122,8 @@ MATCHER(PackageIsEmpty, std::string(negation ? "isn't empty" : "is empty")) && package.moduleDependencies.empty() && package.updatedModuleDependencySourceIds.empty() && package.moduleExportedImports.empty() && package.updatedModuleIds.empty() && package.propertyEditorQmlPaths.empty() - && package.updatedPropertyEditorQmlPathSourceIds.empty(); + && package.updatedPropertyEditorQmlPathSourceIds.empty() + && package.typeAnnotations.empty() && package.updatedTypeAnnotationSourceIds.empty(); } template @@ -168,7 +171,8 @@ public: secondSourceId, thirdSourceId, qmltypes1SourceId, - qmltypes2SourceId}); + qmltypes2SourceId, + itemLibraryPathSourceId}); setFilesAdded({qmldir1SourceId, qmldir2SourceId, qmldir3SourceId}); @@ -298,6 +302,8 @@ public: EXPECT_CALL(fileSystemMock, contentAsQString(Eq(path))).WillRepeatedly(Return(content)); } + auto moduleId(Utils::SmallStringView name) const { return storage.moduleId(name); } + protected: NiceMock fileSystemMock; NiceMock projectStorageMock; @@ -328,6 +334,13 @@ protected: SourceId qmlDocumentSourceId1 = sourcePathCache.sourceId("/path/First.qml"); SourceId qmlDocumentSourceId2 = sourcePathCache.sourceId("/path/First2.qml"); SourceId qmlDocumentSourceId3 = sourcePathCache.sourceId("/path/Second.qml"); + const QString itemLibraryPath = QDir::cleanPath( + UNITTEST_DIR "/../../../../share/qtcreator/qmldesigner/itemLibrary/"); + const QString qmlImportsPath = QDir::cleanPath(UNITTEST_DIR "/projectstorage/data/qml"); + SourceId itemLibraryPathSourceId = sourcePathCache.sourceId( + QmlDesigner::SourcePath{itemLibraryPath + "/."}); + SourceId qmlImportsPathSourceId = sourcePathCache.sourceId( + QmlDesigner::SourcePath{qmlImportsPath + "/."}); ModuleId qmlModuleId{storage.moduleId("Qml")}; ModuleId qmlCppNativeModuleId{storage.moduleId("Qml-cppnative")}; ModuleId exampleModuleId{storage.moduleId("Example")}; @@ -410,7 +423,7 @@ TEST_F(ProjectStorageUpdater, get_content_for_qml_dir_paths_if_file_status_is_di EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/one/qmldir")))); EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/two/qmldir")))); - updater.update(directories, {}, {}); + updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, request_file_status_from_file_system) @@ -419,7 +432,7 @@ TEST_F(ProjectStorageUpdater, request_file_status_from_file_system) EXPECT_CALL(fileSystemMock, fileStatus(Eq(directoryPathSourceId))); - updater.update(directories, {}, {}); + updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, get_content_for_qml_types) @@ -431,7 +444,7 @@ TEST_F(ProjectStorageUpdater, get_content_for_qml_types) EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/example.qmltypes")))); - updater.update(directories, {}, {}); + updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, get_content_for_qml_types_if_project_storage_file_status_is_invalid) @@ -444,7 +457,7 @@ TEST_F(ProjectStorageUpdater, get_content_for_qml_types_if_project_storage_file_ EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/example.qmltypes")))); - updater.update(directories, {}, {}); + updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, parse_qml_types) @@ -463,7 +476,7 @@ TEST_F(ProjectStorageUpdater, parse_qml_types) EXPECT_CALL(qmlTypesParserMock, parse(qmltypes2, _, _, Field(&ProjectData::moduleId, exampleCppNativeModuleId))); - updater.update(directories, {}, {}); + updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_is_empty_for_no_change) @@ -472,7 +485,7 @@ TEST_F(ProjectStorageUpdater, synchronize_is_empty_for_no_change) EXPECT_CALL(projectStorageMock, synchronize(PackageIsEmpty())); - updater.update(directories, {}, {}); + updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_qml_types) @@ -507,7 +520,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_types) Field(&SynchronizationPackage::updatedProjectSourceIds, UnorderedElementsAre(directoryPathSourceId))))); - updater.update(directories, {}, {}); + updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_qml_types_throws_if_qmltpes_does_not_exists) @@ -515,7 +528,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_types_throws_if_qmltpes_does_not_e Storage::Import import{qmlModuleId, Storage::Version{2, 3}, qmltypesPathSourceId}; setFilesDontExists({qmltypesPathSourceId}); - ASSERT_THROW(updater.update(directories, {}, {}), QmlDesigner::CannotParseQmlTypesFile); + ASSERT_THROW(updater.update(directories, {}, {}, {}), QmlDesigner::CannotParseQmlTypesFile); } TEST_F(ProjectStorageUpdater, synchronize_qml_types_are_empty_if_file_does_not_changed) @@ -528,7 +541,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_types_are_empty_if_file_does_not_c EXPECT_CALL(projectStorageMock, synchronize(PackageIsEmpty())); - updater.update(directories, {}, {}); + updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, get_content_for_qml_documents) @@ -549,7 +562,7 @@ TEST_F(ProjectStorageUpdater, get_content_for_qml_documents) EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/OldSecond.qml")))); EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/Second.qml")))); - updater.update(directories, {}, {}); + updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, parse_qml_documents) @@ -570,7 +583,7 @@ TEST_F(ProjectStorageUpdater, parse_qml_documents) EXPECT_CALL(qmlDocumentParserMock, parse(qmlDocument2, _, _, _)); EXPECT_CALL(qmlDocumentParserMock, parse(qmlDocument3, _, _, _)); - updater.update(directories, {}, {}); + updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, parse_qml_documents_with_non_existing_qml_document_throws) @@ -579,7 +592,7 @@ TEST_F(ProjectStorageUpdater, parse_qml_documents_with_non_existing_qml_document NonexitingType 1.0 NonexitingType.qml)"}; setContent(u"/path/qmldir", qmldir); - ASSERT_THROW(updater.update(directories, {}, {}), QmlDesigner::CannotParseQmlDocumentFile); + ASSERT_THROW(updater.update(directories, {}, {}, {}), QmlDesigner::CannotParseQmlDocumentFile); } TEST_F(ProjectStorageUpdater, synchronize_qml_documents) @@ -652,7 +665,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents) ModuleId{}, FileType::QmlDocument)))))); - updater.update(directories, {}, {}); + updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_add_only_qml_document_in_directory) @@ -708,7 +721,7 @@ TEST_F(ProjectStorageUpdater, synchronize_add_only_qml_document_in_directory) ModuleId{}, FileType::QmlDocument)))))); - updater.update(directories, {}, {}); + updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_removes_qml_document) @@ -771,7 +784,7 @@ TEST_F(ProjectStorageUpdater, synchronize_removes_qml_document) ModuleId{}, FileType::QmlDocument)))))); - updater.update(directories, {}, {}); + updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_removes_qml_document_in_qmldir_only) @@ -826,7 +839,7 @@ TEST_F(ProjectStorageUpdater, synchronize_removes_qml_document_in_qmldir_only) ModuleId{}, FileType::QmlDocument)))))); - updater.update(directories, {}, {}); + updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_add_qml_document_to_qmldir) @@ -884,7 +897,7 @@ TEST_F(ProjectStorageUpdater, synchronize_add_qml_document_to_qmldir) ModuleId{}, FileType::QmlDocument)))))); - updater.update(directories, {}, {}); + updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_remove_qml_document_from_qmldir) @@ -939,7 +952,7 @@ TEST_F(ProjectStorageUpdater, synchronize_remove_qml_document_from_qmldir) ModuleId{}, FileType::QmlDocument)))))); - updater.update(directories, {}, {}); + updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_qml_documents_dont_update_if_up_to_date) @@ -1009,7 +1022,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_dont_update_if_up_to_dat ModuleId{}, FileType::QmlDocument)))))); - updater.update(directories, {}, {}); + updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_has_not_changed) @@ -1060,7 +1073,7 @@ TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_has_not_changed) qmlDocumentSourceId2)), Field(&SynchronizationPackage::projectDatas, IsEmpty())))); - updater.update(directories, {}, {}); + updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_has_not_changed_and_some_updated_files) @@ -1095,7 +1108,7 @@ TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_has_not_changed_and_some UnorderedElementsAre(qmltypesPathSourceId, qmlDocumentSourceId1)), Field(&SynchronizationPackage::projectDatas, IsEmpty())))); - updater.update(directories, {}, {}); + updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_not_changed_and_some_removed_files) @@ -1110,7 +1123,7 @@ TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_not_changed_and_some_rem setFilesDontChanged({qmlDirPathSourceId, qmltypes2PathSourceId, qmlDocumentSourceId2}); setFilesRemoved({qmltypesPathSourceId, qmlDocumentSourceId1}); - ASSERT_THROW(updater.update(directories, {}, {}), QmlDesigner::CannotParseQmlTypesFile); + ASSERT_THROW(updater.update(directories, {}, {}, {}), QmlDesigner::CannotParseQmlTypesFile); } TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_has_changed_and_some_removed_files) @@ -1162,7 +1175,7 @@ TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_has_changed_and_some_rem exampleCppNativeModuleId, FileType::QmlTypes)))))); - updater.update(directories, {}, {}); + updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, update_qml_types_files_is_empty) @@ -1177,7 +1190,7 @@ TEST_F(ProjectStorageUpdater, update_qml_types_files_is_empty) Field(&SynchronizationPackage::projectDatas, IsEmpty()), Field(&SynchronizationPackage::updatedProjectSourceIds, IsEmpty())))); - updater.update({}, {}, {}); + updater.update({}, {}, {}, {}); } TEST_F(ProjectStorageUpdater, update_qml_types_files) @@ -1205,7 +1218,7 @@ TEST_F(ProjectStorageUpdater, update_qml_types_files) Field(&SynchronizationPackage::updatedProjectSourceIds, UnorderedElementsAre(qmltypesPathSourceId, qmltypes2PathSourceId))))); - updater.update({}, {"/path/example.qmltypes", "/path/example2.qmltypes"}, {}); + updater.update({}, {"/path/example.qmltypes", "/path/example2.qmltypes"}, {}, {}); } TEST_F(ProjectStorageUpdater, dont_update_qml_types_files_if_unchanged) @@ -1230,7 +1243,7 @@ TEST_F(ProjectStorageUpdater, dont_update_qml_types_files_if_unchanged) Field(&SynchronizationPackage::updatedProjectSourceIds, UnorderedElementsAre(qmltypesPathSourceId))))); - updater.update({}, {"/path/example.qmltypes", "/path/example2.qmltypes"}, {}); + updater.update({}, {"/path/example.qmltypes", "/path/example2.qmltypes"}, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_qml_documents_with_different_version_but_same_type_name_and_file_name) @@ -1273,7 +1286,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_with_different_version_b ModuleId{}, FileType::QmlDocument)))))); - updater.update(directories, {}, {}); + updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_qml_documents_with_different_type_name_but_same_version_and_file_name) @@ -1314,7 +1327,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_with_different_type_name ModuleId{}, FileType::QmlDocument)))))); - updater.update(directories, {}, {}); + updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, dont_synchronize_selectors) @@ -1332,7 +1345,7 @@ TEST_F(ProjectStorageUpdater, dont_synchronize_selectors) Contains(Field(&Storage::Synchronization::Type::exportedTypes, Contains(IsExportedType(exampleModuleId, "FirstType", 1, 0)))))))); - updater.update(directories, {}, {}); + updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_qmldir_dependencies) @@ -1357,7 +1370,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qmldir_dependencies) Field(&SynchronizationPackage::updatedModuleDependencySourceIds, UnorderedElementsAre(qmltypesPathSourceId, qmltypes2PathSourceId))))); - updater.update(directories, {}, {}); + updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_qmldir_dependencies_with_double_entries) @@ -1383,7 +1396,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qmldir_dependencies_with_double_entrie Field(&SynchronizationPackage::updatedModuleDependencySourceIds, UnorderedElementsAre(qmltypesPathSourceId, qmltypes2PathSourceId))))); - updater.update(directories, {}, {}); + updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_qmldir_dependencies_with_colliding_imports) @@ -1409,7 +1422,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qmldir_dependencies_with_colliding_imp Field(&SynchronizationPackage::updatedModuleDependencySourceIds, UnorderedElementsAre(qmltypesPathSourceId, qmltypes2PathSourceId))))); - updater.update(directories, {}, {}); + updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_qmldir_with_no_dependencies) @@ -1426,7 +1439,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qmldir_with_no_dependencies) Field(&SynchronizationPackage::updatedModuleDependencySourceIds, UnorderedElementsAre(qmltypesPathSourceId, qmltypes2PathSourceId))))); - updater.update(directories, {}, {}); + updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_qmldir_imports) @@ -1468,7 +1481,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qmldir_imports) Field(&SynchronizationPackage::updatedModuleIds, ElementsAre(exampleModuleId))))); - updater.update(directories, {}, {}); + updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_qmldir_with_no_imports) @@ -1482,7 +1495,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qmldir_with_no_imports) Field(&SynchronizationPackage::updatedModuleIds, ElementsAre(exampleModuleId))))); - updater.update(directories, {}, {}); + updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_qmldir_imports_with_double_entries) @@ -1525,7 +1538,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qmldir_imports_with_double_entries) Field(&SynchronizationPackage::updatedModuleIds, ElementsAre(exampleModuleId))))); - updater.update(directories, {}, {}); + updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_qmldir_optional_imports) @@ -1567,7 +1580,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qmldir_optional_imports) Field(&SynchronizationPackage::updatedModuleIds, ElementsAre(exampleModuleId))))); - updater.update(directories, {}, {}); + updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, update_path_watcher_directories) @@ -1577,7 +1590,7 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_directories) QmlDesigner::SourceType::Directory, {path1SourceId, path2SourceId, path3SourceId}}))); - updater.update(directories3, {}, {}); + updater.update(directories3, {}, {}, {}); } TEST_F(ProjectStorageUpdater, update_path_watcher_directory_does_not_exists) @@ -1589,7 +1602,7 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_directory_does_not_exists) QmlDesigner::SourceType::Directory, {path1SourceId, path3SourceId}}))); - updater.update(directories3, {}, {}); + updater.update(directories3, {}, {}, {}); } TEST_F(ProjectStorageUpdater, update_path_watcher_directory_does_not_changed) @@ -1601,7 +1614,7 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_directory_does_not_changed) QmlDesigner::SourceType::Directory, {path1SourceId, path2SourceId}}))); - updater.update(directories2, {}, {}); + updater.update(directories2, {}, {}, {}); } TEST_F(ProjectStorageUpdater, update_path_watcher_directory_removed) @@ -1612,7 +1625,7 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_directory_removed) updateIdPaths(Contains( IdPaths{projectPartId, QmlDesigner::SourceType::Directory, {path2SourceId}}))); - updater.update(directories2, {}, {}); + updater.update(directories2, {}, {}, {}); } TEST_F(ProjectStorageUpdater, update_path_watcher_qmldirs) @@ -1622,7 +1635,7 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_qmldirs) QmlDesigner::SourceType::QmlDir, {qmldir1SourceId, qmldir2SourceId, qmldir3SourceId}}))); - updater.update(directories3, {}, {}); + updater.update(directories3, {}, {}, {}); } TEST_F(ProjectStorageUpdater, update_path_watcher_qmldir_does_not_exists) @@ -1634,7 +1647,7 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_qmldir_does_not_exists) QmlDesigner::SourceType::QmlDir, {qmldir1SourceId, qmldir3SourceId}}))); - updater.update(directories3, {}, {}); + updater.update(directories3, {}, {}, {}); } TEST_F(ProjectStorageUpdater, update_path_watcher_qmldir_does_not_changed) @@ -1646,7 +1659,7 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_qmldir_does_not_changed) QmlDesigner::SourceType::QmlDir, {qmldir1SourceId, qmldir2SourceId}}))); - updater.update(directories2, {}, {}); + updater.update(directories2, {}, {}, {}); } TEST_F(ProjectStorageUpdater, update_path_watcher_qmldir_removed) @@ -1657,7 +1670,7 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_qmldir_removed) updateIdPaths(Contains( IdPaths{projectPartId, QmlDesigner::SourceType::QmlDir, {qmldir2SourceId}}))); - updater.update(directories2, {}, {}); + updater.update(directories2, {}, {}, {}); } TEST_F(ProjectStorageUpdater, update_path_watcher_qml_files) @@ -1674,7 +1687,7 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_qml_files) QmlDesigner::SourceType::Qml, {firstSourceId, secondSourceId, thirdSourceId}}))); - updater.update(directories2, {}, {}); + updater.update(directories2, {}, {}, {}); } TEST_F(ProjectStorageUpdater, update_path_watcher_only_qml_files_dont_changed) @@ -1692,7 +1705,7 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_only_qml_files_dont_changed) QmlDesigner::SourceType::Qml, {firstSourceId, secondSourceId, thirdSourceId}}))); - updater.update(directories2, {}, {}); + updater.update(directories2, {}, {}, {}); } TEST_F(ProjectStorageUpdater, update_path_watcher_only_qml_files_changed) @@ -1710,7 +1723,7 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_only_qml_files_changed) QmlDesigner::SourceType::Qml, {firstSourceId, secondSourceId, thirdSourceId}}))); - updater.update(directories2, {}, {}); + updater.update(directories2, {}, {}, {}); } TEST_F(ProjectStorageUpdater, update_path_watcher_qml_files_and_directories_dont_changed) @@ -1733,7 +1746,7 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_qml_files_and_directories_dont QmlDesigner::SourceType::Qml, {firstSourceId, secondSourceId, thirdSourceId}}))); - updater.update(directories2, {}, {}); + updater.update(directories2, {}, {}, {}); } TEST_F(ProjectStorageUpdater, update_path_watcher_qmltypes_files_in_qmldir) @@ -1752,7 +1765,7 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_qmltypes_files_in_qmldir) QmlDesigner::SourceType::QmlTypes, {qmltypes1SourceId, qmltypes2SourceId}}))); - updater.update(directories2, {}, {}); + updater.update(directories2, {}, {}, {}); } TEST_F(ProjectStorageUpdater, update_path_watcher_only_qmltypes_files_in_qmldir_dont_changed) @@ -1770,7 +1783,7 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_only_qmltypes_files_in_qmldir_ QmlDesigner::SourceType::QmlTypes, {qmltypes1SourceId, qmltypes2SourceId}}))); - updater.update(directories2, {}, {}); + updater.update(directories2, {}, {}, {}); } TEST_F(ProjectStorageUpdater, update_path_watcher_only_qmltypes_files_changed) @@ -1787,7 +1800,7 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_only_qmltypes_files_changed) QmlDesigner::SourceType::QmlTypes, {qmltypes1SourceId, qmltypes2SourceId}}))); - updater.update(directories2, {}, {}); + updater.update(directories2, {}, {}, {}); } TEST_F(ProjectStorageUpdater, update_path_watcher_qmltypes_files_and_directories_dont_changed) @@ -1808,7 +1821,7 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_qmltypes_files_and_directories QmlDesigner::SourceType::QmlTypes, {qmltypes1SourceId, qmltypes2SourceId}}))); - updater.update(directories2, {}, {}); + updater.update(directories2, {}, {}, {}); } TEST_F(ProjectStorageUpdater, update_path_watcher_builtin_qmltypes_files) @@ -1823,7 +1836,7 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_builtin_qmltypes_files) QmlDesigner::SourceType::QmlTypes, {qmltypes1SourceId, qmltypes2SourceId}}))); - updater.update({}, {builtinQmltyplesPath1, builtinQmltyplesPath2}, {}); + updater.update({}, {builtinQmltyplesPath1, builtinQmltyplesPath2}, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir) @@ -1890,7 +1903,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir) ModuleId{}, FileType::QmlDocument)))))); - updater.update(directories, {}, {}); + updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_throws_if_qml_document_does_not_exists) @@ -1898,7 +1911,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_throws_if setFilesDontExists({qmlDirPathSourceId, qmlDocumentSourceId1}); setFilesAdded({directoryPathSourceId}); - ASSERT_THROW(updater.update(directories, {}, {}), QmlDesigner::CannotParseQmlDocumentFile); + ASSERT_THROW(updater.update(directories, {}, {}, {}), QmlDesigner::CannotParseQmlDocumentFile); } TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_throws_if_directory_does_not_exists) @@ -1928,7 +1941,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_throws_if UnorderedElementsAre(directoryPathSourceId)), Field(&SynchronizationPackage::projectDatas, IsEmpty())))); - updater.update(directories, {}, {}); + updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_add_qml_document) @@ -1977,7 +1990,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_add_qml_d ModuleId{}, FileType::QmlDocument)))))); - updater.update(directories, {}, {}); + updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_removes_qml_document) @@ -2016,7 +2029,7 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_removes_q ModuleId{}, FileType::QmlDocument)))))); - updater.update(directories, {}, {}); + updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, watcher_updates_directories) @@ -3475,7 +3488,7 @@ TEST_F(ProjectStorageUpdater, update_property_editor_panes) Field(&SynchronizationPackage::updatedPropertyEditorQmlPathSourceIds, ElementsAre(directoryId))))); - updater.update({}, {}, propertyEditorQmlPath); + updater.update({}, {}, propertyEditorQmlPath, {}); } TEST_F(ProjectStorageUpdater, update_property_editor_specifics) @@ -3500,12 +3513,12 @@ TEST_F(ProjectStorageUpdater, update_property_editor_specifics) Field(&SynchronizationPackage::updatedPropertyEditorQmlPathSourceIds, ElementsAre(directoryId))))); - updater.update({}, {}, propertyEditorQmlPath); + updater.update({}, {}, propertyEditorQmlPath, {}); } TEST_F(ProjectStorageUpdater, update_property_editor_panes_is_empty_if_directory_has_not_changed) { - updater.update({}, {}, propertyEditorQmlPath); + updater.update({}, {}, propertyEditorQmlPath, {}); ON_CALL(fileSystemMock, fileStatus(_)).WillByDefault([](SourceId sourceId) { return FileStatus{sourceId, 1, 21}; }); @@ -3515,7 +3528,135 @@ TEST_F(ProjectStorageUpdater, update_property_editor_panes_is_empty_if_directory EXPECT_CALL(projectStorageMock, synchronize(PackageIsEmpty())); - updater.update({}, {}, propertyEditorQmlPath); + updater.update({}, {}, propertyEditorQmlPath, {}); +} + +TEST_F(ProjectStorageUpdater, update_type_annotations) +{ + auto itemSourceId = sourcePathCache.sourceId( + QmlDesigner::SourcePath{itemLibraryPath + "/quick.metainfo"}); + auto buttonSourceId = sourcePathCache.sourceId( + QmlDesigner::SourcePath{itemLibraryPath + "/qtquickcontrols2.metainfo"}); + setFilesChanged({itemLibraryPathSourceId, itemSourceId, buttonSourceId}); + auto qtQuickModuleId = moduleId("QtQuick"); + auto qtQuickControlsModuleId = moduleId("QtQuick.Controls.Basic"); + QmlDesigner::Storage::TypeTraits itemTraits; + itemTraits.canBeContainer = QmlDesigner::FlagIs::True; + + EXPECT_CALL(projectStorageMock, + synchronize(AllOf(Field(&SynchronizationPackage::typeAnnotations, + IsSupersetOf({IsTypeAnnotation(itemSourceId, + itemLibraryPathSourceId, + "Item", + qtQuickModuleId, + StartsWith(itemLibraryPath), + _, + _, + _), + IsTypeAnnotation(buttonSourceId, + itemLibraryPathSourceId, + "Button", + qtQuickControlsModuleId, + StartsWith(itemLibraryPath), + _, + _, + _)})), + Field(&SynchronizationPackage::updatedTypeAnnotationSourceIds, + IsSupersetOf({itemSourceId, buttonSourceId}))))); + + updater.update({}, {}, {}, {itemLibraryPath}); +} + +TEST_F(ProjectStorageUpdater, update_changed_type_annotation) +{ + auto itemSourceId = sourcePathCache.sourceId( + QmlDesigner::SourcePath{itemLibraryPath + "/quick.metainfo"}); + auto buttonSourceId = sourcePathCache.sourceId( + QmlDesigner::SourcePath{itemLibraryPath + "/qtquickcontrols2.metainfo"}); + setFilesDontChanged({itemLibraryPathSourceId}); + setFilesChanged({itemSourceId, buttonSourceId}); + auto qtQuickModuleId = moduleId("QtQuick"); + auto qtQuickControlsModuleId = moduleId("QtQuick.Controls.Basic"); + QmlDesigner::Storage::TypeTraits itemTraits; + itemTraits.canBeContainer = QmlDesigner::FlagIs::True; + + EXPECT_CALL(projectStorageMock, + synchronize(AllOf(Field(&SynchronizationPackage::typeAnnotations, + IsSupersetOf({IsTypeAnnotation(itemSourceId, + itemLibraryPathSourceId, + "Item", + qtQuickModuleId, + StartsWith(itemLibraryPath), + _, + _, + _), + IsTypeAnnotation(buttonSourceId, + itemLibraryPathSourceId, + "Button", + qtQuickControlsModuleId, + StartsWith(itemLibraryPath), + _, + _, + _)})), + Field(&SynchronizationPackage::updatedTypeAnnotationSourceIds, + IsSupersetOf({itemSourceId, buttonSourceId}))))); + + updater.update({}, {}, {}, {itemLibraryPath}); +} + +TEST_F(ProjectStorageUpdater, update_type_annotations_removed_meta_info_file) +{ + ON_CALL(fileSystemMock, fileStatus(_)).WillByDefault([](SourceId sourceId) { + return FileStatus{sourceId, 1, 21}; + }); + ON_CALL(projectStorageMock, fetchFileStatus(_)).WillByDefault([](SourceId sourceId) { + return FileStatus{sourceId, 1, 21}; + }); + auto itemSourceId = sourcePathCache.sourceId( + QmlDesigner::SourcePath{itemLibraryPath + "/quick.metainfo"}); + auto buttonSourceId = sourcePathCache.sourceId( + QmlDesigner::SourcePath{itemLibraryPath + "/qtquickcontrols2.metainfo"}); + ON_CALL(projectStorageMock, typeAnnotationDirectorySourceIds()) + .WillByDefault(Return(QmlDesigner::SmallSourceIds<64>{itemLibraryPathSourceId})); + ON_CALL(projectStorageMock, typeAnnotationSourceIds(itemLibraryPathSourceId)) + .WillByDefault(Return(QmlDesigner::SmallSourceIds<4>{itemSourceId, buttonSourceId})); + setFilesChanged({itemLibraryPathSourceId}); + setFilesDontChanged({itemSourceId, buttonSourceId}); + + EXPECT_CALL(projectStorageMock, + synchronize(AllOf(Field(&SynchronizationPackage::typeAnnotations, IsEmpty()), + Field(&SynchronizationPackage::updatedTypeAnnotationSourceIds, + IsSupersetOf({itemSourceId, buttonSourceId}))))); + + updater.update({}, {}, {}, {itemLibraryPath}); +} + +TEST_F(ProjectStorageUpdater, update_type_annotations_removed_directory) +{ + ON_CALL(fileSystemMock, fileStatus(_)).WillByDefault([](SourceId sourceId) { + return FileStatus{sourceId, 1, 21}; + }); + ON_CALL(projectStorageMock, fetchFileStatus(_)).WillByDefault([](SourceId sourceId) { + return FileStatus{sourceId, 1, 21}; + }); + auto itemSourceId = sourcePathCache.sourceId( + QmlDesigner::SourcePath{itemLibraryPath + "/quick.metainfo"}); + auto buttonSourceId = sourcePathCache.sourceId( + QmlDesigner::SourcePath{itemLibraryPath + "/qtquickcontrols2.metainfo"}); + ON_CALL(projectStorageMock, typeAnnotationDirectorySourceIds()) + .WillByDefault(Return(QmlDesigner::SmallSourceIds<64>{ + itemLibraryPathSourceId, + })); + ON_CALL(projectStorageMock, typeAnnotationSourceIds(itemLibraryPathSourceId)) + .WillByDefault(Return(QmlDesigner::SmallSourceIds<4>{itemSourceId, buttonSourceId})); + setFilesDontExists({itemLibraryPathSourceId, buttonSourceId, itemSourceId}); + + EXPECT_CALL(projectStorageMock, + synchronize(AllOf(Field(&SynchronizationPackage::typeAnnotations, IsEmpty()), + Field(&SynchronizationPackage::updatedTypeAnnotationSourceIds, + IsSupersetOf({buttonSourceId, itemSourceId}))))); + + updater.update({}, {}, {}, {itemLibraryPath}); } } // namespace diff --git a/tests/unit/tests/unittests/projectstorage/typeannotationreader-test.cpp b/tests/unit/tests/unittests/projectstorage/typeannotationreader-test.cpp index ed5c1a0778b..b90a4adf1be 100644 --- a/tests/unit/tests/unittests/projectstorage/typeannotationreader-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/typeannotationreader-test.cpp @@ -3,6 +3,7 @@ #include "../utils/googletest.h" +#include #include #include @@ -11,24 +12,6 @@ namespace { -template -auto IsTypeAnnotation(QmlDesigner::SourceId sourceId, - Utils::SmallStringView typeName, - QmlDesigner::ModuleId moduleId, - Utils::SmallStringView iconPath, - QmlDesigner::Storage::TypeTraits traits, - HintsJsonMatcher hintsJsonMatcher, - ItemLibraryJsonMatcher itemLibraryJsonMatcher) -{ - using QmlDesigner::Storage::Synchronization::TypeAnnotation; - return AllOf(Field("sourceId", &TypeAnnotation::sourceId, sourceId), - Field("typeName", &TypeAnnotation::typeName, typeName), - Field("moduleId", &TypeAnnotation::moduleId, moduleId), - Field("iconPath", &TypeAnnotation::iconPath, iconPath), - Field("traits", &TypeAnnotation::traits, traits), - Field("hintsJson", &TypeAnnotation::hintsJson, hintsJsonMatcher), - Field("itemLibraryJson", &TypeAnnotation::itemLibraryJson, itemLibraryJsonMatcher)); -} class TypeAnnotationReader : public testing::Test { @@ -56,6 +39,7 @@ protected: QmlDesigner::ProjectStorage &storage = *static_projectStorage; QmlDesigner::Storage::TypeAnnotationReader reader{storage}; QmlDesigner::SourceId sourceId = QmlDesigner::SourceId::create(33); + QmlDesigner::SourceId directorySourceId = QmlDesigner::SourceId::create(77); }; TEST_F(TypeAnnotationReader, parse_type) @@ -73,10 +57,11 @@ TEST_F(TypeAnnotationReader, parse_type) })xy"}; QmlDesigner::Storage::TypeTraits traits; - auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); ASSERT_THAT(annotations, UnorderedElementsAre(IsTypeAnnotation(sourceId, + directorySourceId, "Frame", moduleId("QtQuick.Controls"), "/path/images/frame-icon16.png", @@ -84,6 +69,7 @@ TEST_F(TypeAnnotationReader, parse_type) IsEmpty(), IsEmpty()), IsTypeAnnotation(sourceId, + directorySourceId, "Item", moduleId("QtQuick"), "/path/images/item-icon16.png", @@ -109,10 +95,11 @@ TEST_F(TypeAnnotationReader, parse_true_canBeContainer) QmlDesigner::Storage::TypeTraits traits; traits.canBeContainer = FlagIs::True; - auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); ASSERT_THAT(annotations, ElementsAre(IsTypeAnnotation(sourceId, + directorySourceId, "Frame", moduleId("QtQuick.Controls"), "/path/images/frame-icon16.png", @@ -138,10 +125,11 @@ TEST_F(TypeAnnotationReader, parse_true_forceClip) QmlDesigner::Storage::TypeTraits traits; traits.forceClip = FlagIs::True; - auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); ASSERT_THAT(annotations, ElementsAre(IsTypeAnnotation(sourceId, + directorySourceId, "Frame", moduleId("QtQuick.Controls"), "/path/images/frame-icon16.png", @@ -167,10 +155,11 @@ TEST_F(TypeAnnotationReader, parse_true_doesLayoutChildren) QmlDesigner::Storage::TypeTraits traits; traits.doesLayoutChildren = FlagIs::True; - auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); ASSERT_THAT(annotations, ElementsAre(IsTypeAnnotation(sourceId, + directorySourceId, "Frame", moduleId("QtQuick.Controls"), "/path/images/frame-icon16.png", @@ -196,10 +185,11 @@ TEST_F(TypeAnnotationReader, parse_true_canBeDroppedInFormEditor) QmlDesigner::Storage::TypeTraits traits; traits.canBeDroppedInFormEditor = FlagIs::True; - auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); ASSERT_THAT(annotations, ElementsAre(IsTypeAnnotation(sourceId, + directorySourceId, "Frame", moduleId("QtQuick.Controls"), "/path/images/frame-icon16.png", @@ -225,10 +215,11 @@ TEST_F(TypeAnnotationReader, parse_true_canBeDroppedInNavigator) QmlDesigner::Storage::TypeTraits traits; traits.canBeDroppedInNavigator = FlagIs::True; - auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); ASSERT_THAT(annotations, ElementsAre(IsTypeAnnotation(sourceId, + directorySourceId, "Frame", moduleId("QtQuick.Controls"), "/path/images/frame-icon16.png", @@ -254,10 +245,11 @@ TEST_F(TypeAnnotationReader, parse_true_canBeDroppedInView3D) QmlDesigner::Storage::TypeTraits traits; traits.canBeDroppedInView3D = FlagIs::True; - auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); ASSERT_THAT(annotations, ElementsAre(IsTypeAnnotation(sourceId, + directorySourceId, "Frame", moduleId("QtQuick.Controls"), "/path/images/frame-icon16.png", @@ -283,10 +275,11 @@ TEST_F(TypeAnnotationReader, parse_true_isMovable) QmlDesigner::Storage::TypeTraits traits; traits.isMovable = FlagIs::True; - auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); ASSERT_THAT(annotations, ElementsAre(IsTypeAnnotation(sourceId, + directorySourceId, "Frame", moduleId("QtQuick.Controls"), "/path/images/frame-icon16.png", @@ -312,10 +305,11 @@ TEST_F(TypeAnnotationReader, parse_true_isResizable) QmlDesigner::Storage::TypeTraits traits; traits.isResizable = FlagIs::True; - auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); ASSERT_THAT(annotations, ElementsAre(IsTypeAnnotation(sourceId, + directorySourceId, "Frame", moduleId("QtQuick.Controls"), "/path/images/frame-icon16.png", @@ -341,10 +335,11 @@ TEST_F(TypeAnnotationReader, parse_true_hasFormEditorItem) QmlDesigner::Storage::TypeTraits traits; traits.hasFormEditorItem = FlagIs::True; - auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); ASSERT_THAT(annotations, ElementsAre(IsTypeAnnotation(sourceId, + directorySourceId, "Frame", moduleId("QtQuick.Controls"), "/path/images/frame-icon16.png", @@ -370,10 +365,11 @@ TEST_F(TypeAnnotationReader, parse_true_isStackedContainer) QmlDesigner::Storage::TypeTraits traits; traits.isStackedContainer = FlagIs::True; - auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); ASSERT_THAT(annotations, ElementsAre(IsTypeAnnotation(sourceId, + directorySourceId, "Frame", moduleId("QtQuick.Controls"), "/path/images/frame-icon16.png", @@ -399,10 +395,11 @@ TEST_F(TypeAnnotationReader, parse_true_takesOverRenderingOfChildren) QmlDesigner::Storage::TypeTraits traits; traits.takesOverRenderingOfChildren = FlagIs::True; - auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); ASSERT_THAT(annotations, ElementsAre(IsTypeAnnotation(sourceId, + directorySourceId, "Frame", moduleId("QtQuick.Controls"), "/path/images/frame-icon16.png", @@ -428,10 +425,11 @@ TEST_F(TypeAnnotationReader, parse_true_visibleInNavigator) QmlDesigner::Storage::TypeTraits traits; traits.visibleInNavigator = FlagIs::True; - auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); ASSERT_THAT(annotations, ElementsAre(IsTypeAnnotation(sourceId, + directorySourceId, "Frame", moduleId("QtQuick.Controls"), "/path/images/frame-icon16.png", @@ -457,10 +455,11 @@ TEST_F(TypeAnnotationReader, parse_true_visibleInLibrary) QmlDesigner::Storage::TypeTraits traits; traits.visibleInLibrary = FlagIs::True; - auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); ASSERT_THAT(annotations, ElementsAre(IsTypeAnnotation(sourceId, + directorySourceId, "Frame", moduleId("QtQuick.Controls"), "/path/images/frame-icon16.png", @@ -485,10 +484,11 @@ TEST_F(TypeAnnotationReader, parse_false) })xy"}; QmlDesigner::Storage::TypeTraits traits; - auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); ASSERT_THAT(annotations, ElementsAre(IsTypeAnnotation(sourceId, + directorySourceId, "Frame", moduleId("QtQuick.Controls"), "/path/images/frame-icon16.png", @@ -526,10 +526,11 @@ TEST_F(TypeAnnotationReader, parse_complex_expression) QmlDesigner::Storage::TypeTraits itemTraits; itemTraits.canBeContainer = QmlDesigner::FlagIs::True; - auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); ASSERT_THAT(annotations, UnorderedElementsAre(IsTypeAnnotation(sourceId, + directorySourceId, "Frame", moduleId("QtQuick.Controls"), "/path/images/frame-icon16.png", @@ -538,6 +539,7 @@ TEST_F(TypeAnnotationReader, parse_complex_expression) "visibleNonDefaultProperties":"layer.effect"})xy"), IsEmpty()), IsTypeAnnotation(sourceId, + directorySourceId, "Item", moduleId("QtQuick"), "/path/images/item-icon16.png", @@ -573,10 +575,11 @@ TEST_F(TypeAnnotationReader, parse_item_library_entry) })xy"}; QmlDesigner::Storage::TypeTraits traits; - auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); ASSERT_THAT(annotations, ElementsAre(IsTypeAnnotation(sourceId, + directorySourceId, "Frame", moduleId("QtQuick.Controls"), "/path/images/frame-icon16.png", @@ -584,12 +587,12 @@ TEST_F(TypeAnnotationReader, parse_item_library_entry) IsEmpty(), StrippedStringEq(R"xy([ {"category":"Qt Quick - Controls 2", - "iconPath":"images/frame-icon.png", + "iconPath":"/path/images/frame-icon.png", "import":"QtQuick.Controls", "name":"Frame", "toolTip":"qsTr(\"An untitled container for a group of controls.\")"}, {"category":"Qt Quick - Controls 2", - "iconPath":"images/frame-icon.png", + "iconPath":"/path/images/frame-icon.png", "import":"QtQuick.Controls", "name":"Large Frame", "toolTip":"qsTr(\"An large container for a group of controls.\")"}] @@ -629,10 +632,11 @@ TEST_F(TypeAnnotationReader, parse_item_library_entry_with_properties) })xy"}; QmlDesigner::Storage::TypeTraits traits; - auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); ASSERT_THAT(annotations, ElementsAre(IsTypeAnnotation(sourceId, + directorySourceId, "Frame", moduleId("QtQuick.Controls"), "/path/images/frame-icon16.png", @@ -640,13 +644,13 @@ TEST_F(TypeAnnotationReader, parse_item_library_entry_with_properties) IsEmpty(), StrippedStringEq(R"xy([ {"category":"Qt Quick - Controls 2", - "iconPath":"images/frame-icon.png", + "iconPath":"/path/images/frame-icon.png", "import":"QtQuick.Controls", "name":"Frame", "properties":[["width","int",200.0],["height","int",100.0]], "toolTip":"qsTr(\"An untitled container for a group of controls.\")"}, {"category":"Qt Quick - Controls 2", - "iconPath":"images/frame-icon.png", + "iconPath":"/path/images/frame-icon.png", "import":"QtQuick.Controls", "name":"Large Frame", "properties":[["width","int",2000.0],["height","int",1000.0]], @@ -679,13 +683,14 @@ TEST_F(TypeAnnotationReader, parse_item_library_entry_template_path) })xy"}; QmlDesigner::Storage::TypeTraits traits; - auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); ASSERT_THAT(annotations, ElementsAre(IsTypeAnnotation(sourceId, + directorySourceId, "Frame", moduleId("QtQuick.Controls"), - {}, + Utils::SmallStringView{}, traits, IsEmpty(), StrippedStringEq(R"xy([ @@ -693,9 +698,10 @@ TEST_F(TypeAnnotationReader, parse_item_library_entry_template_path) "templatePath":"/path/templates/frame.qml"}] )xy")), IsTypeAnnotation(sourceId, + directorySourceId, "Item", moduleId("QtQuick"), - {}, + Utils::SmallStringView{}, traits, IsEmpty(), StrippedStringEq(R"xy([ @@ -730,13 +736,14 @@ TEST_F(TypeAnnotationReader, parse_item_library_entry_extra_file_paths) })xy"}; QmlDesigner::Storage::TypeTraits traits; - auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId); + auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); ASSERT_THAT(annotations, ElementsAre(IsTypeAnnotation(sourceId, + directorySourceId, "Frame", moduleId("QtQuick.Controls"), - {}, + Utils::SmallStringView{}, traits, IsEmpty(), StrippedStringEq(R"xy([ @@ -744,9 +751,10 @@ TEST_F(TypeAnnotationReader, parse_item_library_entry_extra_file_paths) "name":"Frame"}] )xy")), IsTypeAnnotation(sourceId, + directorySourceId, "Item", moduleId("QtQuick"), - {}, + Utils::SmallStringView{}, traits, IsEmpty(), StrippedStringEq(R"xy([ diff --git a/tests/unit/tests/unittests/utils/smallstring-test.cpp b/tests/unit/tests/unittests/utils/smallstring-test.cpp index 3594c838c96..71090f8760a 100644 --- a/tests/unit/tests/unittests/utils/smallstring-test.cpp +++ b/tests/unit/tests/unittests/utils/smallstring-test.cpp @@ -1326,6 +1326,19 @@ TEST(SmallString, starts_with_string_view) ASSERT_FALSE(text.startsWith('@')); } +TEST(SmallString, starts_with_qstringview) +{ + using namespace Qt::StringLiterals; + SmallString text("$column"); + + ASSERT_FALSE(text.startsWith(u"$columnxxx"_s)); + ASSERT_TRUE(text.startsWith(u"$column"_s)); + ASSERT_TRUE(text.startsWith(u"$col"_s)); + ASSERT_FALSE(text.startsWith(u"col"_s)); + ASSERT_TRUE(text.startsWith(u"$"_s)); + ASSERT_FALSE(text.startsWith(u"@"_s)); +} + TEST(SmallString, ends_with) { SmallString text("/my/path"); From b74c47ec66d99d9ade7621bdae2a76fd10be5f11 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 22 Apr 2024 14:29:50 +0200 Subject: [PATCH 192/202] QmlDesigner: Remove template parameter from project storage There are now other ways to prevent locking bugs. So we don't need the template parameter anymore. That makes it possible to move much of the code to a cpp file. Maybe later we have to move some functions back for performance reasons. Change-Id: I01269912618d7cf5e070219e7edaa3a00623b7cf Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen Reviewed-by: Reviewed-by: Thomas Hartmann --- src/libs/sqlite/sqlitebasestatement.h | 14 +- .../designercore/include/modelfwd.h | 2 +- .../designercore/projectstorage/filesystem.h | 6 +- .../projectstorage/projectstorage.cpp | 3774 +++++++++++++++- .../projectstorage/projectstorage.h | 3777 +---------------- .../projectstorage/projectstoragefwd.h | 1 - .../projectstorage/projectstorageinterface.h | 2 +- .../projectstorage/projectstorageupdater.h | 3 +- .../projectstorage/qmldocumentparser.h | 2 +- .../projectstorage/qmltypesparser.h | 7 +- .../projectstorage/sourcepathcachetypes.h | 2 + .../qmldesigner/qmldesignerprojectmanager.cpp | 4 +- .../projectstorage/projectstorage-test.cpp | 14 +- .../projectstoragepathwatcher-test.cpp | 8 +- .../projectstorageupdater-test.cpp | 8 +- .../projectstorage/qmldocumentparser-test.cpp | 4 +- .../projectstorage/qmltypesparser-test.cpp | 4 +- .../typeannotationreader-test.cpp | 6 +- 18 files changed, 4002 insertions(+), 3636 deletions(-) diff --git a/src/libs/sqlite/sqlitebasestatement.h b/src/libs/sqlite/sqlitebasestatement.h index 81b31473f83..3710021ff53 100644 --- a/src/libs/sqlite/sqlitebasestatement.h +++ b/src/libs/sqlite/sqlitebasestatement.h @@ -210,6 +210,14 @@ public: struct is_container> : std::true_type {}; + template + struct is_small_container : std::false_type + {}; + + template + struct is_small_container> : std::true_type + {}; + template::value>, @@ -223,14 +231,16 @@ public: Resetter resetter{this}; Container resultValues; - resultValues.reserve(std::max(capacity, m_maximumResultCount)); + using size_tupe = typename Container::size_type; + if constexpr (!is_small_container::value) + resultValues.reserve(static_cast(std::max(capacity, m_maximumResultCount))); bindValues(queryValues...); while (BaseStatement::next()) emplaceBackValues(resultValues); - setMaximumResultCount(resultValues.size()); + setMaximumResultCount(static_cast(resultValues.size())); return resultValues; } diff --git a/src/plugins/qmldesigner/designercore/include/modelfwd.h b/src/plugins/qmldesigner/designercore/include/modelfwd.h index 0a062289fd0..91c533fe7b6 100644 --- a/src/plugins/qmldesigner/designercore/include/modelfwd.h +++ b/src/plugins/qmldesigner/designercore/include/modelfwd.h @@ -77,7 +77,7 @@ constexpr bool useProjectStorage() using ProjectStorageType = ProjectStorageInterface; using PathCacheType = SourcePathCacheInterface; #else -using ProjectStorageType = ProjectStorage; +using ProjectStorageType = ProjectStorage; using PathCacheType = SourcePathCache; #endif diff --git a/src/plugins/qmldesigner/designercore/projectstorage/filesystem.h b/src/plugins/qmldesigner/designercore/projectstorage/filesystem.h index 078fd1ee98f..28754a8560b 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/filesystem.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/filesystem.h @@ -6,6 +6,7 @@ #include "filestatuscache.h" #include "filesysteminterface.h" #include "nonlockingmutex.h" +#include "projectstoragefwd.h" namespace Sqlite { class Database; @@ -16,12 +17,9 @@ namespace QmlDesigner { template class SourcePathCache; -template -class ProjectStorage; - class FileSystem : public FileSystemInterface { - using PathCache = SourcePathCache, NonLockingMutex>; + using PathCache = SourcePathCache; public: FileSystem(PathCache &sourcePathCache) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp index 2bef2244d70..a037ddae9a6 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp @@ -5,4 +5,3776 @@ #include -template class QmlDesigner::ProjectStorage; +namespace QmlDesigner { + +class ProjectStorage::Initializer +{ +public: + Initializer(Database &database, bool isInitialized) + { + if (!isInitialized) { + auto moduleIdColumn = createModulesTable(database); + createSourceContextsTable(database); + createSourcesTable(database); + createTypesAndePropertyDeclarationsTables(database, moduleIdColumn); + createExportedTypeNamesTable(database, moduleIdColumn); + createImportedTypeNamesTable(database); + createEnumerationsTable(database); + createFunctionsTable(database); + createSignalsTable(database); + createModuleExportedImportsTable(database, moduleIdColumn); + createDocumentImportsTable(database, moduleIdColumn); + createFileStatusesTable(database); + createProjectDatasTable(database); + createPropertyEditorPathsTable(database); + createTypeAnnotionsTable(database); + } + database.setIsInitialized(true); + } + + void createSourceContextsTable(Database &database) + { + Sqlite::Table table; + table.setUseIfNotExists(true); + table.setName("sourceContexts"); + table.addColumn("sourceContextId", Sqlite::ColumnType::Integer, {Sqlite::PrimaryKey{}}); + const Sqlite::Column &sourceContextPathColumn = table.addColumn("sourceContextPath"); + + table.addUniqueIndex({sourceContextPathColumn}); + + table.initialize(database); + } + + void createSourcesTable(Database &database) + { + Sqlite::StrictTable table; + table.setUseIfNotExists(true); + table.setName("sources"); + table.addColumn("sourceId", Sqlite::StrictColumnType::Integer, {Sqlite::PrimaryKey{}}); + const auto &sourceContextIdColumn = table.addColumn( + "sourceContextId", + Sqlite::StrictColumnType::Integer, + {Sqlite::NotNull{}, + Sqlite::ForeignKey{"sourceContexts", + "sourceContextId", + Sqlite::ForeignKeyAction::NoAction, + Sqlite::ForeignKeyAction::Cascade}}); + const auto &sourceNameColumn = table.addColumn("sourceName", Sqlite::StrictColumnType::Text); + table.addUniqueIndex({sourceContextIdColumn, sourceNameColumn}); + + table.initialize(database); + } + + void createTypesAndePropertyDeclarationsTables( + Database &database, [[maybe_unused]] const Sqlite::StrictColumn &foreignModuleIdColumn) + { + Sqlite::StrictTable typesTable; + typesTable.setUseIfNotExists(true); + typesTable.setName("types"); + typesTable.addColumn("typeId", Sqlite::StrictColumnType::Integer, {Sqlite::PrimaryKey{}}); + auto &sourceIdColumn = typesTable.addColumn("sourceId", Sqlite::StrictColumnType::Integer); + auto &typesNameColumn = typesTable.addColumn("name", Sqlite::StrictColumnType::Text); + typesTable.addColumn("traits", Sqlite::StrictColumnType::Integer); + auto &prototypeIdColumn = typesTable.addForeignKeyColumn("prototypeId", + typesTable, + Sqlite::ForeignKeyAction::NoAction, + Sqlite::ForeignKeyAction::Restrict); + typesTable.addColumn("prototypeNameId", Sqlite::StrictColumnType::Integer); + auto &extensionIdColumn = typesTable.addForeignKeyColumn("extensionId", + typesTable, + Sqlite::ForeignKeyAction::NoAction, + Sqlite::ForeignKeyAction::Restrict); + typesTable.addColumn("extensionNameId", Sqlite::StrictColumnType::Integer); + auto &defaultPropertyIdColumn = typesTable.addColumn("defaultPropertyId", + Sqlite::StrictColumnType::Integer); + typesTable.addColumn("annotationTraits", Sqlite::StrictColumnType::Integer); + typesTable.addUniqueIndex({sourceIdColumn, typesNameColumn}); + typesTable.addIndex({defaultPropertyIdColumn}); + typesTable.addIndex({prototypeIdColumn}); + typesTable.addIndex({extensionIdColumn}); + + typesTable.initialize(database); + + { + Sqlite::StrictTable propertyDeclarationTable; + propertyDeclarationTable.setUseIfNotExists(true); + propertyDeclarationTable.setName("propertyDeclarations"); + propertyDeclarationTable.addColumn("propertyDeclarationId", + Sqlite::StrictColumnType::Integer, + {Sqlite::PrimaryKey{}}); + auto &typeIdColumn = propertyDeclarationTable.addColumn("typeId"); + auto &nameColumn = propertyDeclarationTable.addColumn("name"); + auto &propertyTypeIdColumn = propertyDeclarationTable.addForeignKeyColumn( + "propertyTypeId", + typesTable, + Sqlite::ForeignKeyAction::NoAction, + Sqlite::ForeignKeyAction::Restrict); + propertyDeclarationTable.addColumn("propertyTraits", Sqlite::StrictColumnType::Integer); + propertyDeclarationTable.addColumn("propertyImportedTypeNameId", + Sqlite::StrictColumnType::Integer); + auto &aliasPropertyDeclarationIdColumn = propertyDeclarationTable.addForeignKeyColumn( + "aliasPropertyDeclarationId", + propertyDeclarationTable, + Sqlite::ForeignKeyAction::NoAction, + Sqlite::ForeignKeyAction::Restrict); + auto &aliasPropertyDeclarationTailIdColumn = propertyDeclarationTable.addForeignKeyColumn( + "aliasPropertyDeclarationTailId", + propertyDeclarationTable, + Sqlite::ForeignKeyAction::NoAction, + Sqlite::ForeignKeyAction::Restrict); + + propertyDeclarationTable.addUniqueIndex({typeIdColumn, nameColumn}); + propertyDeclarationTable.addIndex({propertyTypeIdColumn}); + propertyDeclarationTable.addIndex({aliasPropertyDeclarationIdColumn}, + "aliasPropertyDeclarationId IS NOT NULL"); + propertyDeclarationTable.addIndex({aliasPropertyDeclarationTailIdColumn}, + "aliasPropertyDeclarationTailId IS NOT NULL"); + + propertyDeclarationTable.initialize(database); + } + } + + void createExportedTypeNamesTable(Database &database, + const Sqlite::StrictColumn &foreignModuleIdColumn) + { + Sqlite::StrictTable table; + table.setUseIfNotExists(true); + table.setName("exportedTypeNames"); + table.addColumn("exportedTypeNameId", + Sqlite::StrictColumnType::Integer, + {Sqlite::PrimaryKey{}}); + auto &moduleIdColumn = table.addForeignKeyColumn("moduleId", + foreignModuleIdColumn, + Sqlite::ForeignKeyAction::NoAction, + Sqlite::ForeignKeyAction::NoAction); + auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text); + auto &typeIdColumn = table.addColumn("typeId", Sqlite::StrictColumnType::Integer); + auto &majorVersionColumn = table.addColumn("majorVersion", Sqlite::StrictColumnType::Integer); + auto &minorVersionColumn = table.addColumn("minorVersion", Sqlite::StrictColumnType::Integer); + + table.addUniqueIndex({moduleIdColumn, nameColumn}, + "majorVersion IS NULL AND minorVersion IS NULL"); + table.addUniqueIndex({moduleIdColumn, nameColumn, majorVersionColumn}, + "majorVersion IS NOT NULL AND minorVersion IS NULL"); + table.addUniqueIndex({moduleIdColumn, nameColumn, majorVersionColumn, minorVersionColumn}, + "majorVersion IS NOT NULL AND minorVersion IS NOT NULL"); + + table.addIndex({typeIdColumn}); + table.addIndex({moduleIdColumn, nameColumn}); + + table.initialize(database); + } + + void createImportedTypeNamesTable(Database &database) + { + Sqlite::StrictTable table; + table.setUseIfNotExists(true); + table.setName("importedTypeNames"); + table.addColumn("importedTypeNameId", + Sqlite::StrictColumnType::Integer, + {Sqlite::PrimaryKey{}}); + auto &importOrSourceIdColumn = table.addColumn("importOrSourceId"); + auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text); + auto &kindColumn = table.addColumn("kind", Sqlite::StrictColumnType::Integer); + + table.addUniqueIndex({kindColumn, importOrSourceIdColumn, nameColumn}); + table.addIndex({nameColumn}); + + table.initialize(database); + } + + void createEnumerationsTable(Database &database) + { + Sqlite::StrictTable table; + table.setUseIfNotExists(true); + table.setName("enumerationDeclarations"); + table.addColumn("enumerationDeclarationId", + Sqlite::StrictColumnType::Integer, + {Sqlite::PrimaryKey{}}); + auto &typeIdColumn = table.addColumn("typeId", Sqlite::StrictColumnType::Integer); + auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text); + table.addColumn("enumeratorDeclarations", Sqlite::StrictColumnType::Text); + + table.addUniqueIndex({typeIdColumn, nameColumn}); + + table.initialize(database); + } + + void createFunctionsTable(Database &database) + { + Sqlite::StrictTable table; + table.setUseIfNotExists(true); + table.setName("functionDeclarations"); + table.addColumn("functionDeclarationId", + Sqlite::StrictColumnType::Integer, + {Sqlite::PrimaryKey{}}); + auto &typeIdColumn = table.addColumn("typeId", Sqlite::StrictColumnType::Integer); + auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text); + auto &signatureColumn = table.addColumn("signature", Sqlite::StrictColumnType::Text); + table.addColumn("returnTypeName"); + + table.addUniqueIndex({typeIdColumn, nameColumn, signatureColumn}); + + table.initialize(database); + } + + void createSignalsTable(Database &database) + { + Sqlite::StrictTable table; + table.setUseIfNotExists(true); + table.setName("signalDeclarations"); + table.addColumn("signalDeclarationId", + Sqlite::StrictColumnType::Integer, + {Sqlite::PrimaryKey{}}); + auto &typeIdColumn = table.addColumn("typeId", Sqlite::StrictColumnType::Integer); + auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text); + auto &signatureColumn = table.addColumn("signature", Sqlite::StrictColumnType::Text); + + table.addUniqueIndex({typeIdColumn, nameColumn, signatureColumn}); + + table.initialize(database); + } + + Sqlite::StrictColumn createModulesTable(Database &database) + { + Sqlite::StrictTable table; + table.setUseIfNotExists(true); + table.setName("modules"); + auto &modelIdColumn = table.addColumn("moduleId", + Sqlite::StrictColumnType::Integer, + {Sqlite::PrimaryKey{}}); + auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text); + + table.addUniqueIndex({nameColumn}); + + table.initialize(database); + + return std::move(modelIdColumn); + } + + void createModuleExportedImportsTable(Database &database, + const Sqlite::StrictColumn &foreignModuleIdColumn) + { + Sqlite::StrictTable table; + table.setUseIfNotExists(true); + table.setName("moduleExportedImports"); + table.addColumn("moduleExportedImportId", + Sqlite::StrictColumnType::Integer, + {Sqlite::PrimaryKey{}}); + auto &moduleIdColumn = table.addForeignKeyColumn("moduleId", + foreignModuleIdColumn, + Sqlite::ForeignKeyAction::NoAction, + Sqlite::ForeignKeyAction::Cascade, + Sqlite::Enforment::Immediate); + auto &sourceIdColumn = table.addColumn("exportedModuleId", Sqlite::StrictColumnType::Integer); + table.addColumn("isAutoVersion", Sqlite::StrictColumnType::Integer); + table.addColumn("majorVersion", Sqlite::StrictColumnType::Integer); + table.addColumn("minorVersion", Sqlite::StrictColumnType::Integer); + + table.addUniqueIndex({sourceIdColumn, moduleIdColumn}); + + table.initialize(database); + } + + void createDocumentImportsTable(Database &database, + const Sqlite::StrictColumn &foreignModuleIdColumn) + { + Sqlite::StrictTable table; + table.setUseIfNotExists(true); + table.setName("documentImports"); + table.addColumn("importId", Sqlite::StrictColumnType::Integer, {Sqlite::PrimaryKey{}}); + auto &sourceIdColumn = table.addColumn("sourceId", Sqlite::StrictColumnType::Integer); + auto &moduleIdColumn = table.addForeignKeyColumn("moduleId", + foreignModuleIdColumn, + Sqlite::ForeignKeyAction::NoAction, + Sqlite::ForeignKeyAction::Cascade, + Sqlite::Enforment::Immediate); + auto &sourceModuleIdColumn = table.addForeignKeyColumn("sourceModuleId", + foreignModuleIdColumn, + Sqlite::ForeignKeyAction::NoAction, + Sqlite::ForeignKeyAction::Cascade, + Sqlite::Enforment::Immediate); + auto &kindColumn = table.addColumn("kind", Sqlite::StrictColumnType::Integer); + auto &majorVersionColumn = table.addColumn("majorVersion", Sqlite::StrictColumnType::Integer); + auto &minorVersionColumn = table.addColumn("minorVersion", Sqlite::StrictColumnType::Integer); + auto &parentImportIdColumn = table.addColumn("parentImportId", + Sqlite::StrictColumnType::Integer); + + table.addUniqueIndex( + {sourceIdColumn, moduleIdColumn, kindColumn, sourceModuleIdColumn, parentImportIdColumn}, + "majorVersion IS NULL AND minorVersion IS NULL"); + table.addUniqueIndex({sourceIdColumn, + moduleIdColumn, + kindColumn, + sourceModuleIdColumn, + majorVersionColumn, + parentImportIdColumn}, + "majorVersion IS NOT NULL AND minorVersion IS NULL"); + table.addUniqueIndex({sourceIdColumn, + moduleIdColumn, + kindColumn, + sourceModuleIdColumn, + majorVersionColumn, + minorVersionColumn, + parentImportIdColumn}, + "majorVersion IS NOT NULL AND minorVersion IS NOT NULL"); + + table.addIndex({sourceIdColumn, kindColumn}); + + table.initialize(database); + } + + void createFileStatusesTable(Database &database) + { + Sqlite::StrictTable table; + table.setUseIfNotExists(true); + table.setName("fileStatuses"); + table.addColumn("sourceId", + Sqlite::StrictColumnType::Integer, + {Sqlite::PrimaryKey{}, + Sqlite::ForeignKey{"sources", + "sourceId", + Sqlite::ForeignKeyAction::NoAction, + Sqlite::ForeignKeyAction::Cascade}}); + table.addColumn("size", Sqlite::StrictColumnType::Integer); + table.addColumn("lastModified", Sqlite::StrictColumnType::Integer); + + table.initialize(database); + } + + void createProjectDatasTable(Database &database) + { + Sqlite::StrictTable table; + table.setUseIfNotExists(true); + table.setUseWithoutRowId(true); + table.setName("projectDatas"); + auto &projectSourceIdColumn = table.addColumn("projectSourceId", + Sqlite::StrictColumnType::Integer); + auto &sourceIdColumn = table.addColumn("sourceId", Sqlite::StrictColumnType::Integer); + table.addColumn("moduleId", Sqlite::StrictColumnType::Integer); + table.addColumn("fileType", Sqlite::StrictColumnType::Integer); + + table.addPrimaryKeyContraint({projectSourceIdColumn, sourceIdColumn}); + table.addUniqueIndex({sourceIdColumn}); + + table.initialize(database); + } + + void createPropertyEditorPathsTable(Database &database) + { + Sqlite::StrictTable table; + table.setUseIfNotExists(true); + table.setUseWithoutRowId(true); + table.setName("propertyEditorPaths"); + table.addColumn("typeId", Sqlite::StrictColumnType::Integer, {Sqlite::PrimaryKey{}}); + table.addColumn("pathSourceId", Sqlite::StrictColumnType::Integer); + auto &directoryIdColumn = table.addColumn("directoryId", Sqlite::StrictColumnType::Integer); + + table.addIndex({directoryIdColumn}); + + table.initialize(database); + } + + void createTypeAnnotionsTable(Database &database) + { + Sqlite::StrictTable table; + table.setUseIfNotExists(true); + table.setUseWithoutRowId(true); + table.setName("typeAnnotations"); + auto &typeIdColumn = table.addColumn("typeId", + Sqlite::StrictColumnType::Integer, + {Sqlite::PrimaryKey{}}); + auto &sourceIdColumn = table.addColumn("sourceId", Sqlite::StrictColumnType::Integer); + auto &directorySourceIdColumn = table.addColumn("directorySourceId", + Sqlite::StrictColumnType::Integer); + + table.addColumn("iconPath", Sqlite::StrictColumnType::Text); + table.addColumn("itemLibrary", Sqlite::StrictColumnType::Text); + table.addColumn("hints", Sqlite::StrictColumnType::Text); + + table.addUniqueIndex({sourceIdColumn, typeIdColumn}); + table.addIndex({directorySourceIdColumn}); + + table.initialize(database); + } +}; + +ProjectStorage::ProjectStorage(Database &database, bool isInitialized) + : database{database} + , exclusiveTransaction{database} + , initializer{std::make_unique(database, isInitialized)} + , moduleCache{ModuleStorageAdapter{*this}} +{ + NanotraceHR::Tracer tracer{"initialize"_t, projectStorageCategory()}; + + exclusiveTransaction.commit(); + + database.walCheckpointFull(); + + moduleCache.populate(); +} + +ProjectStorage::~ProjectStorage() = default; + +void ProjectStorage::synchronize(Storage::Synchronization::SynchronizationPackage package) +{ + NanotraceHR::Tracer tracer{"synchronize"_t, projectStorageCategory()}; + + TypeIds deletedTypeIds; + Sqlite::withImmediateTransaction(database, [&] { + AliasPropertyDeclarations insertedAliasPropertyDeclarations; + AliasPropertyDeclarations updatedAliasPropertyDeclarations; + + AliasPropertyDeclarations relinkableAliasPropertyDeclarations; + PropertyDeclarations relinkablePropertyDeclarations; + Prototypes relinkablePrototypes; + Prototypes relinkableExtensions; + + TypeIds updatedTypeIds; + updatedTypeIds.reserve(package.types.size()); + + TypeIds typeIdsToBeDeleted; + + 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); + synchronizeTypeAnnotations(package.typeAnnotations, package.updatedTypeAnnotationSourceIds); + synchronizePropertyEditorQmlPaths(package.propertyEditorQmlPaths, + package.updatedPropertyEditorQmlPathSourceIds); + + deleteNotUpdatedTypes(updatedTypeIds, + package.updatedSourceIds, + typeIdsToBeDeleted, + relinkableAliasPropertyDeclarations, + relinkablePropertyDeclarations, + relinkablePrototypes, + relinkableExtensions, + deletedTypeIds); + + relink(relinkableAliasPropertyDeclarations, + relinkablePropertyDeclarations, + relinkablePrototypes, + relinkableExtensions, + deletedTypeIds); + + linkAliases(insertedAliasPropertyDeclarations, updatedAliasPropertyDeclarations); + + synchronizeProjectDatas(package.projectDatas, package.updatedProjectSourceIds); + + commonTypeCache_.resetTypeIds(); + }); + + callRefreshMetaInfoCallback(deletedTypeIds); +} + +void ProjectStorage::synchronizeDocumentImports(Storage::Imports imports, SourceId sourceId) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"synchronize document imports"_t, + projectStorageCategory(), + keyValue("imports", imports), + keyValue("source id", sourceId)}; + + Sqlite::withImmediateTransaction(database, [&] { + synchronizeDocumentImports(imports, {sourceId}, Storage::Synchronization::ImportKind::Import); + }); +} + +void ProjectStorage::addObserver(ProjectStorageObserver *observer) +{ + NanotraceHR::Tracer tracer{"add observer"_t, projectStorageCategory()}; + observers.push_back(observer); +} + +void ProjectStorage::removeObserver(ProjectStorageObserver *observer) +{ + NanotraceHR::Tracer tracer{"remove observer"_t, projectStorageCategory()}; + observers.removeOne(observer); +} + +ModuleId ProjectStorage::moduleId(Utils::SmallStringView moduleName) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get module id"_t, + projectStorageCategory(), + keyValue("module name", moduleName)}; + + auto moduleId = moduleCache.id(moduleName); + + tracer.end(keyValue("module id", moduleId)); + + return moduleId; +} + +Utils::SmallString ProjectStorage::moduleName(ModuleId moduleId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get module name"_t, + projectStorageCategory(), + keyValue("module id", moduleId)}; + + if (!moduleId) + throw ModuleDoesNotExists{}; + + auto moduleName = moduleCache.value(moduleId); + + tracer.end(keyValue("module name", moduleName)); + + return moduleName; +} + +TypeId ProjectStorage::typeId(ModuleId moduleId, + Utils::SmallStringView exportedTypeName, + Storage::Version version) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get type id by exported name"_t, + projectStorageCategory(), + keyValue("module id", moduleId), + keyValue("exported type name", exportedTypeName), + keyValue("version", version)}; + + TypeId typeId; + + if (version.minor) { + typeId = selectTypeIdByModuleIdAndExportedNameAndVersionStatement.valueWithTransaction( + moduleId, exportedTypeName, version.major.value, version.minor.value); + + } else if (version.major) { + typeId = selectTypeIdByModuleIdAndExportedNameAndMajorVersionStatement + .valueWithTransaction(moduleId, exportedTypeName, version.major.value); + + } else { + typeId = selectTypeIdByModuleIdAndExportedNameStatement + .valueWithTransaction(moduleId, exportedTypeName); + } + + tracer.end(keyValue("type id", typeId)); + + return typeId; +} + +TypeId ProjectStorage::typeId(ImportedTypeNameId typeNameId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get type id by imported type name"_t, + projectStorageCategory(), + keyValue("imported type name id", typeNameId)}; + + auto typeId = Sqlite::withDeferredTransaction(database, [&] { return fetchTypeId(typeNameId); }); + + tracer.end(keyValue("type id", typeId)); + + return typeId; +} + +QVarLengthArray ProjectStorage::typeIds(ModuleId moduleId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get type ids by module id"_t, + projectStorageCategory(), + keyValue("module id", moduleId)}; + + auto typeIds = selectTypeIdsByModuleIdStatement.valuesWithTransaction>( + moduleId); + + tracer.end(keyValue("type ids", typeIds)); + + return typeIds; +} + +Storage::Info::ExportedTypeNames ProjectStorage::exportedTypeNames(TypeId typeId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get exported type names by type id"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + auto exportedTypenames = selectExportedTypesByTypeIdStatement + .valuesWithTransaction(typeId); + + tracer.end(keyValue("exported type names", exportedTypenames)); + + return exportedTypenames; +} + +Storage::Info::ExportedTypeNames ProjectStorage::exportedTypeNames(TypeId typeId, SourceId sourceId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get exported type names by source id"_t, + projectStorageCategory(), + keyValue("type id", typeId), + keyValue("source id", sourceId)}; + + auto exportedTypenames = selectExportedTypesByTypeIdAndSourceIdStatement + .valuesWithTransaction(typeId, + sourceId); + + tracer.end(keyValue("exported type names", exportedTypenames)); + + return exportedTypenames; +} + +ImportId ProjectStorage::importId(const Storage::Import &import) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get import id by import"_t, + projectStorageCategory(), + keyValue("import", import)}; + + auto importId = Sqlite::withDeferredTransaction(database, [&] { + return fetchImportId(import.sourceId, import); + }); + + tracer.end(keyValue("import id", importId)); + + return importId; +} + +ImportedTypeNameId ProjectStorage::importedTypeNameId(ImportId importId, + Utils::SmallStringView typeName) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get imported type name id by import id"_t, + projectStorageCategory(), + keyValue("import id", importId), + keyValue("imported type name", typeName)}; + + auto importedTypeNameId = Sqlite::withDeferredTransaction(database, [&] { + return fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::QualifiedExported, + importId, + typeName); + }); + + tracer.end(keyValue("imported type name id", importedTypeNameId)); + + return importedTypeNameId; +} + +ImportedTypeNameId ProjectStorage::importedTypeNameId(SourceId sourceId, + Utils::SmallStringView typeName) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get imported type name id by source id"_t, + projectStorageCategory(), + keyValue("source id", sourceId), + keyValue("imported type name", typeName)}; + + auto importedTypeNameId = Sqlite::withDeferredTransaction(database, [&] { + return fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::Exported, + sourceId, + typeName); + }); + + tracer.end(keyValue("imported type name id", importedTypeNameId)); + + return importedTypeNameId; +} + +QVarLengthArray ProjectStorage::propertyDeclarationIds(TypeId typeId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get property declaration ids"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + auto propertyDeclarationIds = Sqlite::withDeferredTransaction(database, [&] { + return fetchPropertyDeclarationIds(typeId); + }); + + std::sort(propertyDeclarationIds.begin(), propertyDeclarationIds.end()); + + tracer.end(keyValue("property declaration ids", propertyDeclarationIds)); + + return propertyDeclarationIds; +} + +QVarLengthArray ProjectStorage::localPropertyDeclarationIds(TypeId typeId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get local property declaration ids"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + auto propertyDeclarationIds = selectLocalPropertyDeclarationIdsForTypeStatement + .valuesWithTransaction>( + typeId); + + tracer.end(keyValue("property declaration ids", propertyDeclarationIds)); + + return propertyDeclarationIds; +} + +PropertyDeclarationId ProjectStorage::propertyDeclarationId(TypeId typeId, + Utils::SmallStringView propertyName) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get property declaration id"_t, + projectStorageCategory(), + keyValue("type id", typeId), + keyValue("property name", propertyName)}; + + auto propertyDeclarationId = Sqlite::withDeferredTransaction(database, [&] { + return fetchPropertyDeclarationId(typeId, propertyName); + }); + + tracer.end(keyValue("property declaration id", propertyDeclarationId)); + + return propertyDeclarationId; +} + +PropertyDeclarationId ProjectStorage::localPropertyDeclarationId(TypeId typeId, + Utils::SmallStringView propertyName) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get local property declaration id"_t, + projectStorageCategory(), + keyValue("type id", typeId), + keyValue("property name", propertyName)}; + + auto propertyDeclarationId = selectLocalPropertyDeclarationIdForTypeAndPropertyNameStatement + .valueWithTransaction(typeId, + propertyName); + + tracer.end(keyValue("property declaration id", propertyDeclarationId)); + + return propertyDeclarationId; +} + +PropertyDeclarationId ProjectStorage::defaultPropertyDeclarationId(TypeId typeId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get default property declaration id"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + auto propertyDeclarationId = Sqlite::withDeferredTransaction(database, [&] { + return fetchDefaultPropertyDeclarationId(typeId); + }); + + tracer.end(keyValue("property declaration id", propertyDeclarationId)); + + return propertyDeclarationId; +} + +std::optional ProjectStorage::propertyDeclaration( + PropertyDeclarationId propertyDeclarationId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get property declaration"_t, + projectStorageCategory(), + keyValue("property declaration id", propertyDeclarationId)}; + + auto propertyDeclaration = selectPropertyDeclarationForPropertyDeclarationIdStatement + .optionalValueWithTransaction( + propertyDeclarationId); + + tracer.end(keyValue("property declaration", propertyDeclaration)); + + return propertyDeclaration; +} + +std::optional ProjectStorage::type(TypeId typeId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get type"_t, projectStorageCategory(), keyValue("type id", typeId)}; + + auto type = selectInfoTypeByTypeIdStatement.optionalValueWithTransaction( + typeId); + + tracer.end(keyValue("type", type)); + + return type; +} + +Utils::PathString ProjectStorage::typeIconPath(TypeId typeId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get type icon path"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + auto typeIconPath = selectTypeIconPathStatement.valueWithTransaction(typeId); + + tracer.end(keyValue("type icon path", typeIconPath)); + + return typeIconPath; +} + +Storage::Info::TypeHints ProjectStorage::typeHints(TypeId typeId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get type hints"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + auto typeHints = selectTypeHintsStatement.valuesWithTransaction( + typeId); + + tracer.end(keyValue("type hints", typeHints)); + + return typeHints; +} + +SmallSourceIds<4> ProjectStorage::typeAnnotationSourceIds(SourceId directoryId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get type annotaion source ids"_t, + projectStorageCategory(), + keyValue("source id", directoryId)}; + + auto sourceIds = selectTypeAnnotationSourceIdsStatement.valuesWithTransaction>( + directoryId); + + tracer.end(keyValue("source ids", sourceIds)); + + return sourceIds; +} + +SmallSourceIds<64> ProjectStorage::typeAnnotationDirectorySourceIds() const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get type annotaion source ids"_t, projectStorageCategory()}; + + auto sourceIds = selectTypeAnnotationDirectorySourceIdsStatement + .valuesWithTransaction>(); + + tracer.end(keyValue("source ids", sourceIds)); + + return sourceIds; +} + +Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(TypeId typeId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get item library entries by type id"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + using Storage::Info::ItemLibraryProperties; + Storage::Info::ItemLibraryEntries entries; + + auto callback = [&](TypeId typeId_, + Utils::SmallStringView name, + Utils::SmallStringView iconPath, + Utils::SmallStringView category, + Utils::SmallStringView import, + Utils::SmallStringView toolTip, + Utils::SmallStringView properties, + Utils::SmallStringView extraFilePaths, + Utils::SmallStringView templatePath) { + auto &last = entries.emplace_back(typeId_, name, iconPath, category, import, toolTip, templatePath); + if (properties.size()) + selectItemLibraryPropertiesStatement.readTo(last.properties, properties); + if (extraFilePaths.size()) + selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths); + }; + + selectItemLibraryEntriesByTypeIdStatement.readCallbackWithTransaction(callback, typeId); + + tracer.end(keyValue("item library entries", entries)); + + return entries; +} + +Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(ImportId importId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get item library entries by import id"_t, + projectStorageCategory(), + keyValue("import id", importId)}; + + using Storage::Info::ItemLibraryProperties; + Storage::Info::ItemLibraryEntries entries; + + auto callback = [&](TypeId typeId_, + Utils::SmallStringView name, + Utils::SmallStringView iconPath, + Utils::SmallStringView category, + Utils::SmallStringView import, + Utils::SmallStringView toolTip, + Utils::SmallStringView properties, + Utils::SmallStringView extraFilePaths, + Utils::SmallStringView templatePath) { + auto &last = entries.emplace_back(typeId_, name, iconPath, category, import, toolTip, templatePath); + if (properties.size()) + selectItemLibraryPropertiesStatement.readTo(last.properties, properties); + if (extraFilePaths.size()) + selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths); + }; + + selectItemLibraryEntriesByTypeIdStatement.readCallbackWithTransaction(callback, importId); + + tracer.end(keyValue("item library entries", entries)); + + return entries; +} + +Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(SourceId sourceId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get item library entries by source id"_t, + projectStorageCategory(), + keyValue("source id", sourceId)}; + + using Storage::Info::ItemLibraryProperties; + Storage::Info::ItemLibraryEntries entries; + + auto callback = [&](TypeId typeId, + Utils::SmallStringView name, + Utils::SmallStringView iconPath, + Utils::SmallStringView category, + Utils::SmallStringView import, + Utils::SmallStringView toolTip, + Utils::SmallStringView properties, + Utils::SmallStringView extraFilePaths, + Utils::SmallStringView templatePath) { + auto &last = entries.emplace_back(typeId, name, iconPath, category, import, toolTip, templatePath); + if (properties.size()) + selectItemLibraryPropertiesStatement.readTo(last.properties, properties); + if (extraFilePaths.size()) + selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths); + }; + + selectItemLibraryEntriesBySourceIdStatement.readCallbackWithTransaction(callback, sourceId); + + tracer.end(keyValue("item library entries", entries)); + + return entries; +} + +Storage::Info::ItemLibraryEntries ProjectStorage::allItemLibraryEntries() const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get all item library entries"_t, projectStorageCategory()}; + + using Storage::Info::ItemLibraryProperties; + Storage::Info::ItemLibraryEntries entries; + + auto callback = [&](TypeId typeId, + Utils::SmallStringView name, + Utils::SmallStringView iconPath, + Utils::SmallStringView category, + Utils::SmallStringView import, + Utils::SmallStringView toolTip, + Utils::SmallStringView properties, + Utils::SmallStringView extraFilePaths, + Utils::SmallStringView templatePath) { + auto &last = entries.emplace_back(typeId, name, iconPath, category, import, toolTip, templatePath); + if (properties.size()) + selectItemLibraryPropertiesStatement.readTo(last.properties, properties); + if (extraFilePaths.size()) + selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths); + }; + + selectItemLibraryEntriesStatement.readCallbackWithTransaction(callback); + + tracer.end(keyValue("item library entries", entries)); + + return entries; +} + +std::vector ProjectStorage::signalDeclarationNames(TypeId typeId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get signal names"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + auto signalDeclarationNames = selectSignalDeclarationNamesForTypeStatement + .valuesWithTransaction(typeId); + + tracer.end(keyValue("signal names", signalDeclarationNames)); + + return signalDeclarationNames; +} + +std::vector ProjectStorage::functionDeclarationNames(TypeId typeId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get function names"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + auto functionDeclarationNames = selectFuncionDeclarationNamesForTypeStatement + .valuesWithTransaction(typeId); + + tracer.end(keyValue("function names", functionDeclarationNames)); + + return functionDeclarationNames; +} + +std::optional ProjectStorage::propertyName( + PropertyDeclarationId propertyDeclarationId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get property name"_t, + projectStorageCategory(), + keyValue("property declaration id", propertyDeclarationId)}; + + auto propertyName = selectPropertyNameStatement.optionalValueWithTransaction( + propertyDeclarationId); + + tracer.end(keyValue("property name", propertyName)); + + return propertyName; +} + +SmallTypeIds<16> ProjectStorage::prototypeIds(TypeId type) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get prototypes"_t, projectStorageCategory(), keyValue("type id", type)}; + + auto prototypeIds = selectPrototypeAndExtensionIdsStatement.valuesWithTransaction>( + type); + + tracer.end(keyValue("type ids", prototypeIds)); + + return prototypeIds; +} + +SmallTypeIds<16> ProjectStorage::prototypeAndSelfIds(TypeId typeId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get prototypes and self"_t, projectStorageCategory()}; + + SmallTypeIds<16> prototypeAndSelfIds; + prototypeAndSelfIds.push_back(typeId); + + selectPrototypeAndExtensionIdsStatement.readToWithTransaction(prototypeAndSelfIds, typeId); + + tracer.end(keyValue("type ids", prototypeAndSelfIds)); + + return prototypeAndSelfIds; +} + +SmallTypeIds<64> ProjectStorage::heirIds(TypeId typeId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get heirs"_t, projectStorageCategory()}; + + auto heirIds = selectHeirTypeIdsStatement.valuesWithTransaction>(typeId); + + tracer.end(keyValue("type ids", heirIds)); + + return heirIds; +} + +bool ProjectStorage::isBasedOn(TypeId) const +{ + return false; +} + +bool ProjectStorage::isBasedOn(TypeId typeId, TypeId id1) const +{ + return isBasedOn_(typeId, id1); +} + +bool ProjectStorage::isBasedOn(TypeId typeId, TypeId id1, TypeId id2) const +{ + return isBasedOn_(typeId, id1, id2); +} + +bool ProjectStorage::isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3) const +{ + return isBasedOn_(typeId, id1, id2, id3); +} + +bool ProjectStorage::isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4) const +{ + return isBasedOn_(typeId, id1, id2, id3, id4); +} + +bool ProjectStorage::isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4, TypeId id5) const +{ + return isBasedOn_(typeId, id1, id2, id3, id4, id5); +} + +bool ProjectStorage::isBasedOn( + TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4, TypeId id5, TypeId id6) const +{ + return isBasedOn_(typeId, id1, id2, id3, id4, id5, id6); +} + +bool ProjectStorage::isBasedOn( + TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4, TypeId id5, TypeId id6, TypeId id7) const +{ + return isBasedOn_(typeId, id1, id2, id3, id4, id5, id6, id7); +} + +TypeId ProjectStorage::fetchTypeIdByExportedName(Utils::SmallStringView name) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is based on"_t, + projectStorageCategory(), + keyValue("exported type name", name)}; + + auto typeId = selectTypeIdByExportedNameStatement.valueWithTransaction(name); + + tracer.end(keyValue("type id", typeId)); + + return typeId; +} + +TypeId ProjectStorage::fetchTypeIdByModuleIdsAndExportedName(ModuleIds moduleIds, + Utils::SmallStringView name) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch type id by module ids and exported name"_t, + projectStorageCategory(), + keyValue("module ids", NanotraceHR::array(moduleIds)), + keyValue("exported type name", name)}; + auto typeId = selectTypeIdByModuleIdsAndExportedNameStatement.valueWithTransaction( + static_cast(moduleIds.data()), static_cast(moduleIds.size()), name); + + tracer.end(keyValue("type id", typeId)); + + return typeId; +} + +TypeId ProjectStorage::fetchTypeIdByName(SourceId sourceId, Utils::SmallStringView name) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch type id by name"_t, + projectStorageCategory(), + keyValue("source id", sourceId), + keyValue("internal type name", name)}; + + auto typeId = selectTypeIdBySourceIdAndNameStatement.valueWithTransaction(sourceId, name); + + tracer.end(keyValue("type id", typeId)); + + return typeId; +} + +Storage::Synchronization::Type ProjectStorage::fetchTypeByTypeId(TypeId typeId) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch type by type id"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + auto type = Sqlite::withDeferredTransaction(database, [&] { + auto type = selectTypeByTypeIdStatement.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); + + return type; + }); + + tracer.end(keyValue("type", type)); + + return type; +} + +Storage::Synchronization::Types ProjectStorage::fetchTypes() +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch types"_t, projectStorageCategory()}; + + auto types = Sqlite::withDeferredTransaction(database, [&] { + auto types = selectTypesStatement.values(); + + 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; + }); + + tracer.end(keyValue("type", types)); + + return types; +} + +SourceContextId ProjectStorage::fetchSourceContextIdUnguarded(Utils::SmallStringView sourceContextPath) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch source context id unguarded"_t, projectStorageCategory()}; + + auto sourceContextId = readSourceContextId(sourceContextPath); + + return sourceContextId ? sourceContextId : writeSourceContextId(sourceContextPath); +} + +SourceContextId ProjectStorage::fetchSourceContextId(Utils::SmallStringView sourceContextPath) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch source context id"_t, + projectStorageCategory(), + keyValue("source context path", sourceContextPath)}; + + SourceContextId sourceContextId; + try { + sourceContextId = Sqlite::withDeferredTransaction(database, [&] { + return fetchSourceContextIdUnguarded(sourceContextPath); + }); + } catch (const Sqlite::ConstraintPreventsModification &) { + sourceContextId = fetchSourceContextId(sourceContextPath); + } + + tracer.end(keyValue("source context id", sourceContextId)); + + return sourceContextId; +} + +Utils::PathString ProjectStorage::fetchSourceContextPath(SourceContextId sourceContextId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch source context path"_t, + projectStorageCategory(), + keyValue("source context id", sourceContextId)}; + + auto path = Sqlite::withDeferredTransaction(database, [&] { + auto optionalSourceContextPath = selectSourceContextPathFromSourceContextsBySourceContextIdStatement + .optionalValue(sourceContextId); + + if (!optionalSourceContextPath) + throw SourceContextIdDoesNotExists(); + + return std::move(*optionalSourceContextPath); + }); + + tracer.end(keyValue("source context path", path)); + + return path; +} + +Cache::SourceContexts ProjectStorage::fetchAllSourceContexts() const +{ + NanotraceHR::Tracer tracer{"fetch all source contexts"_t, projectStorageCategory()}; + + return selectAllSourceContextsStatement.valuesWithTransaction(); +} + +SourceId ProjectStorage::fetchSourceId(SourceContextId sourceContextId, + Utils::SmallStringView sourceName) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch source id"_t, + projectStorageCategory(), + keyValue("source context id", sourceContextId), + keyValue("source name", sourceName)}; + + auto sourceId = Sqlite::withDeferredTransaction(database, [&] { + return fetchSourceIdUnguarded(sourceContextId, sourceName); + }); + + tracer.end(keyValue("source id", sourceId)); + + return sourceId; +} + +Cache::SourceNameAndSourceContextId ProjectStorage::fetchSourceNameAndSourceContextId(SourceId sourceId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch source name and source context id"_t, + projectStorageCategory(), + keyValue("source id", sourceId)}; + + auto value = selectSourceNameAndSourceContextIdFromSourcesBySourceIdStatement + .valueWithTransaction(sourceId); + + if (!value.sourceContextId) + throw SourceIdDoesNotExists(); + + tracer.end(keyValue("source name", value.sourceName), + keyValue("source context id", value.sourceContextId)); + + return value; +} + +void ProjectStorage::clearSources() +{ + Sqlite::withImmediateTransaction(database, [&] { + deleteAllSourceContextsStatement.execute(); + deleteAllSourcesStatement.execute(); + }); +} + +SourceContextId ProjectStorage::fetchSourceContextId(SourceId sourceId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch source context id"_t, + projectStorageCategory(), + keyValue("source id", sourceId)}; + + auto sourceContextId = selectSourceContextIdFromSourcesBySourceIdStatement + .valueWithTransaction(sourceId); + + if (!sourceContextId) + throw SourceIdDoesNotExists(); + + tracer.end(keyValue("source context id", sourceContextId)); + + return sourceContextId; +} + +Cache::Sources ProjectStorage::fetchAllSources() const +{ + NanotraceHR::Tracer tracer{"fetch all sources"_t, projectStorageCategory()}; + + return selectAllSourcesStatement.valuesWithTransaction(); +} + +SourceId ProjectStorage::fetchSourceIdUnguarded(SourceContextId sourceContextId, + Utils::SmallStringView sourceName) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch source id unguarded"_t, + projectStorageCategory(), + keyValue("source context id", sourceContextId), + keyValue("source name", sourceName)}; + + auto sourceId = readSourceId(sourceContextId, sourceName); + + if (!sourceId) + sourceId = writeSourceId(sourceContextId, sourceName); + + tracer.end(keyValue("source id", sourceId)); + + return sourceId; +} + +FileStatus ProjectStorage::fetchFileStatus(SourceId sourceId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch file status"_t, + projectStorageCategory(), + keyValue("source id", sourceId)}; + + auto fileStatus = selectFileStatusesForSourceIdStatement.valueWithTransaction(sourceId); + + tracer.end(keyValue("file status", fileStatus)); + + return fileStatus; +} + +std::optional ProjectStorage::fetchProjectData(SourceId sourceId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch project data"_t, + projectStorageCategory(), + keyValue("source id", sourceId)}; + + auto projectData = selectProjectDataForSourceIdStatement + .optionalValueWithTransaction( + sourceId); + + tracer.end(keyValue("project data", projectData)); + + return projectData; +} + +Storage::Synchronization::ProjectDatas ProjectStorage::fetchProjectDatas(SourceId projectSourceId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch project datas by source id"_t, + projectStorageCategory(), + keyValue("source id", projectSourceId)}; + + auto projectDatas = selectProjectDatasForSourceIdStatement + .valuesWithTransaction( + projectSourceId); + + tracer.end(keyValue("project datas", projectDatas)); + + return projectDatas; +} + +Storage::Synchronization::ProjectDatas ProjectStorage::fetchProjectDatas( + const SourceIds &projectSourceIds) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch project datas by source ids"_t, + projectStorageCategory(), + keyValue("source ids", projectSourceIds)}; + + auto projectDatas = selectProjectDatasForSourceIdsStatement + .valuesWithTransaction( + toIntegers(projectSourceIds)); + + tracer.end(keyValue("project datas", projectDatas)); + + return projectDatas; +} + +void ProjectStorage::setPropertyEditorPathId(TypeId typeId, SourceId pathId) +{ + Sqlite::ImmediateSessionTransaction transaction{database}; + + upsertPropertyEditorPathIdStatement.write(typeId, pathId); + + transaction.commit(); +} + +SourceId ProjectStorage::propertyEditorPathId(TypeId typeId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"property editor path id"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + auto sourceId = selectPropertyEditorPathIdStatement.valueWithTransaction(typeId); + + tracer.end(keyValue("source id", sourceId)); + + return sourceId; +} + +Storage::Imports ProjectStorage::fetchDocumentImports() const +{ + NanotraceHR::Tracer tracer{"fetch document imports"_t, projectStorageCategory()}; + + return selectAllDocumentImportForSourceIdStatement.valuesWithTransaction(); +} + +void ProjectStorage::resetForTestsOnly() +{ + database.clearAllTablesForTestsOnly(); + commonTypeCache_.clearForTestsOnly(); + moduleCache.clearForTestOnly(); +} + +bool ProjectStorage::moduleNameLess(Utils::SmallStringView first, Utils::SmallStringView second) noexcept +{ + return first < second; +} + +ModuleId ProjectStorage::fetchModuleId(Utils::SmallStringView moduleName) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch module id"_t, + projectStorageCategory(), + keyValue("module name", moduleName)}; + + auto moduleId = Sqlite::withDeferredTransaction(database, [&] { + return fetchModuleIdUnguarded(moduleName); + }); + + tracer.end(keyValue("module id", moduleId)); + + return moduleId; +} + +Utils::PathString ProjectStorage::fetchModuleName(ModuleId id) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch module name"_t, + projectStorageCategory(), + keyValue("module id", id)}; + + auto moduleName = Sqlite::withDeferredTransaction(database, + [&] { return fetchModuleNameUnguarded(id); }); + + tracer.end(keyValue("module name", moduleName)); + + return moduleName; +} + +ProjectStorage::Modules ProjectStorage::fetchAllModules() const +{ + NanotraceHR::Tracer tracer{"fetch all modules"_t, projectStorageCategory()}; + + return selectAllModulesStatement.valuesWithTransaction(); +} + +void ProjectStorage::callRefreshMetaInfoCallback(const TypeIds &deletedTypeIds) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"call refresh meta info callback"_t, + projectStorageCategory(), + keyValue("type ids", deletedTypeIds)}; + + if (deletedTypeIds.size()) { + for (ProjectStorageObserver *observer : observers) + observer->removedTypeIds(deletedTypeIds); + } +} + +SourceIds ProjectStorage::filterSourceIdsWithoutType(const SourceIds &updatedSourceIds, + SourceIds &sourceIdsOfTypes) +{ + std::sort(sourceIdsOfTypes.begin(), sourceIdsOfTypes.end()); + + SourceIds sourceIdsWithoutTypeSourceIds; + sourceIdsWithoutTypeSourceIds.reserve(updatedSourceIds.size()); + std::set_difference(updatedSourceIds.begin(), + updatedSourceIds.end(), + sourceIdsOfTypes.begin(), + sourceIdsOfTypes.end(), + std::back_inserter(sourceIdsWithoutTypeSourceIds)); + + return sourceIdsWithoutTypeSourceIds; +} + +TypeIds ProjectStorage::fetchTypeIds(const SourceIds &sourceIds) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch type ids"_t, + projectStorageCategory(), + keyValue("source ids", sourceIds)}; + + return selectTypeIdsForSourceIdsStatement.values(toIntegers(sourceIds)); +} + +void ProjectStorage::unique(SourceIds &sourceIds) +{ + std::sort(sourceIds.begin(), sourceIds.end()); + auto newEnd = std::unique(sourceIds.begin(), sourceIds.end()); + sourceIds.erase(newEnd, sourceIds.end()); +} + +void ProjectStorage::synchronizeTypeTraits(TypeId typeId, Storage::TypeTraits traits) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"synchronize type traits"_t, + projectStorageCategory(), + keyValue("type id", typeId), + keyValue("type traits", traits)}; + + updateTypeAnnotationTraitStatement.write(typeId, traits.annotation); +} + +void ProjectStorage::updateTypeIdInTypeAnnotations(Storage::Synchronization::TypeAnnotations &typeAnnotations) +{ + NanotraceHR::Tracer tracer{"update type id in type annotations"_t, projectStorageCategory()}; + + for (auto &annotation : typeAnnotations) { + annotation.typeId = fetchTypeIdByModuleIdAndExportedName(annotation.moduleId, + annotation.typeName); + } + + for (auto &annotation : typeAnnotations) { + if (!annotation.typeId) + qWarning() << moduleName(annotation.moduleId).toQString() + << annotation.typeName.toQString(); + } + + typeAnnotations.erase(std::remove_if(typeAnnotations.begin(), + typeAnnotations.end(), + [](const auto &annotation) { return !annotation.typeId; }), + typeAnnotations.end()); +} + +void ProjectStorage::synchronizeTypeAnnotations(Storage::Synchronization::TypeAnnotations &typeAnnotations, + const SourceIds &updatedTypeAnnotationSourceIds) +{ + NanotraceHR::Tracer tracer{"synchronize type annotations"_t, projectStorageCategory()}; + + using Storage::Synchronization::TypeAnnotation; + + updateTypeIdInTypeAnnotations(typeAnnotations); + + auto compareKey = [](auto &&first, auto &&second) { return first.typeId - second.typeId; }; + + std::sort(typeAnnotations.begin(), typeAnnotations.end(), [&](auto &&first, auto &&second) { + return first.typeId < second.typeId; + }); + + auto range = selectTypeAnnotationsForSourceIdsStatement.range( + toIntegers(updatedTypeAnnotationSourceIds)); + + auto insert = [&](const TypeAnnotation &annotation) { + if (!annotation.sourceId) + throw TypeAnnotationHasInvalidSourceId{}; + + synchronizeTypeTraits(annotation.typeId, annotation.traits); + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert type annotations"_t, + projectStorageCategory(), + keyValue("type annotation", annotation)}; + + insertTypeAnnotationStatement.write(annotation.typeId, + annotation.sourceId, + annotation.directorySourceId, + annotation.iconPath, + createEmptyAsNull(annotation.itemLibraryJson), + createEmptyAsNull(annotation.hintsJson)); + }; + + auto update = [&](const TypeAnnotationView &annotationFromDatabase, + const TypeAnnotation &annotation) { + synchronizeTypeTraits(annotation.typeId, annotation.traits); + + if (annotationFromDatabase.iconPath != annotation.iconPath + || annotationFromDatabase.itemLibraryJson != annotation.itemLibraryJson + || annotationFromDatabase.hintsJson != annotation.hintsJson) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"update type annotations"_t, + projectStorageCategory(), + keyValue("type annotation from database", + annotationFromDatabase), + keyValue("type annotation", annotation)}; + + updateTypeAnnotationStatement.write(annotation.typeId, + annotation.iconPath, + createEmptyAsNull(annotation.itemLibraryJson), + createEmptyAsNull(annotation.hintsJson)); + return Sqlite::UpdateChange::Update; + } + + return Sqlite::UpdateChange::No; + }; + + auto remove = [&](const TypeAnnotationView &annotationFromDatabase) { + synchronizeTypeTraits(annotationFromDatabase.typeId, Storage::TypeTraits{}); + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"remove type annotations"_t, + projectStorageCategory(), + keyValue("type annotation", annotationFromDatabase)}; + + deleteTypeAnnotationStatement.write(annotationFromDatabase.typeId); + }; + + Sqlite::insertUpdateDelete(range, typeAnnotations, compareKey, insert, update, remove); +} + +void ProjectStorage::synchronizeTypeTrait(const Storage::Synchronization::Type &type) +{ + updateTypeTraitStatement.write(type.typeId, type.traits.type); +} + +void ProjectStorage::synchronizeTypes(Storage::Synchronization::Types &types, + TypeIds &updatedTypeIds, + AliasPropertyDeclarations &insertedAliasPropertyDeclarations, + AliasPropertyDeclarations &updatedAliasPropertyDeclarations, + AliasPropertyDeclarations &relinkableAliasPropertyDeclarations, + PropertyDeclarations &relinkablePropertyDeclarations, + Prototypes &relinkablePrototypes, + Prototypes &relinkableExtensions, + const SourceIds &updatedSourceIds) +{ + NanotraceHR::Tracer tracer{"synchronize types"_t, projectStorageCategory()}; + + Storage::Synchronization::ExportedTypes exportedTypes; + exportedTypes.reserve(types.size() * 3); + SourceIds sourceIdsOfTypes; + sourceIdsOfTypes.reserve(updatedSourceIds.size()); + SourceIds notUpdatedExportedSourceIds; + notUpdatedExportedSourceIds.reserve(updatedSourceIds.size()); + SourceIds exportedSourceIds; + exportedSourceIds.reserve(types.size()); + + for (auto &type : types) { + if (!type.sourceId) + throw TypeHasInvalidSourceId{}; + + TypeId typeId = declareType(type); + synchronizeTypeTrait(type); + sourceIdsOfTypes.push_back(type.sourceId); + updatedTypeIds.push_back(typeId); + if (type.changeLevel != Storage::Synchronization::ChangeLevel::ExcludeExportedTypes) { + exportedSourceIds.push_back(type.sourceId); + extractExportedTypes(typeId, type, exportedTypes); + } + } + + std::sort(types.begin(), types.end(), [](const auto &first, const auto &second) { + return first.typeId < second.typeId; + }); + + unique(exportedSourceIds); + + SourceIds sourceIdsWithoutType = filterSourceIdsWithoutType(updatedSourceIds, sourceIdsOfTypes); + exportedSourceIds.insert(exportedSourceIds.end(), + sourceIdsWithoutType.begin(), + sourceIdsWithoutType.end()); + TypeIds exportedTypeIds = fetchTypeIds(exportedSourceIds); + synchronizeExportedTypes(exportedTypeIds, + exportedTypes, + relinkableAliasPropertyDeclarations, + relinkablePropertyDeclarations, + relinkablePrototypes, + relinkableExtensions); + + syncPrototypesAndExtensions(types, relinkablePrototypes, relinkableExtensions); + resetDefaultPropertiesIfChanged(types); + resetRemovedAliasPropertyDeclarationsToNull(types, relinkableAliasPropertyDeclarations); + syncDeclarations(types, + insertedAliasPropertyDeclarations, + updatedAliasPropertyDeclarations, + relinkablePropertyDeclarations); + syncDefaultProperties(types); +} + +void ProjectStorage::synchronizeProjectDatas(Storage::Synchronization::ProjectDatas &projectDatas, + const SourceIds &updatedProjectSourceIds) +{ + NanotraceHR::Tracer tracer{"synchronize project datas"_t, projectStorageCategory()}; + + auto compareKey = [](auto &&first, auto &&second) { + auto projectSourceIdDifference = first.projectSourceId - second.projectSourceId; + if (projectSourceIdDifference != 0) + return projectSourceIdDifference; + + return first.sourceId - second.sourceId; + }; + + std::sort(projectDatas.begin(), projectDatas.end(), [&](auto &&first, auto &&second) { + return std::tie(first.projectSourceId, first.sourceId) + < std::tie(second.projectSourceId, second.sourceId); + }); + + auto range = selectProjectDatasForSourceIdsStatement.range( + toIntegers(updatedProjectSourceIds)); + + auto insert = [&](const Storage::Synchronization::ProjectData &projectData) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert project data"_t, + projectStorageCategory(), + keyValue("project data", projectData)}; + + if (!projectData.projectSourceId) + throw ProjectDataHasInvalidProjectSourceId{}; + if (!projectData.sourceId) + throw ProjectDataHasInvalidSourceId{}; + + insertProjectDataStatement.write(projectData.projectSourceId, + projectData.sourceId, + projectData.moduleId, + projectData.fileType); + }; + + auto update = [&](const Storage::Synchronization::ProjectData &projectDataFromDatabase, + const Storage::Synchronization::ProjectData &projectData) { + if (projectDataFromDatabase.fileType != projectData.fileType + || !compareInvalidAreTrue(projectDataFromDatabase.moduleId, projectData.moduleId)) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"update project data"_t, + projectStorageCategory(), + keyValue("project data", projectData), + keyValue("project data from database", projectDataFromDatabase)}; + + updateProjectDataStatement.write(projectData.projectSourceId, + projectData.sourceId, + projectData.moduleId, + projectData.fileType); + return Sqlite::UpdateChange::Update; + } + + return Sqlite::UpdateChange::No; + }; + + auto remove = [&](const Storage::Synchronization::ProjectData &projectData) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"remove project data"_t, + projectStorageCategory(), + keyValue("project data", projectData)}; + + deleteProjectDataStatement.write(projectData.projectSourceId, projectData.sourceId); + }; + + Sqlite::insertUpdateDelete(range, projectDatas, compareKey, insert, update, remove); +} + +void ProjectStorage::synchronizeFileStatuses(FileStatuses &fileStatuses, + const SourceIds &updatedSourceIds) +{ + NanotraceHR::Tracer tracer{"synchronize file statuses"_t, projectStorageCategory()}; + + auto compareKey = [](auto &&first, auto &&second) { return first.sourceId - second.sourceId; }; + + std::sort(fileStatuses.begin(), fileStatuses.end(), [&](auto &&first, auto &&second) { + return first.sourceId < second.sourceId; + }); + + auto range = selectFileStatusesForSourceIdsStatement.range( + toIntegers(updatedSourceIds)); + + auto insert = [&](const FileStatus &fileStatus) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert file status"_t, + projectStorageCategory(), + keyValue("file status", fileStatus)}; + + if (!fileStatus.sourceId) + throw FileStatusHasInvalidSourceId{}; + insertFileStatusStatement.write(fileStatus.sourceId, fileStatus.size, fileStatus.lastModified); + }; + + auto update = [&](const FileStatus &fileStatusFromDatabase, const FileStatus &fileStatus) { + if (fileStatusFromDatabase.lastModified != fileStatus.lastModified + || fileStatusFromDatabase.size != fileStatus.size) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"update file status"_t, + projectStorageCategory(), + keyValue("file status", fileStatus), + keyValue("file status from database", fileStatusFromDatabase)}; + + updateFileStatusStatement.write(fileStatus.sourceId, + fileStatus.size, + fileStatus.lastModified); + return Sqlite::UpdateChange::Update; + } + + return Sqlite::UpdateChange::No; + }; + + auto remove = [&](const FileStatus &fileStatus) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"remove file status"_t, + projectStorageCategory(), + keyValue("file status", fileStatus)}; + + deleteFileStatusStatement.write(fileStatus.sourceId); + }; + + Sqlite::insertUpdateDelete(range, fileStatuses, compareKey, insert, update, remove); +} + +void ProjectStorage::synchronizeImports(Storage::Imports &imports, + const SourceIds &updatedSourceIds, + Storage::Imports &moduleDependencies, + const SourceIds &updatedModuleDependencySourceIds, + Storage::Synchronization::ModuleExportedImports &moduleExportedImports, + const ModuleIds &updatedModuleIds) +{ + NanotraceHR::Tracer tracer{"synchronize imports"_t, projectStorageCategory()}; + + synchromizeModuleExportedImports(moduleExportedImports, updatedModuleIds); + NanotraceHR::Tracer importTracer{"synchronize qml document imports"_t, projectStorageCategory()}; + synchronizeDocumentImports(imports, updatedSourceIds, Storage::Synchronization::ImportKind::Import); + importTracer.end(); + NanotraceHR::Tracer moduleDependenciesTracer{"synchronize module depdencies"_t, + projectStorageCategory()}; + synchronizeDocumentImports(moduleDependencies, + updatedModuleDependencySourceIds, + Storage::Synchronization::ImportKind::ModuleDependency); + moduleDependenciesTracer.end(); +} + +void ProjectStorage::synchromizeModuleExportedImports( + Storage::Synchronization::ModuleExportedImports &moduleExportedImports, + const ModuleIds &updatedModuleIds) +{ + NanotraceHR::Tracer tracer{"synchronize module exported imports"_t, projectStorageCategory()}; + std::sort(moduleExportedImports.begin(), + moduleExportedImports.end(), + [](auto &&first, auto &&second) { + return std::tie(first.moduleId, first.exportedModuleId) + < std::tie(second.moduleId, second.exportedModuleId); + }); + + auto range = selectModuleExportedImportsForSourceIdStatement + .range( + toIntegers(updatedModuleIds)); + + auto compareKey = [](const Storage::Synchronization::ModuleExportedImportView &view, + const Storage::Synchronization::ModuleExportedImport &import) -> long long { + auto moduleIdDifference = view.moduleId - import.moduleId; + if (moduleIdDifference != 0) + return moduleIdDifference; + + return view.exportedModuleId - import.exportedModuleId; + }; + + auto insert = [&](const Storage::Synchronization::ModuleExportedImport &import) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert module exported import"_t, + projectStorageCategory(), + keyValue("module exported import", import), + keyValue("module id", import.moduleId)}; + tracer.tick("exported module"_t, keyValue("module id", import.exportedModuleId)); + + if (import.version.minor) { + insertModuleExportedImportWithVersionStatement.write(import.moduleId, + import.exportedModuleId, + import.isAutoVersion, + import.version.major.value, + import.version.minor.value); + } else if (import.version.major) { + insertModuleExportedImportWithMajorVersionStatement.write(import.moduleId, + import.exportedModuleId, + import.isAutoVersion, + import.version.major.value); + } else { + insertModuleExportedImportWithoutVersionStatement.write(import.moduleId, + import.exportedModuleId, + import.isAutoVersion); + } + }; + + auto update = [](const Storage::Synchronization::ModuleExportedImportView &, + const Storage::Synchronization::ModuleExportedImport &) { + return Sqlite::UpdateChange::No; + }; + + auto remove = [&](const Storage::Synchronization::ModuleExportedImportView &view) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"remove module exported import"_t, + projectStorageCategory(), + keyValue("module exported import view", view), + keyValue("module id", view.moduleId)}; + tracer.tick("exported module"_t, keyValue("module id", view.exportedModuleId)); + + deleteModuleExportedImportStatement.write(view.moduleExportedImportId); + }; + + Sqlite::insertUpdateDelete(range, moduleExportedImports, compareKey, insert, update, remove); +} + +ModuleId ProjectStorage::fetchModuleIdUnguarded(Utils::SmallStringView name) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch module id ungarded"_t, + projectStorageCategory(), + keyValue("module name", name)}; + + auto moduleId = selectModuleIdByNameStatement.value(name); + + if (!moduleId) + moduleId = insertModuleNameStatement.value(name); + + tracer.end(keyValue("module id", moduleId)); + + return moduleId; +} + +Utils::PathString ProjectStorage::fetchModuleNameUnguarded(ModuleId id) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch module name ungarded"_t, + projectStorageCategory(), + keyValue("module id", id)}; + + auto moduleName = selectModuleNameStatement.value(id); + + if (moduleName.empty()) + throw ModuleDoesNotExists{}; + + tracer.end(keyValue("module name", moduleName)); + + return moduleName; +} + +void ProjectStorage::handleAliasPropertyDeclarationsWithPropertyType( + TypeId typeId, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"handle alias property declarations with property type"_t, + projectStorageCategory(), + keyValue("type id", typeId), + keyValue("relinkable alias property declarations", + relinkableAliasPropertyDeclarations)}; + + auto callback = [&](TypeId typeId_, + PropertyDeclarationId propertyDeclarationId, + ImportedTypeNameId propertyImportedTypeNameId, + PropertyDeclarationId aliasPropertyDeclarationId, + PropertyDeclarationId aliasPropertyDeclarationTailId) { + auto aliasPropertyName = selectPropertyNameStatement.value( + aliasPropertyDeclarationId); + Utils::SmallString aliasPropertyNameTail; + if (aliasPropertyDeclarationTailId) + aliasPropertyNameTail = selectPropertyNameStatement.value( + aliasPropertyDeclarationTailId); + + relinkableAliasPropertyDeclarations.emplace_back(TypeId{typeId_}, + PropertyDeclarationId{propertyDeclarationId}, + ImportedTypeNameId{propertyImportedTypeNameId}, + std::move(aliasPropertyName), + std::move(aliasPropertyNameTail)); + + updateAliasPropertyDeclarationToNullStatement.write(propertyDeclarationId); + }; + + selectAliasPropertiesDeclarationForPropertiesWithTypeIdStatement.readCallback(callback, typeId); +} + +void ProjectStorage::handlePropertyDeclarationWithPropertyType( + TypeId typeId, PropertyDeclarations &relinkablePropertyDeclarations) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"handle property declarations with property type"_t, + projectStorageCategory(), + keyValue("type id", typeId), + keyValue("relinkable property declarations", + relinkablePropertyDeclarations)}; + + updatesPropertyDeclarationPropertyTypeToNullStatement.readTo(relinkablePropertyDeclarations, + typeId); +} + +void ProjectStorage::handlePrototypes(TypeId prototypeId, Prototypes &relinkablePrototypes) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"handle prototypes"_t, + projectStorageCategory(), + keyValue("type id", prototypeId), + keyValue("relinkable prototypes", relinkablePrototypes)}; + + auto callback = [&](TypeId typeId, ImportedTypeNameId prototypeNameId) { + relinkablePrototypes.emplace_back(typeId, prototypeNameId); + }; + + updatePrototypeIdToNullStatement.readCallback(callback, prototypeId); +} + +void ProjectStorage::handleExtensions(TypeId extensionId, Prototypes &relinkableExtensions) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"handle extension"_t, + projectStorageCategory(), + keyValue("type id", extensionId), + keyValue("relinkable extensions", relinkableExtensions)}; + + auto callback = [&](TypeId typeId, ImportedTypeNameId extensionNameId) { + relinkableExtensions.emplace_back(typeId, extensionNameId); + }; + + updateExtensionIdToNullStatement.readCallback(callback, extensionId); +} + +void ProjectStorage::deleteType(TypeId typeId, + AliasPropertyDeclarations &relinkableAliasPropertyDeclarations, + PropertyDeclarations &relinkablePropertyDeclarations, + Prototypes &relinkablePrototypes, + Prototypes &relinkableExtensions) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"delete type"_t, projectStorageCategory(), keyValue("type id", typeId)}; + + handlePropertyDeclarationWithPropertyType(typeId, relinkablePropertyDeclarations); + handleAliasPropertyDeclarationsWithPropertyType(typeId, relinkableAliasPropertyDeclarations); + handlePrototypes(typeId, relinkablePrototypes); + handleExtensions(typeId, relinkableExtensions); + deleteTypeNamesByTypeIdStatement.write(typeId); + deleteEnumerationDeclarationByTypeIdStatement.write(typeId); + deletePropertyDeclarationByTypeIdStatement.write(typeId); + deleteFunctionDeclarationByTypeIdStatement.write(typeId); + deleteSignalDeclarationByTypeIdStatement.write(typeId); + deleteTypeStatement.write(typeId); +} + +void ProjectStorage::relinkAliasPropertyDeclarations(AliasPropertyDeclarations &aliasPropertyDeclarations, + const TypeIds &deletedTypeIds) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"relink alias properties"_t, + projectStorageCategory(), + keyValue("alias property declarations", aliasPropertyDeclarations), + keyValue("deleted type ids", deletedTypeIds)}; + + std::sort(aliasPropertyDeclarations.begin(), aliasPropertyDeclarations.end()); + + Utils::set_greedy_difference( + aliasPropertyDeclarations.cbegin(), + aliasPropertyDeclarations.cend(), + deletedTypeIds.begin(), + deletedTypeIds.end(), + [&](const AliasPropertyDeclaration &alias) { + auto typeId = fetchTypeId(alias.aliasImportedTypeNameId); + + if (!typeId) + throw TypeNameDoesNotExists{fetchImportedTypeName(alias.aliasImportedTypeNameId)}; + + auto [propertyTypeId, aliasId, propertyTraits] = fetchPropertyDeclarationByTypeIdAndNameUngarded( + typeId, alias.aliasPropertyName); + + updatePropertyDeclarationWithAliasAndTypeStatement.write(alias.propertyDeclarationId, + propertyTypeId, + propertyTraits, + alias.aliasImportedTypeNameId, + aliasId); + }, + TypeCompare{}); +} + +void ProjectStorage::relinkPropertyDeclarations(PropertyDeclarations &relinkablePropertyDeclaration, + const TypeIds &deletedTypeIds) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"relink property declarations"_t, + projectStorageCategory(), + keyValue("relinkable property declarations", + relinkablePropertyDeclaration), + keyValue("deleted type ids", deletedTypeIds)}; + + std::sort(relinkablePropertyDeclaration.begin(), relinkablePropertyDeclaration.end()); + + Utils::set_greedy_difference( + relinkablePropertyDeclaration.cbegin(), + relinkablePropertyDeclaration.cend(), + deletedTypeIds.begin(), + deletedTypeIds.end(), + [&](const PropertyDeclaration &property) { + TypeId propertyTypeId = fetchTypeId(property.importedTypeNameId); + + if (!propertyTypeId) + throw TypeNameDoesNotExists{fetchImportedTypeName(property.importedTypeNameId)}; + + updatePropertyDeclarationTypeStatement.write(property.propertyDeclarationId, + propertyTypeId); + }, + TypeCompare{}); +} + +void ProjectStorage::deleteNotUpdatedTypes(const TypeIds &updatedTypeIds, + const SourceIds &updatedSourceIds, + const TypeIds &typeIdsToBeDeleted, + AliasPropertyDeclarations &relinkableAliasPropertyDeclarations, + PropertyDeclarations &relinkablePropertyDeclarations, + Prototypes &relinkablePrototypes, + Prototypes &relinkableExtensions, + TypeIds &deletedTypeIds) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"delete not updated types"_t, + projectStorageCategory(), + keyValue("updated type ids", updatedTypeIds), + keyValue("updated source ids", updatedSourceIds), + keyValue("type ids to be deleted", typeIdsToBeDeleted)}; + + auto callback = [&](TypeId typeId) { + deletedTypeIds.push_back(typeId); + deleteType(typeId, + relinkableAliasPropertyDeclarations, + relinkablePropertyDeclarations, + relinkablePrototypes, + relinkableExtensions); + }; + + selectNotUpdatedTypesInSourcesStatement.readCallback(callback, + toIntegers(updatedSourceIds), + toIntegers(updatedTypeIds)); + for (TypeId typeIdToBeDeleted : typeIdsToBeDeleted) + callback(typeIdToBeDeleted); +} + +void ProjectStorage::relink(AliasPropertyDeclarations &relinkableAliasPropertyDeclarations, + PropertyDeclarations &relinkablePropertyDeclarations, + Prototypes &relinkablePrototypes, + Prototypes &relinkableExtensions, + TypeIds &deletedTypeIds) +{ + NanotraceHR::Tracer tracer{"relink"_t, projectStorageCategory()}; + + std::sort(deletedTypeIds.begin(), deletedTypeIds.end()); + + relinkPrototypes(relinkablePrototypes, deletedTypeIds, [&](TypeId typeId, TypeId prototypeId) { + updateTypePrototypeStatement.write(typeId, prototypeId); + }); + relinkPrototypes(relinkableExtensions, deletedTypeIds, [&](TypeId typeId, TypeId prototypeId) { + updateTypeExtensionStatement.write(typeId, prototypeId); + }); + relinkPropertyDeclarations(relinkablePropertyDeclarations, deletedTypeIds); + relinkAliasPropertyDeclarations(relinkableAliasPropertyDeclarations, deletedTypeIds); +} + +PropertyDeclarationId ProjectStorage::fetchAliasId(TypeId aliasTypeId, + Utils::SmallStringView aliasPropertyName, + Utils::SmallStringView aliasPropertyNameTail) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch alias id"_t, + projectStorageCategory(), + keyValue("alias type id", aliasTypeId), + keyValue("alias property name", aliasPropertyName), + keyValue("alias property name tail", aliasPropertyNameTail)}; + + if (aliasPropertyNameTail.empty()) + return fetchPropertyDeclarationIdByTypeIdAndNameUngarded(aliasTypeId, aliasPropertyName); + + auto stemAlias = fetchPropertyDeclarationByTypeIdAndNameUngarded(aliasTypeId, aliasPropertyName); + + return fetchPropertyDeclarationIdByTypeIdAndNameUngarded(stemAlias.propertyTypeId, + aliasPropertyNameTail); +} + +void ProjectStorage::linkAliasPropertyDeclarationAliasIds(const AliasPropertyDeclarations &aliasDeclarations) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"link alias property declarations alias ids"_t, + projectStorageCategory(), + keyValue("alias property declarations", aliasDeclarations)}; + + for (const auto &aliasDeclaration : aliasDeclarations) { + auto aliasTypeId = fetchTypeId(aliasDeclaration.aliasImportedTypeNameId); + + if (!aliasTypeId) { + throw TypeNameDoesNotExists{ + fetchImportedTypeName(aliasDeclaration.aliasImportedTypeNameId)}; + } + + auto aliasId = fetchAliasId(aliasTypeId, + aliasDeclaration.aliasPropertyName, + aliasDeclaration.aliasPropertyNameTail); + + updatePropertyDeclarationAliasIdAndTypeNameIdStatement.write( + aliasDeclaration.propertyDeclarationId, aliasId, aliasDeclaration.aliasImportedTypeNameId); + } +} + +void ProjectStorage::updateAliasPropertyDeclarationValues(const AliasPropertyDeclarations &aliasDeclarations) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"update alias property declarations"_t, + projectStorageCategory(), + keyValue("alias property declarations", aliasDeclarations)}; + + for (const auto &aliasDeclaration : aliasDeclarations) { + updatetPropertiesDeclarationValuesOfAliasStatement.write( + aliasDeclaration.propertyDeclarationId); + updatePropertyAliasDeclarationRecursivelyStatement.write( + aliasDeclaration.propertyDeclarationId); + } +} + +void ProjectStorage::checkAliasPropertyDeclarationCycles(const AliasPropertyDeclarations &aliasDeclarations) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"check alias property declarations cycles"_t, + projectStorageCategory(), + keyValue("alias property declarations", aliasDeclarations)}; + for (const auto &aliasDeclaration : aliasDeclarations) + checkForAliasChainCycle(aliasDeclaration.propertyDeclarationId); +} + +void ProjectStorage::linkAliases(const AliasPropertyDeclarations &insertedAliasPropertyDeclarations, + const AliasPropertyDeclarations &updatedAliasPropertyDeclarations) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"link aliases"_t, projectStorageCategory()}; + + linkAliasPropertyDeclarationAliasIds(insertedAliasPropertyDeclarations); + linkAliasPropertyDeclarationAliasIds(updatedAliasPropertyDeclarations); + + checkAliasPropertyDeclarationCycles(insertedAliasPropertyDeclarations); + checkAliasPropertyDeclarationCycles(updatedAliasPropertyDeclarations); + + updateAliasPropertyDeclarationValues(insertedAliasPropertyDeclarations); + updateAliasPropertyDeclarationValues(updatedAliasPropertyDeclarations); +} + +void ProjectStorage::synchronizeExportedTypes(const TypeIds &updatedTypeIds, + Storage::Synchronization::ExportedTypes &exportedTypes, + AliasPropertyDeclarations &relinkableAliasPropertyDeclarations, + PropertyDeclarations &relinkablePropertyDeclarations, + Prototypes &relinkablePrototypes, + Prototypes &relinkableExtensions) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"synchronize exported types"_t, projectStorageCategory()}; + + std::sort(exportedTypes.begin(), exportedTypes.end(), [](auto &&first, auto &&second) { + if (first.moduleId < second.moduleId) + return true; + else if (first.moduleId > second.moduleId) + return false; + + auto nameCompare = Sqlite::compare(first.name, second.name); + + if (nameCompare < 0) + return true; + else if (nameCompare > 0) + return false; + + return first.version < second.version; + }); + + auto range = selectExportedTypesForSourceIdsStatement + .range(toIntegers(updatedTypeIds)); + + auto compareKey = [](const Storage::Synchronization::ExportedTypeView &view, + const Storage::Synchronization::ExportedType &type) -> long long { + auto moduleIdDifference = view.moduleId - type.moduleId; + if (moduleIdDifference != 0) + return moduleIdDifference; + + auto nameDifference = Sqlite::compare(view.name, type.name); + if (nameDifference != 0) + return nameDifference; + + auto versionDifference = view.version.major.value - type.version.major.value; + if (versionDifference != 0) + return versionDifference; + + return view.version.minor.value - type.version.minor.value; + }; + + auto insert = [&](const Storage::Synchronization::ExportedType &type) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert exported type"_t, + projectStorageCategory(), + keyValue("exported type", type), + keyValue("type id", type.typeId), + keyValue("module id", type.moduleId)}; + if (!type.moduleId) + throw QmlDesigner::ModuleDoesNotExists{}; + + try { + if (type.version) { + insertExportedTypeNamesWithVersionStatement.write(type.moduleId, + type.name, + type.version.major.value, + type.version.minor.value, + type.typeId); + + } else if (type.version.major) { + insertExportedTypeNamesWithMajorVersionStatement.write(type.moduleId, + type.name, + type.version.major.value, + type.typeId); + } else { + insertExportedTypeNamesWithoutVersionStatement.write(type.moduleId, + type.name, + type.typeId); + } + } catch (const Sqlite::ConstraintPreventsModification &) { + throw QmlDesigner::ExportedTypeCannotBeInserted{type.name}; + } + }; + + auto update = [&](const Storage::Synchronization::ExportedTypeView &view, + const Storage::Synchronization::ExportedType &type) { + if (view.typeId != type.typeId) { + NanotraceHR::Tracer tracer{"update exported type"_t, + projectStorageCategory(), + keyValue("exported type", type), + keyValue("exported type view", view), + keyValue("type id", type.typeId), + keyValue("module id", type.typeId)}; + + handlePropertyDeclarationWithPropertyType(view.typeId, relinkablePropertyDeclarations); + handleAliasPropertyDeclarationsWithPropertyType(view.typeId, + relinkableAliasPropertyDeclarations); + handlePrototypes(view.typeId, relinkablePrototypes); + handleExtensions(view.typeId, relinkableExtensions); + updateExportedTypeNameTypeIdStatement.write(view.exportedTypeNameId, type.typeId); + return Sqlite::UpdateChange::Update; + } + return Sqlite::UpdateChange::No; + }; + + auto remove = [&](const Storage::Synchronization::ExportedTypeView &view) { + NanotraceHR::Tracer tracer{"remove exported type"_t, + projectStorageCategory(), + keyValue("exported type", view), + keyValue("type id", view.typeId), + keyValue("module id", view.moduleId)}; + + handlePropertyDeclarationWithPropertyType(view.typeId, relinkablePropertyDeclarations); + handleAliasPropertyDeclarationsWithPropertyType(view.typeId, + relinkableAliasPropertyDeclarations); + handlePrototypes(view.typeId, relinkablePrototypes); + handleExtensions(view.typeId, relinkableExtensions); + deleteExportedTypeNameStatement.write(view.exportedTypeNameId); + }; + + Sqlite::insertUpdateDelete(range, exportedTypes, compareKey, insert, update, remove); +} + +void ProjectStorage::synchronizePropertyDeclarationsInsertAlias( + AliasPropertyDeclarations &insertedAliasPropertyDeclarations, + const Storage::Synchronization::PropertyDeclaration &value, + SourceId sourceId, + TypeId typeId) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert property declaration to alias"_t, + projectStorageCategory(), + keyValue("property declaration", value)}; + + auto callback = [&](PropertyDeclarationId propertyDeclarationId) { + insertedAliasPropertyDeclarations.emplace_back(typeId, + propertyDeclarationId, + fetchImportedTypeNameId(value.typeName, + sourceId), + value.aliasPropertyName, + value.aliasPropertyNameTail); + return Sqlite::CallbackControl::Abort; + }; + + insertAliasPropertyDeclarationStatement.readCallback(callback, typeId, value.name); +} + +QVarLengthArray ProjectStorage::fetchPropertyDeclarationIds( + TypeId baseTypeId) const +{ + QVarLengthArray propertyDeclarationIds; + + selectLocalPropertyDeclarationIdsForTypeStatement.readTo(propertyDeclarationIds, baseTypeId); + + auto range = selectPrototypeAndExtensionIdsStatement.range(baseTypeId); + + for (TypeId prototype : range) { + selectLocalPropertyDeclarationIdsForTypeStatement.readTo(propertyDeclarationIds, prototype); + } + + return propertyDeclarationIds; +} + +PropertyDeclarationId ProjectStorage::fetchNextPropertyDeclarationId( + TypeId baseTypeId, Utils::SmallStringView propertyName) const +{ + auto range = selectPrototypeAndExtensionIdsStatement.range(baseTypeId); + + for (TypeId prototype : range) { + auto propertyDeclarationId = selectPropertyDeclarationIdByTypeIdAndNameStatement + .value(prototype, propertyName); + + if (propertyDeclarationId) + return propertyDeclarationId; + } + + return PropertyDeclarationId{}; +} + +PropertyDeclarationId ProjectStorage::fetchPropertyDeclarationId(TypeId typeId, + Utils::SmallStringView propertyName) const +{ + auto propertyDeclarationId = selectPropertyDeclarationIdByTypeIdAndNameStatement + .value(typeId, propertyName); + + if (propertyDeclarationId) + return propertyDeclarationId; + + return fetchNextPropertyDeclarationId(typeId, propertyName); +} + +PropertyDeclarationId ProjectStorage::fetchNextDefaultPropertyDeclarationId(TypeId baseTypeId) const +{ + auto range = selectPrototypeAndExtensionIdsStatement.range(baseTypeId); + + for (TypeId prototype : range) { + auto propertyDeclarationId = selectDefaultPropertyDeclarationIdStatement + .value(prototype); + + if (propertyDeclarationId) + return propertyDeclarationId; + } + + return PropertyDeclarationId{}; +} + +PropertyDeclarationId ProjectStorage::fetchDefaultPropertyDeclarationId(TypeId typeId) const +{ + auto propertyDeclarationId = selectDefaultPropertyDeclarationIdStatement.value( + typeId); + + if (propertyDeclarationId) + return propertyDeclarationId; + + return fetchNextDefaultPropertyDeclarationId(typeId); +} + +void ProjectStorage::synchronizePropertyDeclarationsInsertProperty( + const Storage::Synchronization::PropertyDeclaration &value, SourceId sourceId, TypeId typeId) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert property declaration"_t, + projectStorageCategory(), + keyValue("property declaration", value)}; + + auto propertyImportedTypeNameId = fetchImportedTypeNameId(value.typeName, sourceId); + auto propertyTypeId = fetchTypeId(propertyImportedTypeNameId); + + if (!propertyTypeId) + throw TypeNameDoesNotExists{fetchImportedTypeName(propertyImportedTypeNameId), sourceId}; + + auto propertyDeclarationId = insertPropertyDeclarationStatement.value( + typeId, value.name, propertyTypeId, value.traits, propertyImportedTypeNameId); + + auto nextPropertyDeclarationId = fetchNextPropertyDeclarationId(typeId, value.name); + if (nextPropertyDeclarationId) { + updateAliasIdPropertyDeclarationStatement.write(nextPropertyDeclarationId, + propertyDeclarationId); + updatePropertyAliasDeclarationRecursivelyWithTypeAndTraitsStatement.write(propertyDeclarationId, + propertyTypeId, + value.traits); + } +} + +void ProjectStorage::synchronizePropertyDeclarationsUpdateAlias( + AliasPropertyDeclarations &updatedAliasPropertyDeclarations, + const Storage::Synchronization::PropertyDeclarationView &view, + const Storage::Synchronization::PropertyDeclaration &value, + SourceId sourceId) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"update property declaration to alias"_t, + projectStorageCategory(), + keyValue("property declaration", value), + keyValue("property declaration view", view)}; + + updatedAliasPropertyDeclarations.emplace_back(view.typeId, + view.id, + fetchImportedTypeNameId(value.typeName, sourceId), + value.aliasPropertyName, + value.aliasPropertyNameTail, + view.aliasId); +} + +Sqlite::UpdateChange ProjectStorage::synchronizePropertyDeclarationsUpdateProperty( + const Storage::Synchronization::PropertyDeclarationView &view, + const Storage::Synchronization::PropertyDeclaration &value, + SourceId sourceId, + PropertyDeclarationIds &propertyDeclarationIds) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"update property declaration"_t, + projectStorageCategory(), + keyValue("property declaration", value), + keyValue("property declaration view", view)}; + + auto propertyImportedTypeNameId = fetchImportedTypeNameId(value.typeName, sourceId); + + auto propertyTypeId = fetchTypeId(propertyImportedTypeNameId); + + if (!propertyTypeId) + throw TypeNameDoesNotExists{fetchImportedTypeName(propertyImportedTypeNameId), sourceId}; + + if (view.traits == value.traits && propertyTypeId == view.typeId + && propertyImportedTypeNameId == view.typeNameId) + return Sqlite::UpdateChange::No; + + updatePropertyDeclarationStatement.write(view.id, + propertyTypeId, + value.traits, + propertyImportedTypeNameId); + updatePropertyAliasDeclarationRecursivelyWithTypeAndTraitsStatement.write(view.id, + propertyTypeId, + value.traits); + propertyDeclarationIds.push_back(view.id); + + tracer.end(keyValue("updated", "yes")); + + return Sqlite::UpdateChange::Update; +} + +void ProjectStorage::synchronizePropertyDeclarations( + TypeId typeId, + Storage::Synchronization::PropertyDeclarations &propertyDeclarations, + SourceId sourceId, + AliasPropertyDeclarations &insertedAliasPropertyDeclarations, + AliasPropertyDeclarations &updatedAliasPropertyDeclarations, + PropertyDeclarationIds &propertyDeclarationIds) +{ + NanotraceHR::Tracer tracer{"synchronize property declaration"_t, projectStorageCategory()}; + + std::sort(propertyDeclarations.begin(), propertyDeclarations.end(), [](auto &&first, auto &&second) { + return Sqlite::compare(first.name, second.name) < 0; + }); + + auto range = selectPropertyDeclarationsForTypeIdStatement + .range(typeId); + + auto compareKey = [](const Storage::Synchronization::PropertyDeclarationView &view, + const Storage::Synchronization::PropertyDeclaration &value) { + return Sqlite::compare(view.name, value.name); + }; + + auto insert = [&](const Storage::Synchronization::PropertyDeclaration &value) { + if (value.kind == Storage::Synchronization::PropertyKind::Alias) { + synchronizePropertyDeclarationsInsertAlias(insertedAliasPropertyDeclarations, + value, + sourceId, + typeId); + } else { + synchronizePropertyDeclarationsInsertProperty(value, sourceId, typeId); + } + }; + + auto update = [&](const Storage::Synchronization::PropertyDeclarationView &view, + const Storage::Synchronization::PropertyDeclaration &value) { + if (value.kind == Storage::Synchronization::PropertyKind::Alias) { + synchronizePropertyDeclarationsUpdateAlias(updatedAliasPropertyDeclarations, + view, + value, + sourceId); + propertyDeclarationIds.push_back(view.id); + } else { + return synchronizePropertyDeclarationsUpdateProperty(view, + value, + sourceId, + propertyDeclarationIds); + } + + return Sqlite::UpdateChange::No; + }; + + auto remove = [&](const Storage::Synchronization::PropertyDeclarationView &view) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"remove property declaration"_t, + projectStorageCategory(), + keyValue("property declaratio viewn", view)}; + + auto nextPropertyDeclarationId = fetchNextPropertyDeclarationId(typeId, view.name); + + if (nextPropertyDeclarationId) { + updateAliasPropertyDeclarationByAliasPropertyDeclarationIdStatement + .write(nextPropertyDeclarationId, view.id); + } + + updateDefaultPropertyIdToNullStatement.write(view.id); + deletePropertyDeclarationStatement.write(view.id); + propertyDeclarationIds.push_back(view.id); + }; + + Sqlite::insertUpdateDelete(range, propertyDeclarations, compareKey, insert, update, remove); +} + +void ProjectStorage::resetRemovedAliasPropertyDeclarationsToNull( + Storage::Synchronization::Type &type, PropertyDeclarationIds &propertyDeclarationIds) +{ + NanotraceHR::Tracer tracer{"reset removed alias property declaration to null"_t, + projectStorageCategory()}; + + if (type.changeLevel == Storage::Synchronization::ChangeLevel::Minimal) + return; + + Storage::Synchronization::PropertyDeclarations &aliasDeclarations = type.propertyDeclarations; + + std::sort(aliasDeclarations.begin(), aliasDeclarations.end(), [](auto &&first, auto &&second) { + return Sqlite::compare(first.name, second.name) < 0; + }); + + auto range = selectPropertyDeclarationsWithAliasForTypeIdStatement + .range(type.typeId); + + auto compareKey = [](const AliasPropertyDeclarationView &view, + const Storage::Synchronization::PropertyDeclaration &value) { + return Sqlite::compare(view.name, value.name); + }; + + auto insert = [&](const Storage::Synchronization::PropertyDeclaration &) {}; + + auto update = [&](const AliasPropertyDeclarationView &, + const Storage::Synchronization::PropertyDeclaration &) { + return Sqlite::UpdateChange::No; + }; + + auto remove = [&](const AliasPropertyDeclarationView &view) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"reset removed alias property declaration to null"_t, + projectStorageCategory(), + keyValue("alias property declaration view", view)}; + + updatePropertyDeclarationAliasIdToNullStatement.write(view.id); + propertyDeclarationIds.push_back(view.id); + }; + + Sqlite::insertUpdateDelete(range, aliasDeclarations, compareKey, insert, update, remove); +} + +void ProjectStorage::resetRemovedAliasPropertyDeclarationsToNull( + Storage::Synchronization::Types &types, + AliasPropertyDeclarations &relinkableAliasPropertyDeclarations) +{ + NanotraceHR::Tracer tracer{"reset removed alias properties to null"_t, projectStorageCategory()}; + + PropertyDeclarationIds propertyDeclarationIds; + propertyDeclarationIds.reserve(types.size()); + + for (auto &&type : types) + resetRemovedAliasPropertyDeclarationsToNull(type, propertyDeclarationIds); + + removeRelinkableEntries(relinkableAliasPropertyDeclarations, + propertyDeclarationIds, + PropertyCompare{}); +} + +ImportId ProjectStorage::insertDocumentImport(const Storage::Import &import, + Storage::Synchronization::ImportKind importKind, + ModuleId sourceModuleId, + ImportId parentImportId) +{ + if (import.version.minor) { + return insertDocumentImportWithVersionStatement.value(import.sourceId, + import.moduleId, + sourceModuleId, + importKind, + import.version.major.value, + import.version.minor.value, + parentImportId); + } else if (import.version.major) { + return insertDocumentImportWithMajorVersionStatement.value(import.sourceId, + import.moduleId, + sourceModuleId, + importKind, + import.version.major.value, + parentImportId); + } else { + return insertDocumentImportWithoutVersionStatement.value(import.sourceId, + import.moduleId, + sourceModuleId, + importKind, + parentImportId); + } +} + +void ProjectStorage::synchronizeDocumentImports(Storage::Imports &imports, + const SourceIds &updatedSourceIds, + Storage::Synchronization::ImportKind importKind) +{ + std::sort(imports.begin(), imports.end(), [](auto &&first, auto &&second) { + return std::tie(first.sourceId, first.moduleId, first.version) + < std::tie(second.sourceId, second.moduleId, second.version); + }); + + auto range = selectDocumentImportForSourceIdStatement.range( + toIntegers(updatedSourceIds), importKind); + + auto compareKey = [](const Storage::Synchronization::ImportView &view, + const Storage::Import &import) -> long long { + auto sourceIdDifference = view.sourceId - import.sourceId; + if (sourceIdDifference != 0) + return sourceIdDifference; + + auto moduleIdDifference = view.moduleId - import.moduleId; + if (moduleIdDifference != 0) + return moduleIdDifference; + + auto versionDifference = view.version.major.value - import.version.major.value; + if (versionDifference != 0) + return versionDifference; + + return view.version.minor.value - import.version.minor.value; + }; + + auto insert = [&](const Storage::Import &import) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert import"_t, + projectStorageCategory(), + keyValue("import", import), + keyValue("import kind", importKind), + keyValue("source id", import.sourceId), + keyValue("module id", import.moduleId)}; + + auto importId = insertDocumentImport(import, importKind, import.moduleId, ImportId{}); + auto callback = [&](ModuleId exportedModuleId, int majorVersion, int minorVersion) { + Storage::Import additionImport{exportedModuleId, + Storage::Version{majorVersion, minorVersion}, + import.sourceId}; + + auto exportedImportKind = importKind == Storage::Synchronization::ImportKind::Import + ? Storage::Synchronization::ImportKind::ModuleExportedImport + : Storage::Synchronization::ImportKind::ModuleExportedModuleDependency; + + NanotraceHR::Tracer tracer{"insert indirect import"_t, + projectStorageCategory(), + keyValue("import", import), + keyValue("import kind", exportedImportKind), + keyValue("source id", import.sourceId), + keyValue("module id", import.moduleId)}; + + auto indirectImportId = insertDocumentImport(additionImport, + exportedImportKind, + import.moduleId, + importId); + + tracer.end(keyValue("import id", indirectImportId)); + }; + + selectModuleExportedImportsForModuleIdStatement.readCallback(callback, + import.moduleId, + import.version.major.value, + import.version.minor.value); + tracer.end(keyValue("import id", importId)); + }; + + auto update = [](const Storage::Synchronization::ImportView &, const Storage::Import &) { + return Sqlite::UpdateChange::No; + }; + + auto remove = [&](const Storage::Synchronization::ImportView &view) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"remove import"_t, + projectStorageCategory(), + keyValue("import", view), + keyValue("import id", view.importId), + keyValue("source id", view.sourceId), + keyValue("module id", view.moduleId)}; + + deleteDocumentImportStatement.write(view.importId); + deleteDocumentImportsWithParentImportIdStatement.write(view.sourceId, view.importId); + }; + + Sqlite::insertUpdateDelete(range, imports, compareKey, insert, update, remove); +} + +Utils::PathString ProjectStorage::createJson(const Storage::Synchronization::ParameterDeclarations ¶meters) +{ + NanotraceHR::Tracer tracer{"create json from parameter declarations"_t, projectStorageCategory()}; + + Utils::PathString json; + json.append("["); + + Utils::SmallStringView comma{""}; + + for (const auto ¶meter : parameters) { + json.append(comma); + comma = ","; + json.append(R"({"n":")"); + json.append(parameter.name); + json.append(R"(","tn":")"); + json.append(parameter.typeName); + if (parameter.traits == Storage::PropertyDeclarationTraits::None) { + json.append("\"}"); + } else { + json.append(R"(","tr":)"); + json.append(Utils::SmallString::number(to_underlying(parameter.traits))); + json.append("}"); + } + } + + json.append("]"); + + return json; +} + +TypeId ProjectStorage::fetchTypeIdByModuleIdAndExportedName(ModuleId moduleId, + Utils::SmallStringView name) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch type id by module id and exported name"_t, + projectStorageCategory(), + keyValue("module id", moduleId), + keyValue("exported name", name)}; + + return selectTypeIdByModuleIdAndExportedNameStatement.value(moduleId, name); +} + +void ProjectStorage::addTypeIdToPropertyEditorQmlPaths( + Storage::Synchronization::PropertyEditorQmlPaths &paths) +{ + NanotraceHR::Tracer tracer{"add type id to property editor qml paths"_t, projectStorageCategory()}; + + for (auto &path : paths) + path.typeId = fetchTypeIdByModuleIdAndExportedName(path.moduleId, path.typeName); +} + +void ProjectStorage::synchronizePropertyEditorPaths(Storage::Synchronization::PropertyEditorQmlPaths &paths, + SourceIds updatedPropertyEditorQmlPathsSourceIds) +{ + using Storage::Synchronization::PropertyEditorQmlPath; + std::sort(paths.begin(), paths.end(), [](auto &&first, auto &&second) { + return first.typeId < second.typeId; + }); + + auto range = selectPropertyEditorPathsForForSourceIdsStatement.range( + toIntegers(updatedPropertyEditorQmlPathsSourceIds)); + + auto compareKey = [](const PropertyEditorQmlPathView &view, + const PropertyEditorQmlPath &value) -> long long { + return view.typeId - value.typeId; + }; + + auto insert = [&](const PropertyEditorQmlPath &path) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert property editor paths"_t, + projectStorageCategory(), + keyValue("property editor qml path", path)}; + + if (path.typeId) + insertPropertyEditorPathStatement.write(path.typeId, path.pathId, path.directoryId); + }; + + auto update = [&](const PropertyEditorQmlPathView &view, const PropertyEditorQmlPath &value) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"update property editor paths"_t, + projectStorageCategory(), + keyValue("property editor qml path", value), + keyValue("property editor qml path view", view)}; + + if (value.pathId != view.pathId || value.directoryId != view.directoryId) { + updatePropertyEditorPathsStatement.write(value.typeId, value.pathId, value.directoryId); + + tracer.end(keyValue("updated", "yes")); + + return Sqlite::UpdateChange::Update; + } + return Sqlite::UpdateChange::No; + }; + + auto remove = [&](const PropertyEditorQmlPathView &view) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"remove property editor paths"_t, + projectStorageCategory(), + keyValue("property editor qml path view", view)}; + + deletePropertyEditorPathStatement.write(view.typeId); + }; + + Sqlite::insertUpdateDelete(range, paths, compareKey, insert, update, remove); +} + +void ProjectStorage::synchronizePropertyEditorQmlPaths( + Storage::Synchronization::PropertyEditorQmlPaths &paths, + SourceIds updatedPropertyEditorQmlPathsSourceIds) +{ + NanotraceHR::Tracer tracer{"synchronize property editor qml paths"_t, projectStorageCategory()}; + + addTypeIdToPropertyEditorQmlPaths(paths); + synchronizePropertyEditorPaths(paths, updatedPropertyEditorQmlPathsSourceIds); +} + +void ProjectStorage::synchronizeFunctionDeclarations( + TypeId typeId, Storage::Synchronization::FunctionDeclarations &functionsDeclarations) +{ + NanotraceHR::Tracer tracer{"synchronize function declaration"_t, projectStorageCategory()}; + + std::sort(functionsDeclarations.begin(), + functionsDeclarations.end(), + [](auto &&first, auto &&second) { + auto compare = Sqlite::compare(first.name, second.name); + + if (compare == 0) { + Utils::PathString firstSignature{createJson(first.parameters)}; + Utils::PathString secondSignature{createJson(second.parameters)}; + + return Sqlite::compare(firstSignature, secondSignature) < 0; + } + + return compare < 0; + }); + + auto range = selectFunctionDeclarationsForTypeIdStatement + .range(typeId); + + auto compareKey = [](const Storage::Synchronization::FunctionDeclarationView &view, + const Storage::Synchronization::FunctionDeclaration &value) { + auto nameKey = Sqlite::compare(view.name, value.name); + if (nameKey != 0) + return nameKey; + + Utils::PathString valueSignature{createJson(value.parameters)}; + + return Sqlite::compare(view.signature, valueSignature); + }; + + auto insert = [&](const Storage::Synchronization::FunctionDeclaration &value) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert function declaration"_t, + projectStorageCategory(), + keyValue("function declaration", value)}; + + Utils::PathString signature{createJson(value.parameters)}; + + insertFunctionDeclarationStatement.write(typeId, value.name, value.returnTypeName, signature); + }; + + auto update = [&](const Storage::Synchronization::FunctionDeclarationView &view, + const Storage::Synchronization::FunctionDeclaration &value) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"update function declaration"_t, + projectStorageCategory(), + keyValue("function declaration", value), + keyValue("function declaration view", view)}; + + Utils::PathString signature{createJson(value.parameters)}; + + if (value.returnTypeName == view.returnTypeName && signature == view.signature) + return Sqlite::UpdateChange::No; + + updateFunctionDeclarationStatement.write(view.id, value.returnTypeName, signature); + + tracer.end(keyValue("updated", "yes")); + + return Sqlite::UpdateChange::Update; + }; + + auto remove = [&](const Storage::Synchronization::FunctionDeclarationView &view) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"remove function declaration"_t, + projectStorageCategory(), + keyValue("function declaration view", view)}; + + deleteFunctionDeclarationStatement.write(view.id); + }; + + Sqlite::insertUpdateDelete(range, functionsDeclarations, compareKey, insert, update, remove); +} + +void ProjectStorage::synchronizeSignalDeclarations( + TypeId typeId, Storage::Synchronization::SignalDeclarations &signalDeclarations) +{ + NanotraceHR::Tracer tracer{"synchronize signal declaration"_t, projectStorageCategory()}; + + std::sort(signalDeclarations.begin(), signalDeclarations.end(), [](auto &&first, auto &&second) { + auto compare = Sqlite::compare(first.name, second.name); + + if (compare == 0) { + Utils::PathString firstSignature{createJson(first.parameters)}; + Utils::PathString secondSignature{createJson(second.parameters)}; + + return Sqlite::compare(firstSignature, secondSignature) < 0; + } + + return compare < 0; + }); + + auto range = selectSignalDeclarationsForTypeIdStatement + .range(typeId); + + auto compareKey = [](const Storage::Synchronization::SignalDeclarationView &view, + const Storage::Synchronization::SignalDeclaration &value) { + auto nameKey = Sqlite::compare(view.name, value.name); + if (nameKey != 0) + return nameKey; + + Utils::PathString valueSignature{createJson(value.parameters)}; + + return Sqlite::compare(view.signature, valueSignature); + }; + + auto insert = [&](const Storage::Synchronization::SignalDeclaration &value) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert signal declaration"_t, + projectStorageCategory(), + keyValue("signal declaration", value)}; + + Utils::PathString signature{createJson(value.parameters)}; + + insertSignalDeclarationStatement.write(typeId, value.name, signature); + }; + + auto update = [&]([[maybe_unused]] const Storage::Synchronization::SignalDeclarationView &view, + [[maybe_unused]] const Storage::Synchronization::SignalDeclaration &value) { + return Sqlite::UpdateChange::No; + }; + + auto remove = [&](const Storage::Synchronization::SignalDeclarationView &view) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"remove signal declaration"_t, + projectStorageCategory(), + keyValue("signal declaration view", view)}; + + deleteSignalDeclarationStatement.write(view.id); + }; + + Sqlite::insertUpdateDelete(range, signalDeclarations, compareKey, insert, update, remove); +} + +Utils::PathString ProjectStorage::createJson( + const Storage::Synchronization::EnumeratorDeclarations &enumeratorDeclarations) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"create json from enumerator declarations"_t, projectStorageCategory()}; + + Utils::PathString json; + json.append("{"); + + Utils::SmallStringView comma{"\""}; + + for (const auto &enumerator : enumeratorDeclarations) { + json.append(comma); + comma = ",\""; + json.append(enumerator.name); + if (enumerator.hasValue) { + json.append("\":\""); + json.append(Utils::SmallString::number(enumerator.value)); + json.append("\""); + } else { + json.append("\":null"); + } + } + + json.append("}"); + + return json; +} + +void ProjectStorage::synchronizeEnumerationDeclarations( + TypeId typeId, Storage::Synchronization::EnumerationDeclarations &enumerationDeclarations) +{ + NanotraceHR::Tracer tracer{"synchronize enumeration declaration"_t, projectStorageCategory()}; + + std::sort(enumerationDeclarations.begin(), + enumerationDeclarations.end(), + [](auto &&first, auto &&second) { + return Sqlite::compare(first.name, second.name) < 0; + }); + + auto range = selectEnumerationDeclarationsForTypeIdStatement + .range(typeId); + + auto compareKey = [](const Storage::Synchronization::EnumerationDeclarationView &view, + const Storage::Synchronization::EnumerationDeclaration &value) { + return Sqlite::compare(view.name, value.name); + }; + + auto insert = [&](const Storage::Synchronization::EnumerationDeclaration &value) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert enumeration declaration"_t, + projectStorageCategory(), + keyValue("enumeration declaration", value)}; + + Utils::PathString signature{createJson(value.enumeratorDeclarations)}; + + insertEnumerationDeclarationStatement.write(typeId, value.name, signature); + }; + + auto update = [&](const Storage::Synchronization::EnumerationDeclarationView &view, + const Storage::Synchronization::EnumerationDeclaration &value) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"update enumeration declaration"_t, + projectStorageCategory(), + keyValue("enumeration declaration", value), + keyValue("enumeration declaration view", view)}; + + Utils::PathString enumeratorDeclarations{createJson(value.enumeratorDeclarations)}; + + if (enumeratorDeclarations == view.enumeratorDeclarations) + return Sqlite::UpdateChange::No; + + updateEnumerationDeclarationStatement.write(view.id, enumeratorDeclarations); + + tracer.end(keyValue("updated", "yes")); + + return Sqlite::UpdateChange::Update; + }; + + auto remove = [&](const Storage::Synchronization::EnumerationDeclarationView &view) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"remove enumeration declaration"_t, + projectStorageCategory(), + keyValue("enumeration declaration view", view)}; + + deleteEnumerationDeclarationStatement.write(view.id); + }; + + Sqlite::insertUpdateDelete(range, enumerationDeclarations, compareKey, insert, update, remove); +} + +void ProjectStorage::extractExportedTypes(TypeId typeId, + const Storage::Synchronization::Type &type, + Storage::Synchronization::ExportedTypes &exportedTypes) +{ + for (const auto &exportedType : type.exportedTypes) + exportedTypes.emplace_back(exportedType.name, exportedType.version, typeId, exportedType.moduleId); +} + +TypeId ProjectStorage::declareType(Storage::Synchronization::Type &type) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"declare type"_t, + projectStorageCategory(), + keyValue("source id", type.sourceId), + keyValue("type name", type.typeName)}; + + if (type.typeName.isEmpty()) { + type.typeId = selectTypeIdBySourceIdStatement.value(type.sourceId); + + tracer.end(keyValue("type id", type.typeId)); + + return type.typeId; + } + + type.typeId = insertTypeStatement.value(type.sourceId, type.typeName); + + if (!type.typeId) + type.typeId = selectTypeIdBySourceIdAndNameStatement.value(type.sourceId, + type.typeName); + + tracer.end(keyValue("type id", type.typeId)); + + return type.typeId; +} + +void ProjectStorage::syncDeclarations(Storage::Synchronization::Type &type, + AliasPropertyDeclarations &insertedAliasPropertyDeclarations, + AliasPropertyDeclarations &updatedAliasPropertyDeclarations, + PropertyDeclarationIds &propertyDeclarationIds) +{ + NanotraceHR::Tracer tracer{"synchronize declaration per type"_t, projectStorageCategory()}; + + if (type.changeLevel == Storage::Synchronization::ChangeLevel::Minimal) + return; + + synchronizePropertyDeclarations(type.typeId, + type.propertyDeclarations, + type.sourceId, + insertedAliasPropertyDeclarations, + updatedAliasPropertyDeclarations, + propertyDeclarationIds); + synchronizeFunctionDeclarations(type.typeId, type.functionDeclarations); + synchronizeSignalDeclarations(type.typeId, type.signalDeclarations); + synchronizeEnumerationDeclarations(type.typeId, type.enumerationDeclarations); +} + +void ProjectStorage::syncDeclarations(Storage::Synchronization::Types &types, + AliasPropertyDeclarations &insertedAliasPropertyDeclarations, + AliasPropertyDeclarations &updatedAliasPropertyDeclarations, + PropertyDeclarations &relinkablePropertyDeclarations) +{ + NanotraceHR::Tracer tracer{"synchronize declaration"_t, projectStorageCategory()}; + + PropertyDeclarationIds propertyDeclarationIds; + propertyDeclarationIds.reserve(types.size() * 10); + + for (auto &&type : types) + syncDeclarations(type, + insertedAliasPropertyDeclarations, + updatedAliasPropertyDeclarations, + propertyDeclarationIds); + + removeRelinkableEntries(relinkablePropertyDeclarations, + propertyDeclarationIds, + PropertyCompare{}); +} + +void ProjectStorage::syncDefaultProperties(Storage::Synchronization::Types &types) +{ + NanotraceHR::Tracer tracer{"synchronize default properties"_t, projectStorageCategory()}; + + auto range = selectTypesWithDefaultPropertyStatement.range(); + + auto compareKey = [](const TypeWithDefaultPropertyView &view, + const Storage::Synchronization::Type &value) { + return view.typeId - value.typeId; + }; + + auto insert = [&](const Storage::Synchronization::Type &) { + + }; + + auto update = [&](const TypeWithDefaultPropertyView &view, + const Storage::Synchronization::Type &value) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"synchronize default properties by update"_t, + projectStorageCategory(), + keyValue("type id", value.typeId), + keyValue("value", value), + keyValue("view", view)}; + + PropertyDeclarationId valueDefaultPropertyId; + if (value.defaultPropertyName.size()) + valueDefaultPropertyId = fetchPropertyDeclarationByTypeIdAndNameUngarded(value.typeId, + value.defaultPropertyName) + .propertyDeclarationId; + + if (compareInvalidAreTrue(valueDefaultPropertyId, view.defaultPropertyId)) + return Sqlite::UpdateChange::No; + + updateDefaultPropertyIdStatement.write(value.typeId, valueDefaultPropertyId); + + tracer.end(keyValue("updated", "yes"), + keyValue("default property id", valueDefaultPropertyId)); + + return Sqlite::UpdateChange::Update; + }; + + auto remove = [&](const TypeWithDefaultPropertyView &) {}; + + Sqlite::insertUpdateDelete(range, types, compareKey, insert, update, remove); +} + +void ProjectStorage::resetDefaultPropertiesIfChanged(Storage::Synchronization::Types &types) +{ + NanotraceHR::Tracer tracer{"reset changed default properties"_t, projectStorageCategory()}; + + auto range = selectTypesWithDefaultPropertyStatement.range(); + + auto compareKey = [](const TypeWithDefaultPropertyView &view, + const Storage::Synchronization::Type &value) { + return view.typeId - value.typeId; + }; + + auto insert = [&](const Storage::Synchronization::Type &) { + + }; + + auto update = [&](const TypeWithDefaultPropertyView &view, + const Storage::Synchronization::Type &value) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"reset changed default properties by update"_t, + projectStorageCategory(), + keyValue("type id", value.typeId), + keyValue("value", value), + keyValue("view", view)}; + + PropertyDeclarationId valueDefaultPropertyId; + if (value.defaultPropertyName.size()) { + auto optionalValueDefaultPropertyId = fetchOptionalPropertyDeclarationByTypeIdAndNameUngarded( + value.typeId, value.defaultPropertyName); + if (optionalValueDefaultPropertyId) + valueDefaultPropertyId = optionalValueDefaultPropertyId->propertyDeclarationId; + } + + if (compareInvalidAreTrue(valueDefaultPropertyId, view.defaultPropertyId)) + return Sqlite::UpdateChange::No; + + updateDefaultPropertyIdStatement.write(value.typeId, Sqlite::NullValue{}); + + tracer.end(keyValue("updated", "yes")); + + return Sqlite::UpdateChange::Update; + }; + + auto remove = [&](const TypeWithDefaultPropertyView &) {}; + + Sqlite::insertUpdateDelete(range, types, compareKey, insert, update, remove); +} + +void ProjectStorage::checkForPrototypeChainCycle(TypeId typeId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"check for prototype chain cycle"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + auto callback = [=](TypeId currentTypeId) { + if (typeId == currentTypeId) + throw PrototypeChainCycle{}; + }; + + selectPrototypeAndExtensionIdsStatement.readCallback(callback, typeId); +} + +void ProjectStorage::checkForAliasChainCycle(PropertyDeclarationId propertyDeclarationId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"check for alias chain cycle"_t, + projectStorageCategory(), + keyValue("property declaration id", propertyDeclarationId)}; + auto callback = [=](PropertyDeclarationId currentPropertyDeclarationId) { + if (propertyDeclarationId == currentPropertyDeclarationId) + throw AliasChainCycle{}; + }; + + selectPropertyDeclarationIdsForAliasChainStatement.readCallback(callback, propertyDeclarationId); +} + +std::pair ProjectStorage::fetchImportedTypeNameIdAndTypeId( + const Storage::Synchronization::ImportedTypeName &typeName, SourceId sourceId) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch imported type name id and type id"_t, + projectStorageCategory(), + keyValue("imported type name", typeName), + keyValue("source id", sourceId)}; + + TypeId typeId; + ImportedTypeNameId typeNameId; + if (!std::visit([](auto &&typeName_) -> bool { return typeName_.name.isEmpty(); }, typeName)) { + typeNameId = fetchImportedTypeNameId(typeName, sourceId); + + typeId = fetchTypeId(typeNameId); + + tracer.end(keyValue("type id", typeId), keyValue("type name id", typeNameId)); + + if (!typeId) + throw TypeNameDoesNotExists{fetchImportedTypeName(typeNameId), sourceId}; + } + + return {typeId, typeNameId}; +} + +void ProjectStorage::syncPrototypeAndExtension(Storage::Synchronization::Type &type, TypeIds &typeIds) +{ + if (type.changeLevel == Storage::Synchronization::ChangeLevel::Minimal) + return; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"synchronize prototype and extension"_t, + projectStorageCategory(), + keyValue("prototype", type.prototype), + keyValue("extension", type.extension), + keyValue("type id", type.typeId), + keyValue("source id", type.sourceId)}; + + auto [prototypeId, prototypeTypeNameId] = fetchImportedTypeNameIdAndTypeId(type.prototype, + type.sourceId); + auto [extensionId, extensionTypeNameId] = fetchImportedTypeNameIdAndTypeId(type.extension, + type.sourceId); + + updatePrototypeAndExtensionStatement.write(type.typeId, + prototypeId, + prototypeTypeNameId, + extensionId, + extensionTypeNameId); + + if (prototypeId || extensionId) + checkForPrototypeChainCycle(type.typeId); + + typeIds.push_back(type.typeId); + + tracer.end(keyValue("prototype id", prototypeId), + keyValue("prototype type name id", prototypeTypeNameId), + keyValue("extension id", extensionId), + keyValue("extension type name id", extensionTypeNameId)); +} + +void ProjectStorage::syncPrototypesAndExtensions(Storage::Synchronization::Types &types, + Prototypes &relinkablePrototypes, + Prototypes &relinkableExtensions) +{ + NanotraceHR::Tracer tracer{"synchronize prototypes and extensions"_t, projectStorageCategory()}; + + TypeIds typeIds; + typeIds.reserve(types.size()); + + for (auto &type : types) + syncPrototypeAndExtension(type, typeIds); + + removeRelinkableEntries(relinkablePrototypes, typeIds, TypeCompare{}); + removeRelinkableEntries(relinkableExtensions, typeIds, TypeCompare{}); +} + +ImportId ProjectStorage::fetchImportId(SourceId sourceId, const Storage::Import &import) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch imported type name id"_t, + projectStorageCategory(), + keyValue("import", import), + keyValue("source id", sourceId)}; + + ImportId importId; + if (import.version) { + importId = selectImportIdBySourceIdAndModuleIdAndVersionStatement.value( + sourceId, import.moduleId, import.version.major.value, import.version.minor.value); + } else if (import.version.major) { + importId = selectImportIdBySourceIdAndModuleIdAndMajorVersionStatement + .value(sourceId, import.moduleId, import.version.major.value); + } else { + importId = selectImportIdBySourceIdAndModuleIdStatement.value(sourceId, + import.moduleId); + } + + tracer.end(keyValue("import id", importId)); + + return importId; +} + +ImportedTypeNameId ProjectStorage::fetchImportedTypeNameId( + const Storage::Synchronization::ImportedTypeName &name, SourceId sourceId) +{ + struct Inspect + { + auto operator()(const Storage::Synchronization::ImportedType &importedType) + { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch imported type name id"_t, + projectStorageCategory(), + keyValue("imported type name", importedType.name), + keyValue("source id", sourceId), + keyValue("type name kind", "exported"sv)}; + + return storage.fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::Exported, + sourceId, + importedType.name); + } + + auto operator()(const Storage::Synchronization::QualifiedImportedType &importedType) + { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch imported type name id"_t, + projectStorageCategory(), + keyValue("imported type name", importedType.name), + keyValue("import", importedType.import), + keyValue("type name kind", "qualified exported"sv)}; + + ImportId importId = storage.fetchImportId(sourceId, importedType.import); + + auto importedTypeNameId = storage.fetchImportedTypeNameId( + Storage::Synchronization::TypeNameKind::QualifiedExported, importId, importedType.name); + + tracer.end(keyValue("import id", importId), keyValue("source id", sourceId)); + + return importedTypeNameId; + } + + ProjectStorage &storage; + SourceId sourceId; + }; + + return std::visit(Inspect{*this, sourceId}, name); +} + +TypeId ProjectStorage::fetchTypeId(ImportedTypeNameId typeNameId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch type id with type name kind"_t, + projectStorageCategory(), + keyValue("type name id", typeNameId)}; + + auto kind = selectKindFromImportedTypeNamesStatement.value( + typeNameId); + + auto typeId = fetchTypeId(typeNameId, kind); + + tracer.end(keyValue("type id", typeId), keyValue("type name kind", kind)); + + return typeId; +} + +Utils::SmallString ProjectStorage::fetchImportedTypeName(ImportedTypeNameId typeNameId) const +{ + return selectNameFromImportedTypeNamesStatement.value(typeNameId); +} + +TypeId ProjectStorage::fetchTypeId(ImportedTypeNameId typeNameId, + Storage::Synchronization::TypeNameKind kind) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch type id"_t, + projectStorageCategory(), + keyValue("type name id", typeNameId), + keyValue("type name kind", kind)}; + + TypeId typeId; + if (kind == Storage::Synchronization::TypeNameKind::Exported) { + typeId = selectTypeIdForImportedTypeNameNamesStatement.value(typeNameId); + } else { + typeId = selectTypeIdForQualifiedImportedTypeNameNamesStatement.value(typeNameId); + } + + tracer.end(keyValue("type id", typeId)); + + return typeId; +} + +std::optional +ProjectStorage::fetchOptionalPropertyDeclarationByTypeIdAndNameUngarded(TypeId typeId, + Utils::SmallStringView name) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch optional property declaration by type id and name ungarded"_t, + projectStorageCategory(), + keyValue("type id", typeId), + keyValue("property name", name)}; + + auto propertyDeclarationId = fetchPropertyDeclarationId(typeId, name); + auto propertyDeclaration = selectPropertyDeclarationResultByPropertyDeclarationIdStatement + .optionalValue( + propertyDeclarationId); + + tracer.end(keyValue("property declaration", propertyDeclaration)); + + return propertyDeclaration; +} + +ProjectStorage::FetchPropertyDeclarationResult ProjectStorage::fetchPropertyDeclarationByTypeIdAndNameUngarded( + TypeId typeId, Utils::SmallStringView name) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch property declaration by type id and name ungarded"_t, + projectStorageCategory(), + keyValue("type id", typeId), + keyValue("property name", name)}; + + auto propertyDeclaration = fetchOptionalPropertyDeclarationByTypeIdAndNameUngarded(typeId, name); + tracer.end(keyValue("property declaration", propertyDeclaration)); + + if (propertyDeclaration) + return *propertyDeclaration; + + throw PropertyNameDoesNotExists{}; +} + +PropertyDeclarationId ProjectStorage::fetchPropertyDeclarationIdByTypeIdAndNameUngarded( + TypeId typeId, Utils::SmallStringView name) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch property declaration id by type id and name ungarded"_t, + projectStorageCategory(), + keyValue("type id", typeId), + keyValue("property name", name)}; + + auto propertyDeclarationId = fetchPropertyDeclarationId(typeId, name); + + tracer.end(keyValue("property declaration id", propertyDeclarationId)); + + if (propertyDeclarationId) + return propertyDeclarationId; + + throw PropertyNameDoesNotExists{}; +} + +SourceContextId ProjectStorage::readSourceContextId(Utils::SmallStringView sourceContextPath) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"read source context id"_t, + projectStorageCategory(), + keyValue("source context path", sourceContextPath)}; + + auto sourceContextId = selectSourceContextIdFromSourceContextsBySourceContextPathStatement + .value(sourceContextPath); + + tracer.end(keyValue("source context id", sourceContextId)); + + return sourceContextId; +} + +SourceContextId ProjectStorage::writeSourceContextId(Utils::SmallStringView sourceContextPath) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"write source context id"_t, + projectStorageCategory(), + keyValue("source context path", sourceContextPath)}; + + insertIntoSourceContextsStatement.write(sourceContextPath); + + auto sourceContextId = SourceContextId::create(static_cast(database.lastInsertedRowId())); + + tracer.end(keyValue("source context id", sourceContextId)); + + return sourceContextId; +} + +SourceId ProjectStorage::writeSourceId(SourceContextId sourceContextId, + Utils::SmallStringView sourceName) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"write source id"_t, + projectStorageCategory(), + keyValue("source context id", sourceContextId), + keyValue("source name", sourceName)}; + + insertIntoSourcesStatement.write(sourceContextId, sourceName); + + auto sourceId = SourceId::create(static_cast(database.lastInsertedRowId())); + + tracer.end(keyValue("source id", sourceId)); + + return sourceId; +} + +SourceId ProjectStorage::readSourceId(SourceContextId sourceContextId, + Utils::SmallStringView sourceName) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"read source id"_t, + projectStorageCategory(), + keyValue("source context id", sourceContextId), + keyValue("source name", sourceName)}; + + auto sourceId = selectSourceIdFromSourcesBySourceContextIdAndSourceNameStatement + .value(sourceContextId, sourceName); + + tracer.end(keyValue("source id", sourceId)); + + return sourceId; +} + +Storage::Synchronization::ExportedTypes ProjectStorage::fetchExportedTypes(TypeId typeId) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch exported type"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + auto exportedTypes = selectExportedTypesByTypeIdStatement + .values(typeId); + + tracer.end(keyValue("exported types", exportedTypes)); + + return exportedTypes; +} + +Storage::Synchronization::PropertyDeclarations ProjectStorage::fetchPropertyDeclarations(TypeId typeId) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch property declarations"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + auto propertyDeclarations = selectPropertyDeclarationsByTypeIdStatement + .values(typeId); + + tracer.end(keyValue("property declarations", propertyDeclarations)); + + return propertyDeclarations; +} + +Storage::Synchronization::FunctionDeclarations ProjectStorage::fetchFunctionDeclarations(TypeId typeId) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch signal declarations"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + Storage::Synchronization::FunctionDeclarations functionDeclarations; + + auto callback = [&](Utils::SmallStringView name, + Utils::SmallStringView returnType, + FunctionDeclarationId functionDeclarationId) { + auto &functionDeclaration = functionDeclarations.emplace_back(name, returnType); + functionDeclaration.parameters = selectFunctionParameterDeclarationsStatement + .values( + functionDeclarationId); + }; + + selectFunctionDeclarationsForTypeIdWithoutSignatureStatement.readCallback(callback, typeId); + + tracer.end(keyValue("function declarations", functionDeclarations)); + + return functionDeclarations; +} + +Storage::Synchronization::SignalDeclarations ProjectStorage::fetchSignalDeclarations(TypeId typeId) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch signal declarations"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + Storage::Synchronization::SignalDeclarations signalDeclarations; + + auto callback = [&](Utils::SmallStringView name, SignalDeclarationId signalDeclarationId) { + auto &signalDeclaration = signalDeclarations.emplace_back(name); + signalDeclaration.parameters = selectSignalParameterDeclarationsStatement + .values( + signalDeclarationId); + }; + + selectSignalDeclarationsForTypeIdWithoutSignatureStatement.readCallback(callback, typeId); + + tracer.end(keyValue("signal declarations", signalDeclarations)); + + return signalDeclarations; +} + +Storage::Synchronization::EnumerationDeclarations ProjectStorage::fetchEnumerationDeclarations(TypeId typeId) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch enumeration declarations"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + Storage::Synchronization::EnumerationDeclarations enumerationDeclarations; + + auto callback = [&](Utils::SmallStringView name, + EnumerationDeclarationId enumerationDeclarationId) { + enumerationDeclarations.emplace_back( + name, + selectEnumeratorDeclarationStatement + .values(enumerationDeclarationId)); + }; + + selectEnumerationDeclarationsForTypeIdWithoutEnumeratorDeclarationsStatement.readCallback(callback, + typeId); + + tracer.end(keyValue("enumeration declarations", enumerationDeclarations)); + + return enumerationDeclarations; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index 70d5e92b256..d7687a8c6f9 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -33,10 +33,10 @@ using namespace NanotraceHR::Literals; using ProjectStorageTracing::projectStorageCategory; -template class ProjectStorage final : public ProjectStorageInterface { - friend Storage::Info::CommonTypeCache; + using Database = Sqlite::Database; + friend Storage::Info::CommonTypeCache; public: template @@ -46,655 +46,79 @@ public: template using WriteStatement = typename Database::template WriteStatement; - ProjectStorage(Database &database, bool isInitialized) - : database{database} - , exclusiveTransaction{database} - , initializer{database, isInitialized} - { - NanotraceHR::Tracer tracer{"initialize"_t, projectStorageCategory()}; + ProjectStorage(Database &database, bool isInitialized); + ~ProjectStorage(); - exclusiveTransaction.commit(); + void synchronize(Storage::Synchronization::SynchronizationPackage package) override; - database.walCheckpointFull(); + void synchronizeDocumentImports(Storage::Imports imports, SourceId sourceId) override; - moduleCache.populate(); - } + void addObserver(ProjectStorageObserver *observer) override; - void synchronize(Storage::Synchronization::SynchronizationPackage package) override - { - NanotraceHR::Tracer tracer{"synchronize"_t, projectStorageCategory()}; + void removeObserver(ProjectStorageObserver *observer) override; - TypeIds deletedTypeIds; - Sqlite::withImmediateTransaction(database, [&] { - AliasPropertyDeclarations insertedAliasPropertyDeclarations; - AliasPropertyDeclarations updatedAliasPropertyDeclarations; + ModuleId moduleId(Utils::SmallStringView moduleName) const override; - AliasPropertyDeclarations relinkableAliasPropertyDeclarations; - PropertyDeclarations relinkablePropertyDeclarations; - Prototypes relinkablePrototypes; - Prototypes relinkableExtensions; - - TypeIds updatedTypeIds; - updatedTypeIds.reserve(package.types.size()); - - TypeIds typeIdsToBeDeleted; - - 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); - synchronizeTypeAnnotations(package.typeAnnotations, - package.updatedTypeAnnotationSourceIds); - synchronizePropertyEditorQmlPaths(package.propertyEditorQmlPaths, - package.updatedPropertyEditorQmlPathSourceIds); - - deleteNotUpdatedTypes(updatedTypeIds, - package.updatedSourceIds, - typeIdsToBeDeleted, - relinkableAliasPropertyDeclarations, - relinkablePropertyDeclarations, - relinkablePrototypes, - relinkableExtensions, - deletedTypeIds); - - relink(relinkableAliasPropertyDeclarations, - relinkablePropertyDeclarations, - relinkablePrototypes, - relinkableExtensions, - deletedTypeIds); - - linkAliases(insertedAliasPropertyDeclarations, updatedAliasPropertyDeclarations); - - synchronizeProjectDatas(package.projectDatas, package.updatedProjectSourceIds); - - commonTypeCache_.resetTypeIds(); - }); - - callRefreshMetaInfoCallback(deletedTypeIds); - } - - void synchronizeDocumentImports(Storage::Imports imports, SourceId sourceId) override - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"synchronize document imports"_t, - projectStorageCategory(), - keyValue("imports", imports), - keyValue("source id", sourceId)}; - - Sqlite::withImmediateTransaction(database, [&] { - synchronizeDocumentImports(imports, - {sourceId}, - Storage::Synchronization::ImportKind::Import); - }); - } - - void addObserver(ProjectStorageObserver *observer) override - { - NanotraceHR::Tracer tracer{"add observer"_t, projectStorageCategory()}; - observers.push_back(observer); - } - - void removeObserver(ProjectStorageObserver *observer) override - { - NanotraceHR::Tracer tracer{"remove observer"_t, projectStorageCategory()}; - observers.removeOne(observer); - } - - ModuleId moduleId(Utils::SmallStringView moduleName) const override - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get module id"_t, - projectStorageCategory(), - keyValue("module name", moduleName)}; - - auto moduleId = moduleCache.id(moduleName); - - tracer.end(keyValue("module id", moduleId)); - - return moduleId; - } - - Utils::SmallString moduleName(ModuleId moduleId) const override - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get module name"_t, - projectStorageCategory(), - keyValue("module id", moduleId)}; - - if (!moduleId) - throw ModuleDoesNotExists{}; - - auto moduleName = moduleCache.value(moduleId); - - tracer.end(keyValue("module name", moduleName)); - - return moduleName; - } + Utils::SmallString moduleName(ModuleId moduleId) const override; TypeId typeId(ModuleId moduleId, Utils::SmallStringView exportedTypeName, - Storage::Version version) const override - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get type id by exported name"_t, - projectStorageCategory(), - keyValue("module id", moduleId), - keyValue("exported type name", exportedTypeName), - keyValue("version", version)}; + Storage::Version version) const override; - TypeId typeId; + TypeId typeId(ImportedTypeNameId typeNameId) const override; - if (version.minor) { - typeId = selectTypeIdByModuleIdAndExportedNameAndVersionStatement - .template valueWithTransaction(moduleId, - exportedTypeName, - version.major.value, - version.minor.value); + QVarLengthArray typeIds(ModuleId moduleId) const override; - } else if (version.major) { - typeId = selectTypeIdByModuleIdAndExportedNameAndMajorVersionStatement - .template valueWithTransaction(moduleId, - exportedTypeName, - version.major.value); + Storage::Info::ExportedTypeNames exportedTypeNames(TypeId typeId) const override; - } else { - typeId = selectTypeIdByModuleIdAndExportedNameStatement - .template valueWithTransaction(moduleId, exportedTypeName); - } + Storage::Info::ExportedTypeNames exportedTypeNames(TypeId typeId, SourceId sourceId) const override; - tracer.end(keyValue("type id", typeId)); + ImportId importId(const Storage::Import &import) const override; - return typeId; - } + ImportedTypeNameId importedTypeNameId(ImportId importId, Utils::SmallStringView typeName) override; - TypeId typeId(ImportedTypeNameId typeNameId) const override - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get type id by imported type name"_t, - projectStorageCategory(), - keyValue("imported type name id", typeNameId)}; + ImportedTypeNameId importedTypeNameId(SourceId sourceId, Utils::SmallStringView typeName) override; - auto typeId = Sqlite::withDeferredTransaction(database, - [&] { return fetchTypeId(typeNameId); }); + QVarLengthArray propertyDeclarationIds(TypeId typeId) const override; - tracer.end(keyValue("type id", typeId)); - - return typeId; - } - - QVarLengthArray typeIds(ModuleId moduleId) const override - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get type ids by module id"_t, - projectStorageCategory(), - keyValue("module id", moduleId)}; - - auto typeIds = selectTypeIdsByModuleIdStatement - .template valuesWithTransaction>(moduleId); - - tracer.end(keyValue("type ids", typeIds)); - - return typeIds; - } - - Storage::Info::ExportedTypeNames exportedTypeNames(TypeId typeId) const override - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get exported type names by type id"_t, - projectStorageCategory(), - keyValue("type id", typeId)}; - - auto exportedTypenames = selectExportedTypesByTypeIdStatement - .template valuesWithTransaction( - typeId); - - tracer.end(keyValue("exported type names", exportedTypenames)); - - return exportedTypenames; - } - - Storage::Info::ExportedTypeNames exportedTypeNames(TypeId typeId, SourceId sourceId) const override - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get exported type names by source id"_t, - projectStorageCategory(), - keyValue("type id", typeId), - keyValue("source id", sourceId)}; - - auto exportedTypenames = selectExportedTypesByTypeIdAndSourceIdStatement - .template valuesWithTransaction(typeId, sourceId); - - tracer.end(keyValue("exported type names", exportedTypenames)); - - return exportedTypenames; - } - - ImportId importId(const Storage::Import &import) const override - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get import id by import"_t, - projectStorageCategory(), - keyValue("import", import)}; - - auto importId = Sqlite::withDeferredTransaction(database, [&] { - return fetchImportId(import.sourceId, import); - }); - - tracer.end(keyValue("import id", importId)); - - return importId; - } - - ImportedTypeNameId importedTypeNameId(ImportId importId, Utils::SmallStringView typeName) override - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get imported type name id by import id"_t, - projectStorageCategory(), - keyValue("import id", importId), - keyValue("imported type name", typeName)}; - - auto importedTypeNameId = Sqlite::withDeferredTransaction(database, [&] { - return fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::QualifiedExported, - importId, - typeName); - }); - - tracer.end(keyValue("imported type name id", importedTypeNameId)); - - return importedTypeNameId; - } - - ImportedTypeNameId importedTypeNameId(SourceId sourceId, Utils::SmallStringView typeName) override - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get imported type name id by source id"_t, - projectStorageCategory(), - keyValue("source id", sourceId), - keyValue("imported type name", typeName)}; - - auto importedTypeNameId = Sqlite::withDeferredTransaction(database, [&] { - return fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::Exported, - sourceId, - typeName); - }); - - tracer.end(keyValue("imported type name id", importedTypeNameId)); - - return importedTypeNameId; - } - - QVarLengthArray propertyDeclarationIds(TypeId typeId) const override - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get property declaration ids"_t, - projectStorageCategory(), - keyValue("type id", typeId)}; - - auto propertyDeclarationIds = Sqlite::withDeferredTransaction(database, [&] { - return fetchPropertyDeclarationIds(typeId); - }); - - std::sort(propertyDeclarationIds.begin(), propertyDeclarationIds.end()); - - tracer.end(keyValue("property declaration ids", propertyDeclarationIds)); - - return propertyDeclarationIds; - } - - QVarLengthArray localPropertyDeclarationIds(TypeId typeId) const override - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get local property declaration ids"_t, - projectStorageCategory(), - keyValue("type id", typeId)}; - - auto propertyDeclarationIds = selectLocalPropertyDeclarationIdsForTypeStatement - .template valuesWithTransaction< - QVarLengthArray>(typeId); - - tracer.end(keyValue("property declaration ids", propertyDeclarationIds)); - - return propertyDeclarationIds; - } + QVarLengthArray localPropertyDeclarationIds(TypeId typeId) const override; PropertyDeclarationId propertyDeclarationId(TypeId typeId, - Utils::SmallStringView propertyName) const override - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get property declaration id"_t, - projectStorageCategory(), - keyValue("type id", typeId), - keyValue("property name", propertyName)}; - - auto propertyDeclarationId = Sqlite::withDeferredTransaction(database, [&] { - return fetchPropertyDeclarationId(typeId, propertyName); - }); - - tracer.end(keyValue("property declaration id", propertyDeclarationId)); - - return propertyDeclarationId; - } + Utils::SmallStringView propertyName) const override; PropertyDeclarationId localPropertyDeclarationId(TypeId typeId, - Utils::SmallStringView propertyName) const - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get local property declaration id"_t, - projectStorageCategory(), - keyValue("type id", typeId), - keyValue("property name", propertyName)}; + Utils::SmallStringView propertyName) const; - auto propertyDeclarationId = selectLocalPropertyDeclarationIdForTypeAndPropertyNameStatement - .template valueWithTransaction( - typeId, propertyName); - - tracer.end(keyValue("property declaration id", propertyDeclarationId)); - - return propertyDeclarationId; - } - - PropertyDeclarationId defaultPropertyDeclarationId(TypeId typeId) const override - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get default property declaration id"_t, - projectStorageCategory(), - keyValue("type id", typeId)}; - - auto propertyDeclarationId = Sqlite::withDeferredTransaction(database, [&] { - return fetchDefaultPropertyDeclarationId(typeId); - }); - - tracer.end(keyValue("property declaration id", propertyDeclarationId)); - - return propertyDeclarationId; - } + PropertyDeclarationId defaultPropertyDeclarationId(TypeId typeId) const override; std::optional propertyDeclaration( - PropertyDeclarationId propertyDeclarationId) const override - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get property declaration"_t, - projectStorageCategory(), - keyValue("property declaration id", propertyDeclarationId)}; + PropertyDeclarationId propertyDeclarationId) const override; - auto propertyDeclaration = selectPropertyDeclarationForPropertyDeclarationIdStatement - .template optionalValueWithTransaction( - propertyDeclarationId); + std::optional type(TypeId typeId) const override; - tracer.end(keyValue("property declaration", propertyDeclaration)); + Utils::PathString typeIconPath(TypeId typeId) const override; - return propertyDeclaration; - } + Storage::Info::TypeHints typeHints(TypeId typeId) const override; - std::optional type(TypeId typeId) const override - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get type"_t, projectStorageCategory(), keyValue("type id", typeId)}; + SmallSourceIds<4> typeAnnotationSourceIds(SourceId directoryId) const override; - auto type = selectInfoTypeByTypeIdStatement - .template optionalValueWithTransaction(typeId); + SmallSourceIds<64> typeAnnotationDirectorySourceIds() const override; - tracer.end(keyValue("type", type)); + Storage::Info::ItemLibraryEntries itemLibraryEntries(TypeId typeId) const override; - return type; - } + Storage::Info::ItemLibraryEntries itemLibraryEntries(ImportId importId) const; - Utils::PathString typeIconPath(TypeId typeId) const override - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get type icon path"_t, - projectStorageCategory(), - keyValue("type id", typeId)}; + Storage::Info::ItemLibraryEntries itemLibraryEntries(SourceId sourceId) const override; - auto typeIconPath = selectTypeIconPathStatement.template valueWithTransaction( - typeId); + Storage::Info::ItemLibraryEntries allItemLibraryEntries() const override; - tracer.end(keyValue("type icon path", typeIconPath)); + std::vector signalDeclarationNames(TypeId typeId) const override; - return typeIconPath; - } + std::vector functionDeclarationNames(TypeId typeId) const override; - Storage::Info::TypeHints typeHints(TypeId typeId) const override - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get type hints"_t, - projectStorageCategory(), - keyValue("type id", typeId)}; + std::optional propertyName(PropertyDeclarationId propertyDeclarationId) const override; - auto typeHints = selectTypeHintsStatement - .template valuesWithTransaction(typeId); - - tracer.end(keyValue("type hints", typeHints)); - - return typeHints; - } - - SmallSourceIds<4> typeAnnotationSourceIds(SourceId directoryId) const override - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get type annotaion source ids"_t, - projectStorageCategory(), - keyValue("source id", directoryId)}; - - auto sourceIds = selectTypeAnnotationSourceIdsStatement - .template valuesWithTransaction>(directoryId); - - tracer.end(keyValue("source ids", sourceIds)); - - return sourceIds; - } - - SmallSourceIds<64> typeAnnotationDirectorySourceIds() const override - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get type annotaion source ids"_t, projectStorageCategory()}; - - auto sourceIds = selectTypeAnnotationDirectorySourceIdsStatement - .template valuesWithTransaction>(); - - tracer.end(keyValue("source ids", sourceIds)); - - return sourceIds; - } - - Storage::Info::ItemLibraryEntries itemLibraryEntries(TypeId typeId) const override - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get item library entries by type id"_t, - projectStorageCategory(), - keyValue("type id", typeId)}; - - using Storage::Info::ItemLibraryProperties; - Storage::Info::ItemLibraryEntries entries; - - auto callback = [&](TypeId typeId_, - Utils::SmallStringView name, - Utils::SmallStringView iconPath, - Utils::SmallStringView category, - Utils::SmallStringView import, - Utils::SmallStringView toolTip, - Utils::SmallStringView properties, - Utils::SmallStringView extraFilePaths, - Utils::SmallStringView templatePath) { - auto &last = entries.emplace_back( - typeId_, name, iconPath, category, import, toolTip, templatePath); - if (properties.size()) - selectItemLibraryPropertiesStatement.readTo(last.properties, properties); - if (extraFilePaths.size()) - selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths); - }; - - selectItemLibraryEntriesByTypeIdStatement.readCallbackWithTransaction(callback, typeId); - - tracer.end(keyValue("item library entries", entries)); - - return entries; - } - - Storage::Info::ItemLibraryEntries itemLibraryEntries(ImportId importId) const - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get item library entries by import id"_t, - projectStorageCategory(), - keyValue("import id", importId)}; - - using Storage::Info::ItemLibraryProperties; - Storage::Info::ItemLibraryEntries entries; - - auto callback = [&](TypeId typeId_, - Utils::SmallStringView name, - Utils::SmallStringView iconPath, - Utils::SmallStringView category, - Utils::SmallStringView import, - Utils::SmallStringView toolTip, - Utils::SmallStringView properties, - Utils::SmallStringView extraFilePaths, - Utils::SmallStringView templatePath) { - auto &last = entries.emplace_back( - typeId_, name, iconPath, category, import, toolTip, templatePath); - if (properties.size()) - selectItemLibraryPropertiesStatement.readTo(last.properties, properties); - if (extraFilePaths.size()) - selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths); - }; - - selectItemLibraryEntriesByTypeIdStatement.readCallbackWithTransaction(callback, importId); - - tracer.end(keyValue("item library entries", entries)); - - return entries; - } - - Storage::Info::ItemLibraryEntries itemLibraryEntries(SourceId sourceId) const override - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get item library entries by source id"_t, - projectStorageCategory(), - keyValue("source id", sourceId)}; - - using Storage::Info::ItemLibraryProperties; - Storage::Info::ItemLibraryEntries entries; - - auto callback = [&](TypeId typeId, - Utils::SmallStringView name, - Utils::SmallStringView iconPath, - Utils::SmallStringView category, - Utils::SmallStringView import, - Utils::SmallStringView toolTip, - Utils::SmallStringView properties, - Utils::SmallStringView extraFilePaths, - Utils::SmallStringView templatePath) { - auto &last = entries.emplace_back( - typeId, name, iconPath, category, import, toolTip, templatePath); - if (properties.size()) - selectItemLibraryPropertiesStatement.readTo(last.properties, properties); - if (extraFilePaths.size()) - selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths); - }; - - selectItemLibraryEntriesBySourceIdStatement.readCallbackWithTransaction(callback, sourceId); - - tracer.end(keyValue("item library entries", entries)); - - return entries; - } - - Storage::Info::ItemLibraryEntries allItemLibraryEntries() const override - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get all item library entries"_t, projectStorageCategory()}; - - using Storage::Info::ItemLibraryProperties; - Storage::Info::ItemLibraryEntries entries; - - auto callback = [&](TypeId typeId, - Utils::SmallStringView name, - Utils::SmallStringView iconPath, - Utils::SmallStringView category, - Utils::SmallStringView import, - Utils::SmallStringView toolTip, - Utils::SmallStringView properties, - Utils::SmallStringView extraFilePaths, - Utils::SmallStringView templatePath) { - auto &last = entries.emplace_back( - typeId, name, iconPath, category, import, toolTip, templatePath); - if (properties.size()) - selectItemLibraryPropertiesStatement.readTo(last.properties, properties); - if (extraFilePaths.size()) - selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths); - }; - - selectItemLibraryEntriesStatement.readCallbackWithTransaction(callback); - - tracer.end(keyValue("item library entries", entries)); - - return entries; - } - - std::vector signalDeclarationNames(TypeId typeId) const override - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get signal names"_t, - projectStorageCategory(), - keyValue("type id", typeId)}; - - auto signalDeclarationNames = selectSignalDeclarationNamesForTypeStatement - .template valuesWithTransaction( - typeId); - - tracer.end(keyValue("signal names", signalDeclarationNames)); - - return signalDeclarationNames; - } - - std::vector functionDeclarationNames(TypeId typeId) const override - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get function names"_t, - projectStorageCategory(), - keyValue("type id", typeId)}; - - auto functionDeclarationNames = selectFuncionDeclarationNamesForTypeStatement - .template valuesWithTransaction( - typeId); - - tracer.end(keyValue("function names", functionDeclarationNames)); - - return functionDeclarationNames; - } - - std::optional propertyName(PropertyDeclarationId propertyDeclarationId) const override - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get property name"_t, - projectStorageCategory(), - keyValue("property declaration id", propertyDeclarationId)}; - - auto propertyName = selectPropertyNameStatement - .template optionalValueWithTransaction( - propertyDeclarationId); - - tracer.end(keyValue("property name", propertyName)); - - return propertyName; - } - - const Storage::Info::CommonTypeCache &commonTypeCache() const override + const Storage::Info::CommonTypeCache &commonTypeCache() const override { return commonTypeCache_; } @@ -708,7 +132,7 @@ public: keyValue("module name", std::string_view{moduleName}), keyValue("type name", std::string_view{typeName})}; - auto typeId = commonTypeCache_.template typeId(); + auto typeId = commonTypeCache_.typeId(); tracer.end(keyValue("type id", typeId)); @@ -722,7 +146,7 @@ public: NanotraceHR::Tracer tracer{"get builtin type id from common type cache"_t, projectStorageCategory()}; - auto typeId = commonTypeCache_.template builtinTypeId(); + auto typeId = commonTypeCache_.builtinTypeId(); tracer.end(keyValue("type id", typeId)); @@ -736,55 +160,18 @@ public: NanotraceHR::Tracer tracer{"get builtin type id from common type cache"_t, projectStorageCategory()}; - auto typeId = commonTypeCache_.template builtinTypeId(); + auto typeId = commonTypeCache_.builtinTypeId(); tracer.end(keyValue("type id", typeId)); return typeId; } - SmallTypeIds<16> prototypeIds(TypeId type) const override - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get prototypes"_t, - projectStorageCategory(), - keyValue("type id", type)}; + SmallTypeIds<16> prototypeIds(TypeId type) const override; - auto prototypeIds = selectPrototypeAndExtensionIdsStatement - .template valuesWithTransaction>(type); + SmallTypeIds<16> prototypeAndSelfIds(TypeId typeId) const override; - tracer.end(keyValue("type ids", prototypeIds)); - - return prototypeIds; - } - - SmallTypeIds<16> prototypeAndSelfIds(TypeId typeId) const override - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get prototypes and self"_t, projectStorageCategory()}; - - SmallTypeIds<16> prototypeAndSelfIds; - prototypeAndSelfIds.push_back(typeId); - - selectPrototypeAndExtensionIdsStatement.readToWithTransaction(prototypeAndSelfIds, typeId); - - tracer.end(keyValue("type ids", prototypeAndSelfIds)); - - return prototypeAndSelfIds; - } - - SmallTypeIds<64> heirIds(TypeId typeId) const override - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"get heirs"_t, projectStorageCategory()}; - - auto heirIds = selectHeirTypeIdsStatement.template valuesWithTransaction>( - typeId); - - tracer.end(keyValue("type ids", heirIds)); - - return heirIds; - } + SmallTypeIds<64> heirIds(TypeId typeId) const override; template bool isBasedOn_(TypeId typeId, TypeIds... baseTypeIds) const @@ -802,8 +189,7 @@ public: return true; } - auto range = selectPrototypeAndExtensionIdsStatement.template rangeWithTransaction( - typeId); + auto range = selectPrototypeAndExtensionIdsStatement.rangeWithTransaction(typeId); auto isBasedOn = std::any_of(range.begin(), range.end(), [&](TypeId currentTypeId) { return ((currentTypeId == baseTypeIds) || ...); @@ -814,35 +200,20 @@ public: return isBasedOn; } - bool isBasedOn(TypeId) const { return false; } + bool isBasedOn(TypeId) const; - bool isBasedOn(TypeId typeId, TypeId id1) const override { return isBasedOn_(typeId, id1); } + bool isBasedOn(TypeId typeId, TypeId id1) const override; - bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2) const override - { - return isBasedOn_(typeId, id1, id2); - } + bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2) const override; - bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3) const override - { - return isBasedOn_(typeId, id1, id2, id3); - } + bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3) const override; - bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4) const override - { - return isBasedOn_(typeId, id1, id2, id3, id4); - } + bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4) const override; - bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4, TypeId id5) const override - { - return isBasedOn_(typeId, id1, id2, id3, id4, id5); - } + bool isBasedOn(TypeId typeId, 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, TypeId id6) const override - { - return isBasedOn_(typeId, id1, id2, id3, id4, id5, id6); - } + bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4, TypeId id5, TypeId id6) + const override; bool isBasedOn(TypeId typeId, TypeId id1, @@ -851,361 +222,62 @@ public: TypeId id4, TypeId id5, TypeId id6, - TypeId id7) const override - { - return isBasedOn_(typeId, id1, id2, id3, id4, id5, id6, id7); - } + TypeId id7) const override; - TypeId fetchTypeIdByExportedName(Utils::SmallStringView name) const - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is based on"_t, - projectStorageCategory(), - keyValue("exported type name", name)}; + TypeId fetchTypeIdByExportedName(Utils::SmallStringView name) const; - auto typeId = selectTypeIdByExportedNameStatement.template valueWithTransaction(name); + TypeId fetchTypeIdByModuleIdsAndExportedName(ModuleIds moduleIds, + Utils::SmallStringView name) const; - tracer.end(keyValue("type id", typeId)); + TypeId fetchTypeIdByName(SourceId sourceId, Utils::SmallStringView name); - return typeId; - } + Storage::Synchronization::Type fetchTypeByTypeId(TypeId typeId); - TypeId fetchTypeIdByModuleIdsAndExportedName(ModuleIds moduleIds, Utils::SmallStringView name) const - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch type id by module ids and exported name"_t, - projectStorageCategory(), - keyValue("module ids", NanotraceHR::array(moduleIds)), - keyValue("exported type name", name)}; - auto typeId = selectTypeIdByModuleIdsAndExportedNameStatement.template valueWithTransaction( - static_cast(moduleIds.data()), static_cast(moduleIds.size()), name); + Storage::Synchronization::Types fetchTypes(); - tracer.end(keyValue("type id", typeId)); + SourceContextId fetchSourceContextIdUnguarded(Utils::SmallStringView sourceContextPath); - return typeId; - } + SourceContextId fetchSourceContextId(Utils::SmallStringView sourceContextPath); - TypeId fetchTypeIdByName(SourceId sourceId, Utils::SmallStringView name) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch type id by name"_t, - projectStorageCategory(), - keyValue("source id", sourceId), - keyValue("internal type name", name)}; + Utils::PathString fetchSourceContextPath(SourceContextId sourceContextId) const; - auto typeId = selectTypeIdBySourceIdAndNameStatement - .template valueWithTransaction(sourceId, name); + Cache::SourceContexts fetchAllSourceContexts() const; - tracer.end(keyValue("type id", typeId)); + SourceId fetchSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName); - return typeId; - } + Cache::SourceNameAndSourceContextId fetchSourceNameAndSourceContextId(SourceId sourceId) const; - Storage::Synchronization::Type fetchTypeByTypeId(TypeId typeId) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch type by type id"_t, - projectStorageCategory(), - keyValue("type id", typeId)}; + void clearSources(); - auto type = Sqlite::withDeferredTransaction(database, [&] { - auto type = selectTypeByTypeIdStatement.template value( - typeId); + SourceContextId fetchSourceContextId(SourceId sourceId) const; - type.exportedTypes = fetchExportedTypes(typeId); - type.propertyDeclarations = fetchPropertyDeclarations(type.typeId); - type.functionDeclarations = fetchFunctionDeclarations(type.typeId); - type.signalDeclarations = fetchSignalDeclarations(type.typeId); - type.enumerationDeclarations = fetchEnumerationDeclarations(type.typeId); + Cache::Sources fetchAllSources() const; - return type; - }); - - tracer.end(keyValue("type", type)); - - return type; - } - - Storage::Synchronization::Types fetchTypes() - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch types"_t, projectStorageCategory()}; - - auto types = Sqlite::withDeferredTransaction(database, [&] { - auto types = selectTypesStatement.template values(); - - 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; - }); - - tracer.end(keyValue("type", types)); - - return types; - } - - SourceContextId fetchSourceContextIdUnguarded(Utils::SmallStringView sourceContextPath) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch source context id unguarded"_t, projectStorageCategory()}; - - auto sourceContextId = readSourceContextId(sourceContextPath); - - return sourceContextId ? sourceContextId : writeSourceContextId(sourceContextPath); - } - - SourceContextId fetchSourceContextId(Utils::SmallStringView sourceContextPath) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch source context id"_t, - projectStorageCategory(), - keyValue("source context path", sourceContextPath)}; - - SourceContextId sourceContextId; - try { - sourceContextId = Sqlite::withDeferredTransaction(database, [&] { - return fetchSourceContextIdUnguarded(sourceContextPath); - }); - } catch (const Sqlite::ConstraintPreventsModification &) { - sourceContextId = fetchSourceContextId(sourceContextPath); - } - - tracer.end(keyValue("source context id", sourceContextId)); - - return sourceContextId; - } - - Utils::PathString fetchSourceContextPath(SourceContextId sourceContextId) const - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch source context path"_t, - projectStorageCategory(), - keyValue("source context id", sourceContextId)}; - - auto path = Sqlite::withDeferredTransaction(database, [&] { - auto optionalSourceContextPath = selectSourceContextPathFromSourceContextsBySourceContextIdStatement - .template optionalValue( - sourceContextId); - - if (!optionalSourceContextPath) - throw SourceContextIdDoesNotExists(); - - return std::move(*optionalSourceContextPath); - }); - - tracer.end(keyValue("source context path", path)); - - return path; - } - - auto fetchAllSourceContexts() const - { - NanotraceHR::Tracer tracer{"fetch all source contexts"_t, projectStorageCategory()}; - - return selectAllSourceContextsStatement - .template valuesWithTransaction(); - } - - SourceId fetchSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch source id"_t, - projectStorageCategory(), - keyValue("source context id", sourceContextId), - keyValue("source name", sourceName)}; - - auto sourceId = Sqlite::withDeferredTransaction(database, [&] { - return fetchSourceIdUnguarded(sourceContextId, sourceName); - }); - - tracer.end(keyValue("source id", sourceId)); - - return sourceId; - } - - auto fetchSourceNameAndSourceContextId(SourceId sourceId) const - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch source name and source context id"_t, - projectStorageCategory(), - keyValue("source id", sourceId)}; - - auto value = selectSourceNameAndSourceContextIdFromSourcesBySourceIdStatement - .template valueWithTransaction(sourceId); - - if (!value.sourceContextId) - throw SourceIdDoesNotExists(); - - tracer.end(keyValue("source name", value.sourceName), - keyValue("source context id", value.sourceContextId)); - - return value; - } - - void clearSources() - { - Sqlite::withImmediateTransaction(database, [&] { - deleteAllSourceContextsStatement.execute(); - deleteAllSourcesStatement.execute(); - }); - } - - SourceContextId fetchSourceContextId(SourceId sourceId) const - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch source context id"_t, - projectStorageCategory(), - keyValue("source id", sourceId)}; - - auto sourceContextId = selectSourceContextIdFromSourcesBySourceIdStatement - .template valueWithTransaction(sourceId); - - if (!sourceContextId) - throw SourceIdDoesNotExists(); - - tracer.end(keyValue("source context id", sourceContextId)); - - return sourceContextId; - } - - auto fetchAllSources() const - { - NanotraceHR::Tracer tracer{"fetch all sources"_t, projectStorageCategory()}; - - return selectAllSourcesStatement.template valuesWithTransaction(); - } - - SourceId fetchSourceIdUnguarded(SourceContextId sourceContextId, Utils::SmallStringView sourceName) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch source id unguarded"_t, - projectStorageCategory(), - keyValue("source context id", sourceContextId), - keyValue("source name", sourceName)}; - - auto sourceId = readSourceId(sourceContextId, sourceName); - - if (!sourceId) - sourceId = writeSourceId(sourceContextId, sourceName); - - tracer.end(keyValue("source id", sourceId)); - - return sourceId; - } + SourceId fetchSourceIdUnguarded(SourceContextId sourceContextId, + Utils::SmallStringView sourceName); auto fetchAllFileStatuses() const { NanotraceHR::Tracer tracer{"fetch all file statuses"_t, projectStorageCategory()}; - return selectAllFileStatusesStatement.template rangeWithTransaction(); + return selectAllFileStatusesStatement.rangeWithTransaction(); } - FileStatus fetchFileStatus(SourceId sourceId) const override - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch file status"_t, - projectStorageCategory(), - keyValue("source id", sourceId)}; + FileStatus fetchFileStatus(SourceId sourceId) const override; - auto fileStatus = selectFileStatusesForSourceIdStatement - .template valueWithTransaction(sourceId); + std::optional fetchProjectData(SourceId sourceId) const override; - tracer.end(keyValue("file status", fileStatus)); + Storage::Synchronization::ProjectDatas fetchProjectDatas(SourceId projectSourceId) const override; - return fileStatus; - } + Storage::Synchronization::ProjectDatas fetchProjectDatas(const SourceIds &projectSourceIds) const; - std::optional fetchProjectData(SourceId sourceId) const override - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch project data"_t, - projectStorageCategory(), - keyValue("source id", sourceId)}; + void setPropertyEditorPathId(TypeId typeId, SourceId pathId); - auto projectData = selectProjectDataForSourceIdStatement.template optionalValueWithTransaction< - Storage::Synchronization::ProjectData>(sourceId); + SourceId propertyEditorPathId(TypeId typeId) const override; - tracer.end(keyValue("project data", projectData)); + Storage::Imports fetchDocumentImports() const; - return projectData; - } - - Storage::Synchronization::ProjectDatas fetchProjectDatas(SourceId projectSourceId) const override - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch project datas by source id"_t, - projectStorageCategory(), - keyValue("source id", projectSourceId)}; - - auto projectDatas = selectProjectDatasForSourceIdStatement - .template valuesWithTransaction( - projectSourceId); - - tracer.end(keyValue("project datas", projectDatas)); - - return projectDatas; - } - - Storage::Synchronization::ProjectDatas fetchProjectDatas(const SourceIds &projectSourceIds) const - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch project datas by source ids"_t, - projectStorageCategory(), - keyValue("source ids", projectSourceIds)}; - - auto projectDatas = selectProjectDatasForSourceIdsStatement - .template valuesWithTransaction( - toIntegers(projectSourceIds)); - - tracer.end(keyValue("project datas", projectDatas)); - - return projectDatas; - } - - void setPropertyEditorPathId(TypeId typeId, SourceId pathId) - { - Sqlite::ImmediateSessionTransaction transaction{database}; - - upsertPropertyEditorPathIdStatement.write(typeId, pathId); - - transaction.commit(); - } - - SourceId propertyEditorPathId(TypeId typeId) const override - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"property editor path id"_t, - projectStorageCategory(), - keyValue("type id", typeId)}; - - auto sourceId = selectPropertyEditorPathIdStatement.template valueWithTransaction( - typeId); - - tracer.end(keyValue("source id", sourceId)); - - return sourceId; - } - - Storage::Imports fetchDocumentImports() const - { - NanotraceHR::Tracer tracer{"fetch document imports"_t, projectStorageCategory()}; - - return selectAllDocumentImportForSourceIdStatement - .template valuesWithTransaction(); - } - - void resetForTestsOnly() - { - database.clearAllTablesForTestsOnly(); - commonTypeCache_.clearForTestsOnly(); - moduleCache.clearForTestOnly(); - } + void resetForTestsOnly(); private: class ModuleStorageAdapter @@ -1233,12 +305,11 @@ private: } }; + using Modules = std::vector; + friend ModuleStorageAdapter; - static bool moduleNameLess(Utils::SmallStringView first, Utils::SmallStringView second) noexcept - { - return first < second; - } + static bool moduleNameLess(Utils::SmallStringView first, Utils::SmallStringView second) noexcept; using ModuleCache = StorageCache; - ModuleId fetchModuleId(Utils::SmallStringView moduleName) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch module id"_t, - projectStorageCategory(), - keyValue("module name", moduleName)}; + ModuleId fetchModuleId(Utils::SmallStringView moduleName); - auto moduleId = Sqlite::withDeferredTransaction(database, [&] { - return fetchModuleIdUnguarded(moduleName); - }); + Utils::PathString fetchModuleName(ModuleId id); - tracer.end(keyValue("module id", moduleId)); + Modules fetchAllModules() const; - return moduleId; - } - - auto fetchModuleName(ModuleId id) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch module name"_t, - projectStorageCategory(), - keyValue("module id", id)}; - - auto moduleName = Sqlite::withDeferredTransaction(database, [&] { - return fetchModuleNameUnguarded(id); - }); - - tracer.end(keyValue("module name", moduleName)); - - return moduleName; - } - - auto fetchAllModules() const - { - NanotraceHR::Tracer tracer{"fetch all modules"_t, projectStorageCategory()}; - - return selectAllModulesStatement.template valuesWithTransaction(); - } - - void callRefreshMetaInfoCallback(const TypeIds &deletedTypeIds) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"call refresh meta info callback"_t, - projectStorageCategory(), - keyValue("type ids", deletedTypeIds)}; - - if (deletedTypeIds.size()) { - for (ProjectStorageObserver *observer : observers) - observer->removedTypeIds(deletedTypeIds); - } - } + void callRefreshMetaInfoCallback(const TypeIds &deletedTypeIds); class AliasPropertyDeclaration { @@ -1457,48 +484,14 @@ private: } }; - SourceIds filterSourceIdsWithoutType(const SourceIds &updatedSourceIds, SourceIds &sourceIdsOfTypes) - { - std::sort(sourceIdsOfTypes.begin(), sourceIdsOfTypes.end()); + SourceIds filterSourceIdsWithoutType(const SourceIds &updatedSourceIds, + SourceIds &sourceIdsOfTypes); - SourceIds sourceIdsWithoutTypeSourceIds; - sourceIdsWithoutTypeSourceIds.reserve(updatedSourceIds.size()); - std::set_difference(updatedSourceIds.begin(), - updatedSourceIds.end(), - sourceIdsOfTypes.begin(), - sourceIdsOfTypes.end(), - std::back_inserter(sourceIdsWithoutTypeSourceIds)); + TypeIds fetchTypeIds(const SourceIds &sourceIds); - return sourceIdsWithoutTypeSourceIds; - } + void unique(SourceIds &sourceIds); - TypeIds fetchTypeIds(const SourceIds &sourceIds) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch type ids"_t, - projectStorageCategory(), - keyValue("source ids", sourceIds)}; - - return selectTypeIdsForSourceIdsStatement.template values(toIntegers(sourceIds)); - } - - void unique(SourceIds &sourceIds) - { - std::sort(sourceIds.begin(), sourceIds.end()); - auto newEnd = std::unique(sourceIds.begin(), sourceIds.end()); - sourceIds.erase(newEnd, sourceIds.end()); - } - - void synchronizeTypeTraits(TypeId typeId, Storage::TypeTraits traits) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"synchronize type traits"_t, - projectStorageCategory(), - keyValue("type id", typeId), - keyValue("type traits", traits)}; - - updateTypeAnnotationTraitStatement.write(typeId, traits.annotation); - } + void synchronizeTypeTraits(TypeId typeId, Storage::TypeTraits traits); class TypeAnnotationView { @@ -1533,28 +526,7 @@ private: Utils::PathString hintsJson; }; - void updateTypeIdInTypeAnnotations(Storage::Synchronization::TypeAnnotations &typeAnnotations) - { - NanotraceHR::Tracer tracer{"update type id in type annotations"_t, projectStorageCategory()}; - - for (auto &annotation : typeAnnotations) { - annotation.typeId = fetchTypeIdByModuleIdAndExportedName(annotation.moduleId, - annotation.typeName); - } - - for (auto &annotation : typeAnnotations) { - if (!annotation.typeId) - qWarning() << moduleName(annotation.moduleId).toQString() - << annotation.typeName.toQString(); - } - - typeAnnotations.erase(std::remove_if(typeAnnotations.begin(), - typeAnnotations.end(), - [](const auto &annotation) { - return !annotation.typeId; - }), - typeAnnotations.end()); - } + void updateTypeIdInTypeAnnotations(Storage::Synchronization::TypeAnnotations &typeAnnotations); template static Sqlite::ValueView createEmptyAsNull(const Value &value) @@ -1566,84 +538,9 @@ private: } void synchronizeTypeAnnotations(Storage::Synchronization::TypeAnnotations &typeAnnotations, - const SourceIds &updatedTypeAnnotationSourceIds) - { - NanotraceHR::Tracer tracer{"synchronize type annotations"_t, projectStorageCategory()}; + const SourceIds &updatedTypeAnnotationSourceIds); - using Storage::Synchronization::TypeAnnotation; - - updateTypeIdInTypeAnnotations(typeAnnotations); - - auto compareKey = [](auto &&first, auto &&second) { return first.typeId - second.typeId; }; - - std::sort(typeAnnotations.begin(), typeAnnotations.end(), [&](auto &&first, auto &&second) { - return first.typeId < second.typeId; - }); - - auto range = selectTypeAnnotationsForSourceIdsStatement.template range( - toIntegers(updatedTypeAnnotationSourceIds)); - - auto insert = [&](const TypeAnnotation &annotation) { - if (!annotation.sourceId) - throw TypeAnnotationHasInvalidSourceId{}; - - synchronizeTypeTraits(annotation.typeId, annotation.traits); - - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"insert type annotations"_t, - projectStorageCategory(), - keyValue("type annotation", annotation)}; - - insertTypeAnnotationStatement.write(annotation.typeId, - annotation.sourceId, - annotation.directorySourceId, - annotation.iconPath, - createEmptyAsNull(annotation.itemLibraryJson), - createEmptyAsNull(annotation.hintsJson)); - }; - - auto update = [&](const TypeAnnotationView &annotationFromDatabase, - const TypeAnnotation &annotation) { - synchronizeTypeTraits(annotation.typeId, annotation.traits); - - if (annotationFromDatabase.iconPath != annotation.iconPath - || annotationFromDatabase.itemLibraryJson != annotation.itemLibraryJson - || annotationFromDatabase.hintsJson != annotation.hintsJson) { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"update type annotations"_t, - projectStorageCategory(), - keyValue("type annotation from database", - annotationFromDatabase), - keyValue("type annotation", annotation)}; - - updateTypeAnnotationStatement.write(annotation.typeId, - annotation.iconPath, - createEmptyAsNull(annotation.itemLibraryJson), - createEmptyAsNull(annotation.hintsJson)); - return Sqlite::UpdateChange::Update; - } - - return Sqlite::UpdateChange::No; - }; - - auto remove = [&](const TypeAnnotationView &annotationFromDatabase) { - synchronizeTypeTraits(annotationFromDatabase.typeId, Storage::TypeTraits{}); - - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"remove type annotations"_t, - projectStorageCategory(), - keyValue("type annotation", annotationFromDatabase)}; - - deleteTypeAnnotationStatement.write(annotationFromDatabase.typeId); - }; - - Sqlite::insertUpdateDelete(range, typeAnnotations, compareKey, insert, update, remove); - } - - void synchronizeTypeTrait(const Storage::Synchronization::Type &type) - { - updateTypeTraitStatement.write(type.typeId, type.traits.type); - } + void synchronizeTypeTrait(const Storage::Synchronization::Type &type); void synchronizeTypes(Storage::Synchronization::Types &types, TypeIds &updatedTypeIds, @@ -1653,486 +550,49 @@ private: PropertyDeclarations &relinkablePropertyDeclarations, Prototypes &relinkablePrototypes, Prototypes &relinkableExtensions, - const SourceIds &updatedSourceIds) - { - NanotraceHR::Tracer tracer{"synchronize types"_t, projectStorageCategory()}; - - Storage::Synchronization::ExportedTypes exportedTypes; - exportedTypes.reserve(types.size() * 3); - SourceIds sourceIdsOfTypes; - sourceIdsOfTypes.reserve(updatedSourceIds.size()); - SourceIds notUpdatedExportedSourceIds; - notUpdatedExportedSourceIds.reserve(updatedSourceIds.size()); - SourceIds exportedSourceIds; - exportedSourceIds.reserve(types.size()); - - for (auto &type : types) { - if (!type.sourceId) - throw TypeHasInvalidSourceId{}; - - TypeId typeId = declareType(type); - synchronizeTypeTrait(type); - sourceIdsOfTypes.push_back(type.sourceId); - updatedTypeIds.push_back(typeId); - if (type.changeLevel != Storage::Synchronization::ChangeLevel::ExcludeExportedTypes) { - exportedSourceIds.push_back(type.sourceId); - extractExportedTypes(typeId, type, exportedTypes); - } - } - - std::sort(types.begin(), types.end(), [](const auto &first, const auto &second) { - return first.typeId < second.typeId; - }); - - unique(exportedSourceIds); - - SourceIds sourceIdsWithoutType = filterSourceIdsWithoutType(updatedSourceIds, - sourceIdsOfTypes); - exportedSourceIds.insert(exportedSourceIds.end(), - sourceIdsWithoutType.begin(), - sourceIdsWithoutType.end()); - TypeIds exportedTypeIds = fetchTypeIds(exportedSourceIds); - synchronizeExportedTypes(exportedTypeIds, - exportedTypes, - relinkableAliasPropertyDeclarations, - relinkablePropertyDeclarations, - relinkablePrototypes, - relinkableExtensions); - - syncPrototypesAndExtensions(types, relinkablePrototypes, relinkableExtensions); - resetDefaultPropertiesIfChanged(types); - resetRemovedAliasPropertyDeclarationsToNull(types, relinkableAliasPropertyDeclarations); - syncDeclarations(types, - insertedAliasPropertyDeclarations, - updatedAliasPropertyDeclarations, - relinkablePropertyDeclarations); - syncDefaultProperties(types); - } + const SourceIds &updatedSourceIds); void synchronizeProjectDatas(Storage::Synchronization::ProjectDatas &projectDatas, - const SourceIds &updatedProjectSourceIds) - { - NanotraceHR::Tracer tracer{"synchronize project datas"_t, projectStorageCategory()}; + const SourceIds &updatedProjectSourceIds); - auto compareKey = [](auto &&first, auto &&second) { - auto projectSourceIdDifference = first.projectSourceId - second.projectSourceId; - if (projectSourceIdDifference != 0) - return projectSourceIdDifference; - - return first.sourceId - second.sourceId; - }; - - std::sort(projectDatas.begin(), projectDatas.end(), [&](auto &&first, auto &&second) { - return std::tie(first.projectSourceId, first.sourceId) - < std::tie(second.projectSourceId, second.sourceId); - }); - - auto range = selectProjectDatasForSourceIdsStatement - .template range( - toIntegers(updatedProjectSourceIds)); - - auto insert = [&](const Storage::Synchronization::ProjectData &projectData) { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"insert project data"_t, - projectStorageCategory(), - keyValue("project data", projectData)}; - - if (!projectData.projectSourceId) - throw ProjectDataHasInvalidProjectSourceId{}; - if (!projectData.sourceId) - throw ProjectDataHasInvalidSourceId{}; - - insertProjectDataStatement.write(projectData.projectSourceId, - projectData.sourceId, - projectData.moduleId, - projectData.fileType); - }; - - auto update = [&](const Storage::Synchronization::ProjectData &projectDataFromDatabase, - const Storage::Synchronization::ProjectData &projectData) { - if (projectDataFromDatabase.fileType != projectData.fileType - || !compareInvalidAreTrue(projectDataFromDatabase.moduleId, projectData.moduleId)) { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"update project data"_t, - projectStorageCategory(), - keyValue("project data", projectData), - keyValue("project data from database", - projectDataFromDatabase)}; - - updateProjectDataStatement.write(projectData.projectSourceId, - projectData.sourceId, - projectData.moduleId, - projectData.fileType); - return Sqlite::UpdateChange::Update; - } - - return Sqlite::UpdateChange::No; - }; - - auto remove = [&](const Storage::Synchronization::ProjectData &projectData) { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"remove project data"_t, - projectStorageCategory(), - keyValue("project data", projectData)}; - - deleteProjectDataStatement.write(projectData.projectSourceId, projectData.sourceId); - }; - - Sqlite::insertUpdateDelete(range, projectDatas, compareKey, insert, update, remove); - } - - void synchronizeFileStatuses(FileStatuses &fileStatuses, const SourceIds &updatedSourceIds) - { - NanotraceHR::Tracer tracer{"synchronize file statuses"_t, projectStorageCategory()}; - - auto compareKey = [](auto &&first, auto &&second) { - return first.sourceId - second.sourceId; - }; - - std::sort(fileStatuses.begin(), fileStatuses.end(), [&](auto &&first, auto &&second) { - return first.sourceId < second.sourceId; - }); - - auto range = selectFileStatusesForSourceIdsStatement.template range( - toIntegers(updatedSourceIds)); - - auto insert = [&](const FileStatus &fileStatus) { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"insert file status"_t, - projectStorageCategory(), - keyValue("file status", fileStatus)}; - - if (!fileStatus.sourceId) - throw FileStatusHasInvalidSourceId{}; - insertFileStatusStatement.write(fileStatus.sourceId, - fileStatus.size, - fileStatus.lastModified); - }; - - auto update = [&](const FileStatus &fileStatusFromDatabase, const FileStatus &fileStatus) { - if (fileStatusFromDatabase.lastModified != fileStatus.lastModified - || fileStatusFromDatabase.size != fileStatus.size) { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"update file status"_t, - projectStorageCategory(), - keyValue("file status", fileStatus), - keyValue("file status from database", - fileStatusFromDatabase)}; - - updateFileStatusStatement.write(fileStatus.sourceId, - fileStatus.size, - fileStatus.lastModified); - return Sqlite::UpdateChange::Update; - } - - return Sqlite::UpdateChange::No; - }; - - auto remove = [&](const FileStatus &fileStatus) { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"remove file status"_t, - projectStorageCategory(), - keyValue("file status", fileStatus)}; - - deleteFileStatusStatement.write(fileStatus.sourceId); - }; - - Sqlite::insertUpdateDelete(range, fileStatuses, compareKey, insert, update, remove); - } + void synchronizeFileStatuses(FileStatuses &fileStatuses, const SourceIds &updatedSourceIds); void synchronizeImports(Storage::Imports &imports, const SourceIds &updatedSourceIds, Storage::Imports &moduleDependencies, const SourceIds &updatedModuleDependencySourceIds, Storage::Synchronization::ModuleExportedImports &moduleExportedImports, - const ModuleIds &updatedModuleIds) - { - NanotraceHR::Tracer tracer{"synchronize imports"_t, projectStorageCategory()}; - - synchromizeModuleExportedImports(moduleExportedImports, updatedModuleIds); - NanotraceHR::Tracer importTracer{"synchronize qml document imports"_t, - projectStorageCategory()}; - synchronizeDocumentImports(imports, - updatedSourceIds, - Storage::Synchronization::ImportKind::Import); - importTracer.end(); - NanotraceHR::Tracer moduleDependenciesTracer{"synchronize module depdencies"_t, - projectStorageCategory()}; - synchronizeDocumentImports(moduleDependencies, - updatedModuleDependencySourceIds, - Storage::Synchronization::ImportKind::ModuleDependency); - moduleDependenciesTracer.end(); - } + const ModuleIds &updatedModuleIds); void synchromizeModuleExportedImports( Storage::Synchronization::ModuleExportedImports &moduleExportedImports, - const ModuleIds &updatedModuleIds) - { - NanotraceHR::Tracer tracer{"synchronize module exported imports"_t, projectStorageCategory()}; - std::sort(moduleExportedImports.begin(), - moduleExportedImports.end(), - [](auto &&first, auto &&second) { - return std::tie(first.moduleId, first.exportedModuleId) - < std::tie(second.moduleId, second.exportedModuleId); - }); + const ModuleIds &updatedModuleIds); - auto range = selectModuleExportedImportsForSourceIdStatement - .template range( - toIntegers(updatedModuleIds)); + ModuleId fetchModuleIdUnguarded(Utils::SmallStringView name) const override; - auto compareKey = [](const Storage::Synchronization::ModuleExportedImportView &view, - const Storage::Synchronization::ModuleExportedImport &import) -> long long { - auto moduleIdDifference = view.moduleId - import.moduleId; - if (moduleIdDifference != 0) - return moduleIdDifference; - - return view.exportedModuleId - import.exportedModuleId; - }; - - auto insert = [&](const Storage::Synchronization::ModuleExportedImport &import) { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"insert module exported import"_t, - projectStorageCategory(), - keyValue("module exported import", import), - keyValue("module id", import.moduleId)}; - tracer.tick("exported module"_t, keyValue("module id", import.exportedModuleId)); - - if (import.version.minor) { - insertModuleExportedImportWithVersionStatement.write(import.moduleId, - import.exportedModuleId, - import.isAutoVersion, - import.version.major.value, - import.version.minor.value); - } else if (import.version.major) { - insertModuleExportedImportWithMajorVersionStatement.write(import.moduleId, - import.exportedModuleId, - import.isAutoVersion, - import.version.major.value); - } else { - insertModuleExportedImportWithoutVersionStatement.write(import.moduleId, - import.exportedModuleId, - import.isAutoVersion); - } - }; - - auto update = [](const Storage::Synchronization::ModuleExportedImportView &, - const Storage::Synchronization::ModuleExportedImport &) { - return Sqlite::UpdateChange::No; - }; - - auto remove = [&](const Storage::Synchronization::ModuleExportedImportView &view) { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"remove module exported import"_t, - projectStorageCategory(), - keyValue("module exported import view", view), - keyValue("module id", view.moduleId)}; - tracer.tick("exported module"_t, keyValue("module id", view.exportedModuleId)); - - deleteModuleExportedImportStatement.write(view.moduleExportedImportId); - }; - - Sqlite::insertUpdateDelete(range, moduleExportedImports, compareKey, insert, update, remove); - } - - ModuleId fetchModuleIdUnguarded(Utils::SmallStringView name) const override - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch module id ungarded"_t, - projectStorageCategory(), - keyValue("module name", name)}; - - auto moduleId = selectModuleIdByNameStatement.template value(name); - - if (!moduleId) - moduleId = insertModuleNameStatement.template value(name); - - tracer.end(keyValue("module id", moduleId)); - - return moduleId; - } - - auto fetchModuleNameUnguarded(ModuleId id) const - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch module name ungarded"_t, - projectStorageCategory(), - keyValue("module id", id)}; - - auto moduleName = selectModuleNameStatement.template value(id); - - if (moduleName.empty()) - throw ModuleDoesNotExists{}; - - tracer.end(keyValue("module name", moduleName)); - - return moduleName; - } + Utils::PathString fetchModuleNameUnguarded(ModuleId id) const; void handleAliasPropertyDeclarationsWithPropertyType( - TypeId typeId, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"handle alias property declarations with property type"_t, - projectStorageCategory(), - keyValue("type id", typeId), - keyValue("relinkable alias property declarations", - relinkableAliasPropertyDeclarations)}; - - auto callback = [&](TypeId typeId_, - PropertyDeclarationId propertyDeclarationId, - ImportedTypeNameId propertyImportedTypeNameId, - PropertyDeclarationId aliasPropertyDeclarationId, - PropertyDeclarationId aliasPropertyDeclarationTailId) { - auto aliasPropertyName = selectPropertyNameStatement.template value( - aliasPropertyDeclarationId); - Utils::SmallString aliasPropertyNameTail; - if (aliasPropertyDeclarationTailId) - aliasPropertyNameTail = selectPropertyNameStatement.template value( - aliasPropertyDeclarationTailId); - - relinkableAliasPropertyDeclarations - .emplace_back(TypeId{typeId_}, - PropertyDeclarationId{propertyDeclarationId}, - ImportedTypeNameId{propertyImportedTypeNameId}, - std::move(aliasPropertyName), - std::move(aliasPropertyNameTail)); - - updateAliasPropertyDeclarationToNullStatement.write(propertyDeclarationId); - }; - - selectAliasPropertiesDeclarationForPropertiesWithTypeIdStatement.readCallback(callback, - typeId); - } + TypeId typeId, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations); void handlePropertyDeclarationWithPropertyType(TypeId typeId, - PropertyDeclarations &relinkablePropertyDeclarations) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"handle property declarations with property type"_t, - projectStorageCategory(), - keyValue("type id", typeId), - keyValue("relinkable property declarations", - relinkablePropertyDeclarations)}; + PropertyDeclarations &relinkablePropertyDeclarations); - updatesPropertyDeclarationPropertyTypeToNullStatement.readTo(relinkablePropertyDeclarations, - typeId); - } + void handlePrototypes(TypeId prototypeId, Prototypes &relinkablePrototypes); - void handlePrototypes(TypeId prototypeId, Prototypes &relinkablePrototypes) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"handle prototypes"_t, - projectStorageCategory(), - keyValue("type id", prototypeId), - keyValue("relinkable prototypes", relinkablePrototypes)}; - - auto callback = [&](TypeId typeId, ImportedTypeNameId prototypeNameId) { - relinkablePrototypes.emplace_back(typeId, prototypeNameId); - }; - - updatePrototypeIdToNullStatement.readCallback(callback, prototypeId); - } - - void handleExtensions(TypeId extensionId, Prototypes &relinkableExtensions) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"handle extension"_t, - projectStorageCategory(), - keyValue("type id", extensionId), - keyValue("relinkable extensions", relinkableExtensions)}; - - auto callback = [&](TypeId typeId, ImportedTypeNameId extensionNameId) { - relinkableExtensions.emplace_back(typeId, extensionNameId); - }; - - updateExtensionIdToNullStatement.readCallback(callback, extensionId); - } + void handleExtensions(TypeId extensionId, Prototypes &relinkableExtensions); void deleteType(TypeId typeId, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations, PropertyDeclarations &relinkablePropertyDeclarations, Prototypes &relinkablePrototypes, - Prototypes &relinkableExtensions) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"delete type"_t, - projectStorageCategory(), - keyValue("type id", typeId)}; - - handlePropertyDeclarationWithPropertyType(typeId, relinkablePropertyDeclarations); - handleAliasPropertyDeclarationsWithPropertyType(typeId, relinkableAliasPropertyDeclarations); - handlePrototypes(typeId, relinkablePrototypes); - handleExtensions(typeId, relinkableExtensions); - deleteTypeNamesByTypeIdStatement.write(typeId); - deleteEnumerationDeclarationByTypeIdStatement.write(typeId); - deletePropertyDeclarationByTypeIdStatement.write(typeId); - deleteFunctionDeclarationByTypeIdStatement.write(typeId); - deleteSignalDeclarationByTypeIdStatement.write(typeId); - deleteTypeStatement.write(typeId); - } + Prototypes &relinkableExtensions); void relinkAliasPropertyDeclarations(AliasPropertyDeclarations &aliasPropertyDeclarations, - const TypeIds &deletedTypeIds) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"relink alias properties"_t, - projectStorageCategory(), - keyValue("alias property declarations", aliasPropertyDeclarations), - keyValue("deleted type ids", deletedTypeIds)}; - - std::sort(aliasPropertyDeclarations.begin(), aliasPropertyDeclarations.end()); - - Utils::set_greedy_difference( - aliasPropertyDeclarations.cbegin(), - aliasPropertyDeclarations.cend(), - deletedTypeIds.begin(), - deletedTypeIds.end(), - [&](const AliasPropertyDeclaration &alias) { - auto typeId = fetchTypeId(alias.aliasImportedTypeNameId); - - if (!typeId) - throw TypeNameDoesNotExists{fetchImportedTypeName(alias.aliasImportedTypeNameId)}; - - auto [propertyTypeId, aliasId, propertyTraits] = fetchPropertyDeclarationByTypeIdAndNameUngarded( - typeId, alias.aliasPropertyName); - - updatePropertyDeclarationWithAliasAndTypeStatement.write(alias.propertyDeclarationId, - propertyTypeId, - propertyTraits, - alias.aliasImportedTypeNameId, - aliasId); - }, - TypeCompare{}); - } + const TypeIds &deletedTypeIds); void relinkPropertyDeclarations(PropertyDeclarations &relinkablePropertyDeclaration, - const TypeIds &deletedTypeIds) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"relink property declarations"_t, - projectStorageCategory(), - keyValue("relinkable property declarations", - relinkablePropertyDeclaration), - keyValue("deleted type ids", deletedTypeIds)}; - - std::sort(relinkablePropertyDeclaration.begin(), relinkablePropertyDeclaration.end()); - - Utils::set_greedy_difference( - relinkablePropertyDeclaration.cbegin(), - relinkablePropertyDeclaration.cend(), - deletedTypeIds.begin(), - deletedTypeIds.end(), - [&](const PropertyDeclaration &property) { - TypeId propertyTypeId = fetchTypeId(property.importedTypeNameId); - - if (!propertyTypeId) - throw TypeNameDoesNotExists{fetchImportedTypeName(property.importedTypeNameId)}; - - updatePropertyDeclarationTypeStatement.write(property.propertyDeclarationId, - propertyTypeId); - }, - TypeCompare{}); - } + const TypeIds &deletedTypeIds); template void relinkPrototypes(Prototypes &relinkablePrototypes, @@ -2171,435 +631,66 @@ private: PropertyDeclarations &relinkablePropertyDeclarations, Prototypes &relinkablePrototypes, Prototypes &relinkableExtensions, - TypeIds &deletedTypeIds) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"delete not updated types"_t, - projectStorageCategory(), - keyValue("updated type ids", updatedTypeIds), - keyValue("updated source ids", updatedSourceIds), - keyValue("type ids to be deleted", typeIdsToBeDeleted)}; - - auto callback = [&](TypeId typeId) { - deletedTypeIds.push_back(typeId); - deleteType(typeId, - relinkableAliasPropertyDeclarations, - relinkablePropertyDeclarations, - relinkablePrototypes, - relinkableExtensions); - }; - - selectNotUpdatedTypesInSourcesStatement.readCallback(callback, - toIntegers(updatedSourceIds), - toIntegers(updatedTypeIds)); - for (TypeId typeIdToBeDeleted : typeIdsToBeDeleted) - callback(typeIdToBeDeleted); - } + TypeIds &deletedTypeIds); void relink(AliasPropertyDeclarations &relinkableAliasPropertyDeclarations, PropertyDeclarations &relinkablePropertyDeclarations, Prototypes &relinkablePrototypes, Prototypes &relinkableExtensions, - TypeIds &deletedTypeIds) - { - NanotraceHR::Tracer tracer{"relink"_t, projectStorageCategory()}; - - std::sort(deletedTypeIds.begin(), deletedTypeIds.end()); - - relinkPrototypes(relinkablePrototypes, deletedTypeIds, [&](TypeId typeId, TypeId prototypeId) { - updateTypePrototypeStatement.write(typeId, prototypeId); - }); - relinkPrototypes(relinkableExtensions, deletedTypeIds, [&](TypeId typeId, TypeId prototypeId) { - updateTypeExtensionStatement.write(typeId, prototypeId); - }); - relinkPropertyDeclarations(relinkablePropertyDeclarations, deletedTypeIds); - relinkAliasPropertyDeclarations(relinkableAliasPropertyDeclarations, deletedTypeIds); - } + TypeIds &deletedTypeIds); PropertyDeclarationId fetchAliasId(TypeId aliasTypeId, Utils::SmallStringView aliasPropertyName, - Utils::SmallStringView aliasPropertyNameTail) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch alias id"_t, - projectStorageCategory(), - keyValue("alias type id", aliasTypeId), - keyValue("alias property name", aliasPropertyName), - keyValue("alias property name tail", aliasPropertyNameTail)}; + Utils::SmallStringView aliasPropertyNameTail); - if (aliasPropertyNameTail.empty()) - return fetchPropertyDeclarationIdByTypeIdAndNameUngarded(aliasTypeId, aliasPropertyName); + void linkAliasPropertyDeclarationAliasIds(const AliasPropertyDeclarations &aliasDeclarations); - auto stemAlias = fetchPropertyDeclarationByTypeIdAndNameUngarded(aliasTypeId, - aliasPropertyName); + void updateAliasPropertyDeclarationValues(const AliasPropertyDeclarations &aliasDeclarations); - return fetchPropertyDeclarationIdByTypeIdAndNameUngarded(stemAlias.propertyTypeId, - aliasPropertyNameTail); - } - - void linkAliasPropertyDeclarationAliasIds(const AliasPropertyDeclarations &aliasDeclarations) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"link alias property declarations alias ids"_t, - projectStorageCategory(), - keyValue("alias property declarations", aliasDeclarations)}; - - for (const auto &aliasDeclaration : aliasDeclarations) { - auto aliasTypeId = fetchTypeId(aliasDeclaration.aliasImportedTypeNameId); - - if (!aliasTypeId) { - throw TypeNameDoesNotExists{ - fetchImportedTypeName(aliasDeclaration.aliasImportedTypeNameId)}; - } - - auto aliasId = fetchAliasId(aliasTypeId, - aliasDeclaration.aliasPropertyName, - aliasDeclaration.aliasPropertyNameTail); - - updatePropertyDeclarationAliasIdAndTypeNameIdStatement - .write(aliasDeclaration.propertyDeclarationId, - aliasId, - aliasDeclaration.aliasImportedTypeNameId); - } - } - - void updateAliasPropertyDeclarationValues(const AliasPropertyDeclarations &aliasDeclarations) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"update alias property declarations"_t, - projectStorageCategory(), - keyValue("alias property declarations", aliasDeclarations)}; - - for (const auto &aliasDeclaration : aliasDeclarations) { - updatetPropertiesDeclarationValuesOfAliasStatement.write( - aliasDeclaration.propertyDeclarationId); - updatePropertyAliasDeclarationRecursivelyStatement.write( - aliasDeclaration.propertyDeclarationId); - } - } - - void checkAliasPropertyDeclarationCycles(const AliasPropertyDeclarations &aliasDeclarations) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"check alias property declarations cycles"_t, - projectStorageCategory(), - keyValue("alias property declarations", aliasDeclarations)}; - for (const auto &aliasDeclaration : aliasDeclarations) - checkForAliasChainCycle(aliasDeclaration.propertyDeclarationId); - } + void checkAliasPropertyDeclarationCycles(const AliasPropertyDeclarations &aliasDeclarations); void linkAliases(const AliasPropertyDeclarations &insertedAliasPropertyDeclarations, - const AliasPropertyDeclarations &updatedAliasPropertyDeclarations) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"link aliases"_t, projectStorageCategory()}; - - linkAliasPropertyDeclarationAliasIds(insertedAliasPropertyDeclarations); - linkAliasPropertyDeclarationAliasIds(updatedAliasPropertyDeclarations); - - checkAliasPropertyDeclarationCycles(insertedAliasPropertyDeclarations); - checkAliasPropertyDeclarationCycles(updatedAliasPropertyDeclarations); - - updateAliasPropertyDeclarationValues(insertedAliasPropertyDeclarations); - updateAliasPropertyDeclarationValues(updatedAliasPropertyDeclarations); - } + const AliasPropertyDeclarations &updatedAliasPropertyDeclarations); void synchronizeExportedTypes(const TypeIds &updatedTypeIds, Storage::Synchronization::ExportedTypes &exportedTypes, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations, PropertyDeclarations &relinkablePropertyDeclarations, Prototypes &relinkablePrototypes, - Prototypes &relinkableExtensions) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"synchronize exported types"_t, projectStorageCategory()}; - - std::sort(exportedTypes.begin(), exportedTypes.end(), [](auto &&first, auto &&second) { - if (first.moduleId < second.moduleId) - return true; - else if (first.moduleId > second.moduleId) - return false; - - auto nameCompare = Sqlite::compare(first.name, second.name); - - if (nameCompare < 0) - return true; - else if (nameCompare > 0) - return false; - - return first.version < second.version; - }); - - auto range = selectExportedTypesForSourceIdsStatement - .template range( - toIntegers(updatedTypeIds)); - - auto compareKey = [](const Storage::Synchronization::ExportedTypeView &view, - const Storage::Synchronization::ExportedType &type) -> long long { - auto moduleIdDifference = view.moduleId - type.moduleId; - if (moduleIdDifference != 0) - return moduleIdDifference; - - auto nameDifference = Sqlite::compare(view.name, type.name); - if (nameDifference != 0) - return nameDifference; - - auto versionDifference = view.version.major.value - type.version.major.value; - if (versionDifference != 0) - return versionDifference; - - return view.version.minor.value - type.version.minor.value; - }; - - auto insert = [&](const Storage::Synchronization::ExportedType &type) { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"insert exported type"_t, - projectStorageCategory(), - keyValue("exported type", type), - keyValue("type id", type.typeId), - keyValue("module id", type.moduleId)}; - if (!type.moduleId) - throw QmlDesigner::ModuleDoesNotExists{}; - - try { - if (type.version) { - insertExportedTypeNamesWithVersionStatement.write(type.moduleId, - type.name, - type.version.major.value, - type.version.minor.value, - type.typeId); - - } else if (type.version.major) { - insertExportedTypeNamesWithMajorVersionStatement.write(type.moduleId, - type.name, - type.version.major.value, - type.typeId); - } else { - insertExportedTypeNamesWithoutVersionStatement.write(type.moduleId, - type.name, - type.typeId); - } - } catch (const Sqlite::ConstraintPreventsModification &) { - throw QmlDesigner::ExportedTypeCannotBeInserted{type.name}; - } - }; - - auto update = [&](const Storage::Synchronization::ExportedTypeView &view, - const Storage::Synchronization::ExportedType &type) { - if (view.typeId != type.typeId) { - NanotraceHR::Tracer tracer{"update exported type"_t, - projectStorageCategory(), - keyValue("exported type", type), - keyValue("exported type view", view), - keyValue("type id", type.typeId), - keyValue("module id", type.typeId)}; - - handlePropertyDeclarationWithPropertyType(view.typeId, relinkablePropertyDeclarations); - handleAliasPropertyDeclarationsWithPropertyType(view.typeId, - relinkableAliasPropertyDeclarations); - handlePrototypes(view.typeId, relinkablePrototypes); - handleExtensions(view.typeId, relinkableExtensions); - updateExportedTypeNameTypeIdStatement.write(view.exportedTypeNameId, type.typeId); - return Sqlite::UpdateChange::Update; - } - return Sqlite::UpdateChange::No; - }; - - auto remove = [&](const Storage::Synchronization::ExportedTypeView &view) { - NanotraceHR::Tracer tracer{"remove exported type"_t, - projectStorageCategory(), - keyValue("exported type", view), - keyValue("type id", view.typeId), - keyValue("module id", view.moduleId)}; - - handlePropertyDeclarationWithPropertyType(view.typeId, relinkablePropertyDeclarations); - handleAliasPropertyDeclarationsWithPropertyType(view.typeId, - relinkableAliasPropertyDeclarations); - handlePrototypes(view.typeId, relinkablePrototypes); - handleExtensions(view.typeId, relinkableExtensions); - deleteExportedTypeNameStatement.write(view.exportedTypeNameId); - }; - - Sqlite::insertUpdateDelete(range, exportedTypes, compareKey, insert, update, remove); - } + Prototypes &relinkableExtensions); void synchronizePropertyDeclarationsInsertAlias( AliasPropertyDeclarations &insertedAliasPropertyDeclarations, const Storage::Synchronization::PropertyDeclaration &value, SourceId sourceId, - TypeId typeId) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"insert property declaration to alias"_t, - projectStorageCategory(), - keyValue("property declaration", value)}; + TypeId typeId); - auto callback = [&](PropertyDeclarationId propertyDeclarationId) { - insertedAliasPropertyDeclarations.emplace_back(typeId, - propertyDeclarationId, - fetchImportedTypeNameId(value.typeName, - sourceId), - value.aliasPropertyName, - value.aliasPropertyNameTail); - return Sqlite::CallbackControl::Abort; - }; - - insertAliasPropertyDeclarationStatement.readCallback(callback, typeId, value.name); - } - - auto fetchPropertyDeclarationIds(TypeId baseTypeId) const - { - QVarLengthArray propertyDeclarationIds; - - selectLocalPropertyDeclarationIdsForTypeStatement.readTo(propertyDeclarationIds, baseTypeId); - - auto range = selectPrototypeAndExtensionIdsStatement.template range(baseTypeId); - - for (TypeId prototype : range) { - selectLocalPropertyDeclarationIdsForTypeStatement.readTo(propertyDeclarationIds, - prototype); - } - - return propertyDeclarationIds; - } + QVarLengthArray fetchPropertyDeclarationIds(TypeId baseTypeId) const; PropertyDeclarationId fetchNextPropertyDeclarationId(TypeId baseTypeId, - Utils::SmallStringView propertyName) const - { - auto range = selectPrototypeAndExtensionIdsStatement.template range(baseTypeId); - - for (TypeId prototype : range) { - auto propertyDeclarationId = selectPropertyDeclarationIdByTypeIdAndNameStatement - .template value(prototype, - propertyName); - - if (propertyDeclarationId) - return propertyDeclarationId; - } - - return PropertyDeclarationId{}; - } + Utils::SmallStringView propertyName) const; PropertyDeclarationId fetchPropertyDeclarationId(TypeId typeId, - Utils::SmallStringView propertyName) const - { - auto propertyDeclarationId = selectPropertyDeclarationIdByTypeIdAndNameStatement - .template value(typeId, propertyName); + Utils::SmallStringView propertyName) const; - if (propertyDeclarationId) - return propertyDeclarationId; + PropertyDeclarationId fetchNextDefaultPropertyDeclarationId(TypeId baseTypeId) const; - return fetchNextPropertyDeclarationId(typeId, propertyName); - } - - PropertyDeclarationId fetchNextDefaultPropertyDeclarationId(TypeId baseTypeId) const - { - auto range = selectPrototypeAndExtensionIdsStatement.template range(baseTypeId); - - for (TypeId prototype : range) { - auto propertyDeclarationId = selectDefaultPropertyDeclarationIdStatement - .template value(prototype); - - if (propertyDeclarationId) - return propertyDeclarationId; - } - - return PropertyDeclarationId{}; - } - - PropertyDeclarationId fetchDefaultPropertyDeclarationId(TypeId typeId) const - { - auto propertyDeclarationId = selectDefaultPropertyDeclarationIdStatement - .template value(typeId); - - if (propertyDeclarationId) - return propertyDeclarationId; - - return fetchNextDefaultPropertyDeclarationId(typeId); - } + PropertyDeclarationId fetchDefaultPropertyDeclarationId(TypeId typeId) const; void synchronizePropertyDeclarationsInsertProperty( - const Storage::Synchronization::PropertyDeclaration &value, SourceId sourceId, TypeId typeId) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"insert property declaration"_t, - projectStorageCategory(), - keyValue("property declaration", value)}; - - auto propertyImportedTypeNameId = fetchImportedTypeNameId(value.typeName, sourceId); - auto propertyTypeId = fetchTypeId(propertyImportedTypeNameId); - - if (!propertyTypeId) - throw TypeNameDoesNotExists{fetchImportedTypeName(propertyImportedTypeNameId), sourceId}; - - auto propertyDeclarationId = insertPropertyDeclarationStatement.template value( - typeId, value.name, propertyTypeId, value.traits, propertyImportedTypeNameId); - - auto nextPropertyDeclarationId = fetchNextPropertyDeclarationId(typeId, value.name); - if (nextPropertyDeclarationId) { - updateAliasIdPropertyDeclarationStatement.write(nextPropertyDeclarationId, - propertyDeclarationId); - updatePropertyAliasDeclarationRecursivelyWithTypeAndTraitsStatement - .write(propertyDeclarationId, propertyTypeId, value.traits); - } - } + const Storage::Synchronization::PropertyDeclaration &value, SourceId sourceId, TypeId typeId); void synchronizePropertyDeclarationsUpdateAlias( AliasPropertyDeclarations &updatedAliasPropertyDeclarations, const Storage::Synchronization::PropertyDeclarationView &view, const Storage::Synchronization::PropertyDeclaration &value, - SourceId sourceId) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"update property declaration to alias"_t, - projectStorageCategory(), - keyValue("property declaration", value), - keyValue("property declaration view", view)}; + SourceId sourceId); - updatedAliasPropertyDeclarations.emplace_back(view.typeId, - view.id, - fetchImportedTypeNameId(value.typeName, sourceId), - value.aliasPropertyName, - value.aliasPropertyNameTail, - view.aliasId); - } - - auto synchronizePropertyDeclarationsUpdateProperty( + Sqlite::UpdateChange synchronizePropertyDeclarationsUpdateProperty( const Storage::Synchronization::PropertyDeclarationView &view, const Storage::Synchronization::PropertyDeclaration &value, SourceId sourceId, - PropertyDeclarationIds &propertyDeclarationIds) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"update property declaration"_t, - projectStorageCategory(), - keyValue("property declaration", value), - keyValue("property declaration view", view)}; - - auto propertyImportedTypeNameId = fetchImportedTypeNameId(value.typeName, sourceId); - - auto propertyTypeId = fetchTypeId(propertyImportedTypeNameId); - - if (!propertyTypeId) - throw TypeNameDoesNotExists{fetchImportedTypeName(propertyImportedTypeNameId), sourceId}; - - if (view.traits == value.traits && propertyTypeId == view.typeId - && propertyImportedTypeNameId == view.typeNameId) - return Sqlite::UpdateChange::No; - - updatePropertyDeclarationStatement.write(view.id, - propertyTypeId, - value.traits, - propertyImportedTypeNameId); - updatePropertyAliasDeclarationRecursivelyWithTypeAndTraitsStatement.write(view.id, - propertyTypeId, - value.traits); - propertyDeclarationIds.push_back(view.id); - - tracer.end(keyValue("updated", "yes")); - - return Sqlite::UpdateChange::Update; - } + PropertyDeclarationIds &propertyDeclarationIds); void synchronizePropertyDeclarations( TypeId typeId, @@ -2607,73 +698,7 @@ private: SourceId sourceId, AliasPropertyDeclarations &insertedAliasPropertyDeclarations, AliasPropertyDeclarations &updatedAliasPropertyDeclarations, - PropertyDeclarationIds &propertyDeclarationIds) - { - NanotraceHR::Tracer tracer{"synchronize property declaration"_t, projectStorageCategory()}; - - std::sort(propertyDeclarations.begin(), - propertyDeclarations.end(), - [](auto &&first, auto &&second) { - return Sqlite::compare(first.name, second.name) < 0; - }); - - auto range = selectPropertyDeclarationsForTypeIdStatement - .template range(typeId); - - auto compareKey = [](const Storage::Synchronization::PropertyDeclarationView &view, - const Storage::Synchronization::PropertyDeclaration &value) { - return Sqlite::compare(view.name, value.name); - }; - - auto insert = [&](const Storage::Synchronization::PropertyDeclaration &value) { - if (value.kind == Storage::Synchronization::PropertyKind::Alias) { - synchronizePropertyDeclarationsInsertAlias(insertedAliasPropertyDeclarations, - value, - sourceId, - typeId); - } else { - synchronizePropertyDeclarationsInsertProperty(value, sourceId, typeId); - } - }; - - auto update = [&](const Storage::Synchronization::PropertyDeclarationView &view, - const Storage::Synchronization::PropertyDeclaration &value) { - if (value.kind == Storage::Synchronization::PropertyKind::Alias) { - synchronizePropertyDeclarationsUpdateAlias(updatedAliasPropertyDeclarations, - view, - value, - sourceId); - propertyDeclarationIds.push_back(view.id); - } else { - return synchronizePropertyDeclarationsUpdateProperty(view, - value, - sourceId, - propertyDeclarationIds); - } - - return Sqlite::UpdateChange::No; - }; - - auto remove = [&](const Storage::Synchronization::PropertyDeclarationView &view) { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"remove property declaration"_t, - projectStorageCategory(), - keyValue("property declaratio viewn", view)}; - - auto nextPropertyDeclarationId = fetchNextPropertyDeclarationId(typeId, view.name); - - if (nextPropertyDeclarationId) { - updateAliasPropertyDeclarationByAliasPropertyDeclarationIdStatement - .write(nextPropertyDeclarationId, view.id); - } - - updateDefaultPropertyIdToNullStatement.write(view.id); - deletePropertyDeclarationStatement.write(view.id); - propertyDeclarationIds.push_back(view.id); - }; - - Sqlite::insertUpdateDelete(range, propertyDeclarations, compareKey, insert, update, remove); - } + PropertyDeclarationIds &propertyDeclarationIds); class AliasPropertyDeclarationView { @@ -2706,237 +731,27 @@ private: }; void resetRemovedAliasPropertyDeclarationsToNull(Storage::Synchronization::Type &type, - PropertyDeclarationIds &propertyDeclarationIds) - { - NanotraceHR::Tracer tracer{"reset removed alias property declaration to null"_t, - projectStorageCategory()}; - - if (type.changeLevel == Storage::Synchronization::ChangeLevel::Minimal) - return; - - Storage::Synchronization::PropertyDeclarations &aliasDeclarations = type.propertyDeclarations; - - std::sort(aliasDeclarations.begin(), aliasDeclarations.end(), [](auto &&first, auto &&second) { - return Sqlite::compare(first.name, second.name) < 0; - }); - - auto range = selectPropertyDeclarationsWithAliasForTypeIdStatement - .template range(type.typeId); - - auto compareKey = [](const AliasPropertyDeclarationView &view, - const Storage::Synchronization::PropertyDeclaration &value) { - return Sqlite::compare(view.name, value.name); - }; - - auto insert = [&](const Storage::Synchronization::PropertyDeclaration &) {}; - - auto update = [&](const AliasPropertyDeclarationView &, - const Storage::Synchronization::PropertyDeclaration &) { - return Sqlite::UpdateChange::No; - }; - - auto remove = [&](const AliasPropertyDeclarationView &view) { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"reset removed alias property declaration to null"_t, - projectStorageCategory(), - keyValue("alias property declaration view", view)}; - - updatePropertyDeclarationAliasIdToNullStatement.write(view.id); - propertyDeclarationIds.push_back(view.id); - }; - - Sqlite::insertUpdateDelete(range, aliasDeclarations, compareKey, insert, update, remove); - } + PropertyDeclarationIds &propertyDeclarationIds); void resetRemovedAliasPropertyDeclarationsToNull( Storage::Synchronization::Types &types, - AliasPropertyDeclarations &relinkableAliasPropertyDeclarations) - { - NanotraceHR::Tracer tracer{"reset removed alias properties to null"_t, - projectStorageCategory()}; - - PropertyDeclarationIds propertyDeclarationIds; - propertyDeclarationIds.reserve(types.size()); - - for (auto &&type : types) - resetRemovedAliasPropertyDeclarationsToNull(type, propertyDeclarationIds); - - removeRelinkableEntries(relinkableAliasPropertyDeclarations, - propertyDeclarationIds, - PropertyCompare{}); - } + AliasPropertyDeclarations &relinkableAliasPropertyDeclarations); ImportId insertDocumentImport(const Storage::Import &import, Storage::Synchronization::ImportKind importKind, ModuleId sourceModuleId, - ImportId parentImportId) - { - if (import.version.minor) { - return insertDocumentImportWithVersionStatement - .template value(import.sourceId, - import.moduleId, - sourceModuleId, - importKind, - import.version.major.value, - import.version.minor.value, - parentImportId); - } else if (import.version.major) { - return insertDocumentImportWithMajorVersionStatement - .template value(import.sourceId, - import.moduleId, - sourceModuleId, - importKind, - import.version.major.value, - parentImportId); - } else { - return insertDocumentImportWithoutVersionStatement.template value( - import.sourceId, import.moduleId, sourceModuleId, importKind, parentImportId); - } - } + ImportId parentImportId); void synchronizeDocumentImports(Storage::Imports &imports, const SourceIds &updatedSourceIds, - Storage::Synchronization::ImportKind importKind) - { - std::sort(imports.begin(), imports.end(), [](auto &&first, auto &&second) { - return std::tie(first.sourceId, first.moduleId, first.version) - < std::tie(second.sourceId, second.moduleId, second.version); - }); + Storage::Synchronization::ImportKind importKind); - auto range = selectDocumentImportForSourceIdStatement - .template range(toIntegers( - updatedSourceIds), - importKind); - - auto compareKey = [](const Storage::Synchronization::ImportView &view, - const Storage::Import &import) -> long long { - auto sourceIdDifference = view.sourceId - import.sourceId; - if (sourceIdDifference != 0) - return sourceIdDifference; - - auto moduleIdDifference = view.moduleId - import.moduleId; - if (moduleIdDifference != 0) - return moduleIdDifference; - - auto versionDifference = view.version.major.value - import.version.major.value; - if (versionDifference != 0) - return versionDifference; - - return view.version.minor.value - import.version.minor.value; - }; - - auto insert = [&](const Storage::Import &import) { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"insert import"_t, - projectStorageCategory(), - keyValue("import", import), - keyValue("import kind", importKind), - keyValue("source id", import.sourceId), - keyValue("module id", import.moduleId)}; - - auto importId = insertDocumentImport(import, importKind, import.moduleId, ImportId{}); - auto callback = [&](ModuleId exportedModuleId, int majorVersion, int minorVersion) { - Storage::Import additionImport{exportedModuleId, - Storage::Version{majorVersion, minorVersion}, - import.sourceId}; - - auto exportedImportKind = importKind == Storage::Synchronization::ImportKind::Import - ? Storage::Synchronization::ImportKind::ModuleExportedImport - : Storage::Synchronization::ImportKind::ModuleExportedModuleDependency; - - NanotraceHR::Tracer tracer{"insert indirect import"_t, - projectStorageCategory(), - keyValue("import", import), - keyValue("import kind", exportedImportKind), - keyValue("source id", import.sourceId), - keyValue("module id", import.moduleId)}; - - auto indirectImportId = insertDocumentImport(additionImport, - exportedImportKind, - import.moduleId, - importId); - - tracer.end(keyValue("import id", indirectImportId)); - }; - - selectModuleExportedImportsForModuleIdStatement.readCallback(callback, - import.moduleId, - import.version.major.value, - import.version.minor.value); - tracer.end(keyValue("import id", importId)); - }; - - auto update = [](const Storage::Synchronization::ImportView &, const Storage::Import &) { - return Sqlite::UpdateChange::No; - }; - - auto remove = [&](const Storage::Synchronization::ImportView &view) { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"remove import"_t, - projectStorageCategory(), - keyValue("import", view), - keyValue("import id", view.importId), - keyValue("source id", view.sourceId), - keyValue("module id", view.moduleId)}; - - deleteDocumentImportStatement.write(view.importId); - deleteDocumentImportsWithParentImportIdStatement.write(view.sourceId, view.importId); - }; - - Sqlite::insertUpdateDelete(range, imports, compareKey, insert, update, remove); - } - - static Utils::PathString createJson(const Storage::Synchronization::ParameterDeclarations ¶meters) - { - NanotraceHR::Tracer tracer{"create json from parameter declarations"_t, - projectStorageCategory()}; - - Utils::PathString json; - json.append("["); - - Utils::SmallStringView comma{""}; - - for (const auto ¶meter : parameters) { - json.append(comma); - comma = ","; - json.append(R"({"n":")"); - json.append(parameter.name); - json.append(R"(","tn":")"); - json.append(parameter.typeName); - if (parameter.traits == Storage::PropertyDeclarationTraits::None) { - json.append("\"}"); - } else { - json.append(R"(","tr":)"); - json.append(Utils::SmallString::number(to_underlying(parameter.traits))); - json.append("}"); - } - } - - json.append("]"); - - return json; - } + static Utils::PathString createJson(const Storage::Synchronization::ParameterDeclarations ¶meters); TypeId fetchTypeIdByModuleIdAndExportedName(ModuleId moduleId, - Utils::SmallStringView name) const override - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch type id by module id and exported name"_t, - projectStorageCategory(), - keyValue("module id", moduleId), - keyValue("exported name", name)}; + Utils::SmallStringView name) const override; - return selectTypeIdByModuleIdAndExportedNameStatement.template value(moduleId, name); - } - - void addTypeIdToPropertyEditorQmlPaths(Storage::Synchronization::PropertyEditorQmlPaths &paths) - { - NanotraceHR::Tracer tracer{"add type id to property editor qml paths"_t, - projectStorageCategory()}; - - for (auto &path : paths) - path.typeId = fetchTypeIdByModuleIdAndExportedName(path.moduleId, path.typeName); - } + void addTypeIdToPropertyEditorQmlPaths(Storage::Synchronization::PropertyEditorQmlPaths &paths); class PropertyEditorQmlPathView { @@ -2967,358 +782,33 @@ private: }; void synchronizePropertyEditorPaths(Storage::Synchronization::PropertyEditorQmlPaths &paths, - SourceIds updatedPropertyEditorQmlPathsSourceIds) - { - using Storage::Synchronization::PropertyEditorQmlPath; - std::sort(paths.begin(), paths.end(), [](auto &&first, auto &&second) { - return first.typeId < second.typeId; - }); - - auto range = selectPropertyEditorPathsForForSourceIdsStatement - .template range( - toIntegers(updatedPropertyEditorQmlPathsSourceIds)); - - auto compareKey = [](const PropertyEditorQmlPathView &view, - const PropertyEditorQmlPath &value) -> long long { - return view.typeId - value.typeId; - }; - - auto insert = [&](const PropertyEditorQmlPath &path) { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"insert property editor paths"_t, - projectStorageCategory(), - keyValue("property editor qml path", path)}; - - if (path.typeId) - insertPropertyEditorPathStatement.write(path.typeId, path.pathId, path.directoryId); - }; - - auto update = [&](const PropertyEditorQmlPathView &view, const PropertyEditorQmlPath &value) { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"update property editor paths"_t, - projectStorageCategory(), - keyValue("property editor qml path", value), - keyValue("property editor qml path view", view)}; - - if (value.pathId != view.pathId || value.directoryId != view.directoryId) { - updatePropertyEditorPathsStatement.write(value.typeId, value.pathId, value.directoryId); - - tracer.end(keyValue("updated", "yes")); - - return Sqlite::UpdateChange::Update; - } - return Sqlite::UpdateChange::No; - }; - - auto remove = [&](const PropertyEditorQmlPathView &view) { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"remove property editor paths"_t, - projectStorageCategory(), - keyValue("property editor qml path view", view)}; - - deletePropertyEditorPathStatement.write(view.typeId); - }; - - Sqlite::insertUpdateDelete(range, paths, compareKey, insert, update, remove); - } + SourceIds updatedPropertyEditorQmlPathsSourceIds); void synchronizePropertyEditorQmlPaths(Storage::Synchronization::PropertyEditorQmlPaths &paths, - SourceIds updatedPropertyEditorQmlPathsSourceIds) - { - NanotraceHR::Tracer tracer{"synchronize property editor qml paths"_t, - projectStorageCategory()}; - - addTypeIdToPropertyEditorQmlPaths(paths); - synchronizePropertyEditorPaths(paths, updatedPropertyEditorQmlPathsSourceIds); - } + SourceIds updatedPropertyEditorQmlPathsSourceIds); void synchronizeFunctionDeclarations( - TypeId typeId, Storage::Synchronization::FunctionDeclarations &functionsDeclarations) - { - NanotraceHR::Tracer tracer{"synchronize function declaration"_t, projectStorageCategory()}; - - std::sort(functionsDeclarations.begin(), - functionsDeclarations.end(), - [](auto &&first, auto &&second) { - auto compare = Sqlite::compare(first.name, second.name); - - if (compare == 0) { - Utils::PathString firstSignature{createJson(first.parameters)}; - Utils::PathString secondSignature{createJson(second.parameters)}; - - return Sqlite::compare(firstSignature, secondSignature) < 0; - } - - return compare < 0; - }); - - auto range = selectFunctionDeclarationsForTypeIdStatement - .template range(typeId); - - auto compareKey = [](const Storage::Synchronization::FunctionDeclarationView &view, - const Storage::Synchronization::FunctionDeclaration &value) { - auto nameKey = Sqlite::compare(view.name, value.name); - if (nameKey != 0) - return nameKey; - - Utils::PathString valueSignature{createJson(value.parameters)}; - - return Sqlite::compare(view.signature, valueSignature); - }; - - auto insert = [&](const Storage::Synchronization::FunctionDeclaration &value) { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"insert function declaration"_t, - projectStorageCategory(), - keyValue("function declaration", value)}; - - Utils::PathString signature{createJson(value.parameters)}; - - insertFunctionDeclarationStatement.write(typeId, value.name, value.returnTypeName, signature); - }; - - auto update = [&](const Storage::Synchronization::FunctionDeclarationView &view, - const Storage::Synchronization::FunctionDeclaration &value) { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"update function declaration"_t, - projectStorageCategory(), - keyValue("function declaration", value), - keyValue("function declaration view", view)}; - - Utils::PathString signature{createJson(value.parameters)}; - - if (value.returnTypeName == view.returnTypeName) - return Sqlite::UpdateChange::No; - - updateFunctionDeclarationStatement.write(view.id, value.returnTypeName); - - tracer.end(keyValue("updated", "yes")); - - return Sqlite::UpdateChange::Update; - }; - - auto remove = [&](const Storage::Synchronization::FunctionDeclarationView &view) { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"remove function declaration"_t, - projectStorageCategory(), - keyValue("function declaration view", view)}; - - deleteFunctionDeclarationStatement.write(view.id); - }; - - Sqlite::insertUpdateDelete(range, functionsDeclarations, compareKey, insert, update, remove); - } + TypeId typeId, Storage::Synchronization::FunctionDeclarations &functionsDeclarations); void synchronizeSignalDeclarations(TypeId typeId, - Storage::Synchronization::SignalDeclarations &signalDeclarations) - { - NanotraceHR::Tracer tracer{"synchronize signal declaration"_t, projectStorageCategory()}; - - std::sort(signalDeclarations.begin(), signalDeclarations.end(), [](auto &&first, auto &&second) { - auto compare = Sqlite::compare(first.name, second.name); - - if (compare == 0) { - Utils::PathString firstSignature{createJson(first.parameters)}; - Utils::PathString secondSignature{createJson(second.parameters)}; - - return Sqlite::compare(firstSignature, secondSignature) < 0; - } - - return compare < 0; - }); - - auto range = selectSignalDeclarationsForTypeIdStatement - .template range(typeId); - - auto compareKey = [](const Storage::Synchronization::SignalDeclarationView &view, - const Storage::Synchronization::SignalDeclaration &value) { - auto nameKey = Sqlite::compare(view.name, value.name); - if (nameKey != 0) - return nameKey; - - Utils::PathString valueSignature{createJson(value.parameters)}; - - return Sqlite::compare(view.signature, valueSignature); - }; - - auto insert = [&](const Storage::Synchronization::SignalDeclaration &value) { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"insert signal declaration"_t, - projectStorageCategory(), - keyValue("signal declaration", value)}; - - Utils::PathString signature{createJson(value.parameters)}; - - insertSignalDeclarationStatement.write(typeId, value.name, signature); - }; - - auto update = [&]([[maybe_unused]] const Storage::Synchronization::SignalDeclarationView &view, - [[maybe_unused]] const Storage::Synchronization::SignalDeclaration &value) { - return Sqlite::UpdateChange::No; - }; - - auto remove = [&](const Storage::Synchronization::SignalDeclarationView &view) { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"remove signal declaration"_t, - projectStorageCategory(), - keyValue("signal declaration view", view)}; - - deleteSignalDeclarationStatement.write(view.id); - }; - - Sqlite::insertUpdateDelete(range, signalDeclarations, compareKey, insert, update, remove); - } + Storage::Synchronization::SignalDeclarations &signalDeclarations); static Utils::PathString createJson( - const Storage::Synchronization::EnumeratorDeclarations &enumeratorDeclarations) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"create json from enumerator declarations"_t, - projectStorageCategory()}; - - Utils::PathString json; - json.append("{"); - - Utils::SmallStringView comma{"\""}; - - for (const auto &enumerator : enumeratorDeclarations) { - json.append(comma); - comma = ",\""; - json.append(enumerator.name); - if (enumerator.hasValue) { - json.append("\":\""); - json.append(Utils::SmallString::number(enumerator.value)); - json.append("\""); - } else { - json.append("\":null"); - } - } - - json.append("}"); - - return json; - } + const Storage::Synchronization::EnumeratorDeclarations &enumeratorDeclarations); void synchronizeEnumerationDeclarations( - TypeId typeId, Storage::Synchronization::EnumerationDeclarations &enumerationDeclarations) - { - NanotraceHR::Tracer tracer{"synchronize enumeration declaration"_t, projectStorageCategory()}; - - std::sort(enumerationDeclarations.begin(), - enumerationDeclarations.end(), - [](auto &&first, auto &&second) { - return Sqlite::compare(first.name, second.name) < 0; - }); - - auto range = selectEnumerationDeclarationsForTypeIdStatement - .template range(typeId); - - auto compareKey = [](const Storage::Synchronization::EnumerationDeclarationView &view, - const Storage::Synchronization::EnumerationDeclaration &value) { - return Sqlite::compare(view.name, value.name); - }; - - auto insert = [&](const Storage::Synchronization::EnumerationDeclaration &value) { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"insert enumeration declaration"_t, - projectStorageCategory(), - keyValue("enumeration declaration", value)}; - - Utils::PathString signature{createJson(value.enumeratorDeclarations)}; - - insertEnumerationDeclarationStatement.write(typeId, value.name, signature); - }; - - auto update = [&](const Storage::Synchronization::EnumerationDeclarationView &view, - const Storage::Synchronization::EnumerationDeclaration &value) { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"update enumeration declaration"_t, - projectStorageCategory(), - keyValue("enumeration declaration", value), - keyValue("enumeration declaration view", view)}; - - Utils::PathString enumeratorDeclarations{createJson(value.enumeratorDeclarations)}; - - if (enumeratorDeclarations == view.enumeratorDeclarations) - return Sqlite::UpdateChange::No; - - updateEnumerationDeclarationStatement.write(view.id, enumeratorDeclarations); - - tracer.end(keyValue("updated", "yes")); - - return Sqlite::UpdateChange::Update; - }; - - auto remove = [&](const Storage::Synchronization::EnumerationDeclarationView &view) { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"remove enumeration declaration"_t, - projectStorageCategory(), - keyValue("enumeration declaration view", view)}; - - deleteEnumerationDeclarationStatement.write(view.id); - }; - - Sqlite::insertUpdateDelete(range, enumerationDeclarations, compareKey, insert, update, remove); - } + TypeId typeId, Storage::Synchronization::EnumerationDeclarations &enumerationDeclarations); void extractExportedTypes(TypeId typeId, const Storage::Synchronization::Type &type, - Storage::Synchronization::ExportedTypes &exportedTypes) - { - for (const auto &exportedType : type.exportedTypes) - exportedTypes.emplace_back(exportedType.name, - exportedType.version, - typeId, - exportedType.moduleId); - } + Storage::Synchronization::ExportedTypes &exportedTypes); - TypeId declareType(Storage::Synchronization::Type &type) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"declare type"_t, - projectStorageCategory(), - keyValue("source id", type.sourceId), - keyValue("type name", type.typeName)}; - - if (type.typeName.isEmpty()) { - type.typeId = selectTypeIdBySourceIdStatement.template value(type.sourceId); - - tracer.end(keyValue("type id", type.typeId)); - - return type.typeId; - } - - type.typeId = insertTypeStatement.template value(type.sourceId, type.typeName); - - if (!type.typeId) - type.typeId = selectTypeIdBySourceIdAndNameStatement.template value(type.sourceId, - type.typeName); - - tracer.end(keyValue("type id", type.typeId)); - - return type.typeId; - } + TypeId declareType(Storage::Synchronization::Type &type); void syncDeclarations(Storage::Synchronization::Type &type, AliasPropertyDeclarations &insertedAliasPropertyDeclarations, AliasPropertyDeclarations &updatedAliasPropertyDeclarations, - PropertyDeclarationIds &propertyDeclarationIds) - { - NanotraceHR::Tracer tracer{"synchronize declaration per type"_t, projectStorageCategory()}; - - if (type.changeLevel == Storage::Synchronization::ChangeLevel::Minimal) - return; - - synchronizePropertyDeclarations(type.typeId, - type.propertyDeclarations, - type.sourceId, - insertedAliasPropertyDeclarations, - updatedAliasPropertyDeclarations, - propertyDeclarationIds); - synchronizeFunctionDeclarations(type.typeId, type.functionDeclarations); - synchronizeSignalDeclarations(type.typeId, type.signalDeclarations); - synchronizeEnumerationDeclarations(type.typeId, type.enumerationDeclarations); - } + PropertyDeclarationIds &propertyDeclarationIds); template void removeRelinkableEntries(std::vector &relinkables, Ids &ids, Compare compare) @@ -3345,23 +835,7 @@ private: void syncDeclarations(Storage::Synchronization::Types &types, AliasPropertyDeclarations &insertedAliasPropertyDeclarations, AliasPropertyDeclarations &updatedAliasPropertyDeclarations, - PropertyDeclarations &relinkablePropertyDeclarations) - { - NanotraceHR::Tracer tracer{"synchronize declaration"_t, projectStorageCategory()}; - - PropertyDeclarationIds propertyDeclarationIds; - propertyDeclarationIds.reserve(types.size() * 10); - - for (auto &&type : types) - syncDeclarations(type, - insertedAliasPropertyDeclarations, - updatedAliasPropertyDeclarations, - propertyDeclarationIds); - - removeRelinkableEntries(relinkablePropertyDeclarations, - propertyDeclarationIds, - PropertyCompare{}); - } + PropertyDeclarations &relinkablePropertyDeclarations); class TypeWithDefaultPropertyView { @@ -3386,279 +860,27 @@ private: PropertyDeclarationId defaultPropertyId; }; - void syncDefaultProperties(Storage::Synchronization::Types &types) - { - NanotraceHR::Tracer tracer{"synchronize default properties"_t, projectStorageCategory()}; + void syncDefaultProperties(Storage::Synchronization::Types &types); - auto range = selectTypesWithDefaultPropertyStatement.template range(); + void resetDefaultPropertiesIfChanged(Storage::Synchronization::Types &types); - auto compareKey = [](const TypeWithDefaultPropertyView &view, - const Storage::Synchronization::Type &value) { - return view.typeId - value.typeId; - }; + void checkForPrototypeChainCycle(TypeId typeId) const; - auto insert = [&](const Storage::Synchronization::Type &) { - - }; - - auto update = [&](const TypeWithDefaultPropertyView &view, - const Storage::Synchronization::Type &value) { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"synchronize default properties by update"_t, - projectStorageCategory(), - keyValue("type id", value.typeId), - keyValue("value", value), - keyValue("view", view)}; - - PropertyDeclarationId valueDefaultPropertyId; - if (value.defaultPropertyName.size()) - valueDefaultPropertyId = fetchPropertyDeclarationByTypeIdAndNameUngarded( - value.typeId, value.defaultPropertyName) - .propertyDeclarationId; - - if (compareInvalidAreTrue(valueDefaultPropertyId, view.defaultPropertyId)) - return Sqlite::UpdateChange::No; - - updateDefaultPropertyIdStatement.write(value.typeId, valueDefaultPropertyId); - - tracer.end(keyValue("updated", "yes"), - keyValue("default property id", valueDefaultPropertyId)); - - return Sqlite::UpdateChange::Update; - }; - - auto remove = [&](const TypeWithDefaultPropertyView &) {}; - - Sqlite::insertUpdateDelete(range, types, compareKey, insert, update, remove); - } - - void resetDefaultPropertiesIfChanged(Storage::Synchronization::Types &types) - { - NanotraceHR::Tracer tracer{"reset changed default properties"_t, projectStorageCategory()}; - - auto range = selectTypesWithDefaultPropertyStatement.template range(); - - auto compareKey = [](const TypeWithDefaultPropertyView &view, - const Storage::Synchronization::Type &value) { - return view.typeId - value.typeId; - }; - - auto insert = [&](const Storage::Synchronization::Type &) { - - }; - - auto update = [&](const TypeWithDefaultPropertyView &view, - const Storage::Synchronization::Type &value) { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"reset changed default properties by update"_t, - projectStorageCategory(), - keyValue("type id", value.typeId), - keyValue("value", value), - keyValue("view", view)}; - - PropertyDeclarationId valueDefaultPropertyId; - if (value.defaultPropertyName.size()) { - auto optionalValueDefaultPropertyId = fetchOptionalPropertyDeclarationByTypeIdAndNameUngarded( - value.typeId, value.defaultPropertyName); - if (optionalValueDefaultPropertyId) - valueDefaultPropertyId = optionalValueDefaultPropertyId->propertyDeclarationId; - } - - if (compareInvalidAreTrue(valueDefaultPropertyId, view.defaultPropertyId)) - return Sqlite::UpdateChange::No; - - updateDefaultPropertyIdStatement.write(value.typeId, Sqlite::NullValue{}); - - tracer.end(keyValue("updated", "yes")); - - return Sqlite::UpdateChange::Update; - }; - - auto remove = [&](const TypeWithDefaultPropertyView &) {}; - - Sqlite::insertUpdateDelete(range, types, compareKey, insert, update, remove); - } - - void checkForPrototypeChainCycle(TypeId typeId) const - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"check for prototype chain cycle"_t, - projectStorageCategory(), - keyValue("type id", typeId)}; - - auto callback = [=](TypeId currentTypeId) { - if (typeId == currentTypeId) - throw PrototypeChainCycle{}; - }; - - selectPrototypeAndExtensionIdsStatement.readCallback(callback, typeId); - } - - void checkForAliasChainCycle(PropertyDeclarationId propertyDeclarationId) const - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"check for alias chain cycle"_t, - projectStorageCategory(), - keyValue("property declaration id", propertyDeclarationId)}; - auto callback = [=](PropertyDeclarationId currentPropertyDeclarationId) { - if (propertyDeclarationId == currentPropertyDeclarationId) - throw AliasChainCycle{}; - }; - - selectPropertyDeclarationIdsForAliasChainStatement.readCallback(callback, - propertyDeclarationId); - } + void checkForAliasChainCycle(PropertyDeclarationId propertyDeclarationId) const; std::pair fetchImportedTypeNameIdAndTypeId( - const Storage::Synchronization::ImportedTypeName &typeName, SourceId sourceId) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch imported type name id and type id"_t, - projectStorageCategory(), - keyValue("imported type name", typeName), - keyValue("source id", sourceId)}; + const Storage::Synchronization::ImportedTypeName &typeName, SourceId sourceId); - TypeId typeId; - ImportedTypeNameId typeNameId; - if (!std::visit([](auto &&typeName_) -> bool { return typeName_.name.isEmpty(); }, typeName)) { - typeNameId = fetchImportedTypeNameId(typeName, sourceId); - - typeId = fetchTypeId(typeNameId); - - tracer.end(keyValue("type id", typeId), keyValue("type name id", typeNameId)); - - if (!typeId) - throw TypeNameDoesNotExists{fetchImportedTypeName(typeNameId), sourceId}; - } - - return {typeId, typeNameId}; - } - - void syncPrototypeAndExtension(Storage::Synchronization::Type &type, TypeIds &typeIds) - { - if (type.changeLevel == Storage::Synchronization::ChangeLevel::Minimal) - return; - - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"synchronize prototype and extension"_t, - projectStorageCategory(), - keyValue("prototype", type.prototype), - keyValue("extension", type.extension), - keyValue("type id", type.typeId), - keyValue("source id", type.sourceId)}; - - auto [prototypeId, prototypeTypeNameId] = fetchImportedTypeNameIdAndTypeId(type.prototype, - type.sourceId); - auto [extensionId, extensionTypeNameId] = fetchImportedTypeNameIdAndTypeId(type.extension, - type.sourceId); - - updatePrototypeAndExtensionStatement.write(type.typeId, - prototypeId, - prototypeTypeNameId, - extensionId, - extensionTypeNameId); - - if (prototypeId || extensionId) - checkForPrototypeChainCycle(type.typeId); - - typeIds.push_back(type.typeId); - - tracer.end(keyValue("prototype id", prototypeId), - keyValue("prototype type name id", prototypeTypeNameId), - keyValue("extension id", extensionId), - keyValue("extension type name id", extensionTypeNameId)); - } + void syncPrototypeAndExtension(Storage::Synchronization::Type &type, TypeIds &typeIds); void syncPrototypesAndExtensions(Storage::Synchronization::Types &types, Prototypes &relinkablePrototypes, - Prototypes &relinkableExtensions) - { - NanotraceHR::Tracer tracer{"synchronize prototypes and extensions"_t, - projectStorageCategory()}; + Prototypes &relinkableExtensions); - TypeIds typeIds; - typeIds.reserve(types.size()); - - for (auto &type : types) - syncPrototypeAndExtension(type, typeIds); - - removeRelinkableEntries(relinkablePrototypes, typeIds, TypeCompare{}); - removeRelinkableEntries(relinkableExtensions, typeIds, TypeCompare{}); - } - - ImportId fetchImportId(SourceId sourceId, const Storage::Import &import) const - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch imported type name id"_t, - projectStorageCategory(), - keyValue("import", import), - keyValue("source id", sourceId)}; - - ImportId importId; - if (import.version) { - importId = selectImportIdBySourceIdAndModuleIdAndVersionStatement.template value( - sourceId, import.moduleId, import.version.major.value, import.version.minor.value); - } else if (import.version.major) { - importId = selectImportIdBySourceIdAndModuleIdAndMajorVersionStatement - .template value(sourceId, - import.moduleId, - import.version.major.value); - } else { - importId = selectImportIdBySourceIdAndModuleIdStatement - .template value(sourceId, import.moduleId); - } - - tracer.end(keyValue("import id", importId)); - - return importId; - } + ImportId fetchImportId(SourceId sourceId, const Storage::Import &import) const; ImportedTypeNameId fetchImportedTypeNameId(const Storage::Synchronization::ImportedTypeName &name, - SourceId sourceId) - { - struct Inspect - { - auto operator()(const Storage::Synchronization::ImportedType &importedType) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch imported type name id"_t, - projectStorageCategory(), - keyValue("imported type name", importedType.name), - keyValue("source id", sourceId), - keyValue("type name kind", "exported"sv)}; - - return storage.fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::Exported, - sourceId, - importedType.name); - } - - auto operator()(const Storage::Synchronization::QualifiedImportedType &importedType) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch imported type name id"_t, - projectStorageCategory(), - keyValue("imported type name", importedType.name), - keyValue("import", importedType.import), - keyValue("type name kind", "qualified exported"sv)}; - - ImportId importId = storage.fetchImportId(sourceId, importedType.import); - - auto importedTypeNameId = storage.fetchImportedTypeNameId( - Storage::Synchronization::TypeNameKind::QualifiedExported, - importId, - importedType.name); - - tracer.end(keyValue("import id", importId), keyValue("source id", sourceId)); - - return importedTypeNameId; - } - - ProjectStorage &storage; - SourceId sourceId; - }; - - return std::visit(Inspect{*this, sourceId}, name); - } + SourceId sourceId); template ImportedTypeNameId fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind kind, @@ -3671,60 +893,26 @@ private: keyValue("imported type name", typeName), keyValue("kind", kind)}; - auto importedTypeNameId = selectImportedTypeNameIdStatement - .template value(kind, id, typeName); + auto importedTypeNameId = selectImportedTypeNameIdStatement.value(kind, + id, + typeName); if (!importedTypeNameId) - importedTypeNameId = insertImportedTypeNameIdStatement - .template value(kind, id, typeName); + importedTypeNameId = insertImportedTypeNameIdStatement.value(kind, + id, + typeName); tracer.end(keyValue("imported type name id", importedTypeNameId)); return importedTypeNameId; } - TypeId fetchTypeId(ImportedTypeNameId typeNameId) const - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch type id with type name kind"_t, - projectStorageCategory(), - keyValue("type name id", typeNameId)}; + TypeId fetchTypeId(ImportedTypeNameId typeNameId) const; - auto kind = selectKindFromImportedTypeNamesStatement - .template value(typeNameId); + Utils::SmallString fetchImportedTypeName(ImportedTypeNameId typeNameId) const; - auto typeId = fetchTypeId(typeNameId, kind); - - tracer.end(keyValue("type id", typeId), keyValue("type name kind", kind)); - - return typeId; - } - - Utils::SmallString fetchImportedTypeName(ImportedTypeNameId typeNameId) const - { - return selectNameFromImportedTypeNamesStatement.template value(typeNameId); - } - - TypeId fetchTypeId(ImportedTypeNameId typeNameId, Storage::Synchronization::TypeNameKind kind) const - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch type id"_t, - projectStorageCategory(), - keyValue("type name id", typeNameId), - keyValue("type name kind", kind)}; - - TypeId typeId; - if (kind == Storage::Synchronization::TypeNameKind::Exported) { - typeId = selectTypeIdForImportedTypeNameNamesStatement.template value(typeNameId); - } else { - typeId = selectTypeIdForQualifiedImportedTypeNameNamesStatement.template value( - typeNameId); - } - - tracer.end(keyValue("type id", typeId)); - - return typeId; - } + TypeId fetchTypeId(ImportedTypeNameId typeNameId, + Storage::Synchronization::TypeNameKind kind) const; class FetchPropertyDeclarationResult { @@ -3755,641 +943,41 @@ private: Storage::PropertyDeclarationTraits propertyTraits; }; - auto fetchOptionalPropertyDeclarationByTypeIdAndNameUngarded(TypeId typeId, - Utils::SmallStringView name) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch optional property declaration by type id and name ungarded"_t, - projectStorageCategory(), - keyValue("type id", typeId), - keyValue("property name", name)}; - - auto propertyDeclarationId = fetchPropertyDeclarationId(typeId, name); - auto propertyDeclaration = selectPropertyDeclarationResultByPropertyDeclarationIdStatement - .template optionalValue( - propertyDeclarationId); - - tracer.end(keyValue("property declaration", propertyDeclaration)); - - return propertyDeclaration; - } + std::optional fetchOptionalPropertyDeclarationByTypeIdAndNameUngarded( + TypeId typeId, Utils::SmallStringView name); FetchPropertyDeclarationResult fetchPropertyDeclarationByTypeIdAndNameUngarded( - TypeId typeId, Utils::SmallStringView name) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch property declaration by type id and name ungarded"_t, - projectStorageCategory(), - keyValue("type id", typeId), - keyValue("property name", name)}; - - auto propertyDeclaration = fetchOptionalPropertyDeclarationByTypeIdAndNameUngarded(typeId, - name); - tracer.end(keyValue("property declaration", propertyDeclaration)); - - if (propertyDeclaration) - return *propertyDeclaration; - - throw PropertyNameDoesNotExists{}; - } + TypeId typeId, Utils::SmallStringView name); PropertyDeclarationId fetchPropertyDeclarationIdByTypeIdAndNameUngarded(TypeId typeId, - Utils::SmallStringView name) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch property declaration id by type id and name ungarded"_t, - projectStorageCategory(), - keyValue("type id", typeId), - keyValue("property name", name)}; + Utils::SmallStringView name); - auto propertyDeclarationId = fetchPropertyDeclarationId(typeId, name); + SourceContextId readSourceContextId(Utils::SmallStringView sourceContextPath); - tracer.end(keyValue("property declaration id", propertyDeclarationId)); + SourceContextId writeSourceContextId(Utils::SmallStringView sourceContextPath); - if (propertyDeclarationId) - return propertyDeclarationId; + SourceId writeSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName); - throw PropertyNameDoesNotExists{}; - } + SourceId readSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName); - SourceContextId readSourceContextId(Utils::SmallStringView sourceContextPath) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"read source context id"_t, - projectStorageCategory(), - keyValue("source context path", sourceContextPath)}; + Storage::Synchronization::ExportedTypes fetchExportedTypes(TypeId typeId); - auto sourceContextId = selectSourceContextIdFromSourceContextsBySourceContextPathStatement - .template value(sourceContextPath); + Storage::Synchronization::PropertyDeclarations fetchPropertyDeclarations(TypeId typeId); - tracer.end(keyValue("source context id", sourceContextId)); + Storage::Synchronization::FunctionDeclarations fetchFunctionDeclarations(TypeId typeId); - return sourceContextId; - } + Storage::Synchronization::SignalDeclarations fetchSignalDeclarations(TypeId typeId); - SourceContextId writeSourceContextId(Utils::SmallStringView sourceContextPath) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"write source context id"_t, - projectStorageCategory(), - keyValue("source context path", sourceContextPath)}; + Storage::Synchronization::EnumerationDeclarations fetchEnumerationDeclarations(TypeId typeId); - insertIntoSourceContextsStatement.write(sourceContextPath); - - auto sourceContextId = SourceContextId::create(database.lastInsertedRowId()); - - tracer.end(keyValue("source context id", sourceContextId)); - - return sourceContextId; - } - - SourceId writeSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"write source id"_t, - projectStorageCategory(), - keyValue("source context id", sourceContextId), - keyValue("source name", sourceName)}; - - insertIntoSourcesStatement.write(sourceContextId, sourceName); - - auto sourceId = SourceId::create(database.lastInsertedRowId()); - - tracer.end(keyValue("source id", sourceId)); - - return sourceId; - } - - SourceId readSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"read source id"_t, - projectStorageCategory(), - keyValue("source context id", sourceContextId), - keyValue("source name", sourceName)}; - - auto sourceId = selectSourceIdFromSourcesBySourceContextIdAndSourceNameStatement - .template value(sourceContextId, sourceName); - - tracer.end(keyValue("source id", sourceId)); - - return sourceId; - } - - auto fetchExportedTypes(TypeId typeId) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch exported type"_t, - projectStorageCategory(), - keyValue("type id", typeId)}; - - auto typeIds = selectExportedTypesByTypeIdStatement - .template values(typeId); - - tracer.end(keyValue("type ids", typeIds)); - - return typeIds; - } - - auto fetchPropertyDeclarations(TypeId typeId) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch property declarations"_t, - projectStorageCategory(), - keyValue("type id", typeId)}; - - auto propertyDeclarations = selectPropertyDeclarationsByTypeIdStatement - .template values( - typeId); - - tracer.end(keyValue("property declarations", propertyDeclarations)); - - return propertyDeclarations; - } - - auto fetchFunctionDeclarations(TypeId typeId) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch signal declarations"_t, - projectStorageCategory(), - keyValue("type id", typeId)}; - - Storage::Synchronization::FunctionDeclarations functionDeclarations; - - auto callback = [&](Utils::SmallStringView name, - Utils::SmallStringView returnType, - FunctionDeclarationId functionDeclarationId) { - auto &functionDeclaration = functionDeclarations.emplace_back(name, returnType); - functionDeclaration.parameters = selectFunctionParameterDeclarationsStatement - .template values(functionDeclarationId); - }; - - selectFunctionDeclarationsForTypeIdWithoutSignatureStatement.readCallback(callback, typeId); - - tracer.end(keyValue("function declarations", functionDeclarations)); - - return functionDeclarations; - } - - auto fetchSignalDeclarations(TypeId typeId) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch signal declarations"_t, - projectStorageCategory(), - keyValue("type id", typeId)}; - - Storage::Synchronization::SignalDeclarations signalDeclarations; - - auto callback = [&](Utils::SmallStringView name, SignalDeclarationId signalDeclarationId) { - auto &signalDeclaration = signalDeclarations.emplace_back(name); - signalDeclaration.parameters = selectSignalParameterDeclarationsStatement - .template values(signalDeclarationId); - }; - - selectSignalDeclarationsForTypeIdWithoutSignatureStatement.readCallback(callback, typeId); - - tracer.end(keyValue("signal declarations", signalDeclarations)); - - return signalDeclarations; - } - - auto fetchEnumerationDeclarations(TypeId typeId) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch enumeration declarations"_t, - projectStorageCategory(), - keyValue("type id", typeId)}; - - Storage::Synchronization::EnumerationDeclarations enumerationDeclarations; - - auto callback = [&](Utils::SmallStringView name, - EnumerationDeclarationId enumerationDeclarationId) { - enumerationDeclarations.emplace_back( - name, - selectEnumeratorDeclarationStatement - .template values( - enumerationDeclarationId)); - }; - - selectEnumerationDeclarationsForTypeIdWithoutEnumeratorDeclarationsStatement - .readCallback(callback, typeId); - - tracer.end(keyValue("enumeration declarations", enumerationDeclarations)); - - return enumerationDeclarations; - } - - class Initializer - { - public: - Initializer(Database &database, bool isInitialized) - { - if (!isInitialized) { - auto moduleIdColumn = createModulesTable(database); - createSourceContextsTable(database); - createSourcesTable(database); - createTypesAndePropertyDeclarationsTables(database, moduleIdColumn); - createExportedTypeNamesTable(database, moduleIdColumn); - createImportedTypeNamesTable(database); - createEnumerationsTable(database); - createFunctionsTable(database); - createSignalsTable(database); - createModuleExportedImportsTable(database, moduleIdColumn); - createDocumentImportsTable(database, moduleIdColumn); - createFileStatusesTable(database); - createProjectDatasTable(database); - createPropertyEditorPathsTable(database); - createTypeAnnotionsTable(database); - } - database.setIsInitialized(true); - } - - void createSourceContextsTable(Database &database) - { - Sqlite::Table table; - table.setUseIfNotExists(true); - table.setName("sourceContexts"); - table.addColumn("sourceContextId", Sqlite::ColumnType::Integer, {Sqlite::PrimaryKey{}}); - const Sqlite::Column &sourceContextPathColumn = table.addColumn("sourceContextPath"); - - table.addUniqueIndex({sourceContextPathColumn}); - - table.initialize(database); - } - - void createSourcesTable(Database &database) - { - Sqlite::StrictTable table; - table.setUseIfNotExists(true); - table.setName("sources"); - table.addColumn("sourceId", Sqlite::StrictColumnType::Integer, {Sqlite::PrimaryKey{}}); - const auto &sourceContextIdColumn = table.addColumn( - "sourceContextId", - Sqlite::StrictColumnType::Integer, - {Sqlite::NotNull{}, - Sqlite::ForeignKey{"sourceContexts", - "sourceContextId", - Sqlite::ForeignKeyAction::NoAction, - Sqlite::ForeignKeyAction::Cascade}}); - const auto &sourceNameColumn = table.addColumn("sourceName", - Sqlite::StrictColumnType::Text); - table.addUniqueIndex({sourceContextIdColumn, sourceNameColumn}); - - table.initialize(database); - } - - void createTypesAndePropertyDeclarationsTables( - Database &database, [[maybe_unused]] const Sqlite::StrictColumn &foreignModuleIdColumn) - { - Sqlite::StrictTable typesTable; - typesTable.setUseIfNotExists(true); - typesTable.setName("types"); - typesTable.addColumn("typeId", Sqlite::StrictColumnType::Integer, {Sqlite::PrimaryKey{}}); - auto &sourceIdColumn = typesTable.addColumn("sourceId", Sqlite::StrictColumnType::Integer); - auto &typesNameColumn = typesTable.addColumn("name", Sqlite::StrictColumnType::Text); - typesTable.addColumn("traits", Sqlite::StrictColumnType::Integer); - auto &prototypeIdColumn = typesTable.addForeignKeyColumn("prototypeId", - typesTable, - Sqlite::ForeignKeyAction::NoAction, - Sqlite::ForeignKeyAction::Restrict); - typesTable.addColumn("prototypeNameId", Sqlite::StrictColumnType::Integer); - auto &extensionIdColumn = typesTable.addForeignKeyColumn("extensionId", - typesTable, - Sqlite::ForeignKeyAction::NoAction, - Sqlite::ForeignKeyAction::Restrict); - typesTable.addColumn("extensionNameId", Sqlite::StrictColumnType::Integer); - auto &defaultPropertyIdColumn = typesTable.addColumn("defaultPropertyId", - Sqlite::StrictColumnType::Integer); - typesTable.addColumn("annotationTraits", Sqlite::StrictColumnType::Integer); - typesTable.addUniqueIndex({sourceIdColumn, typesNameColumn}); - typesTable.addIndex({defaultPropertyIdColumn}); - typesTable.addIndex({prototypeIdColumn}); - typesTable.addIndex({extensionIdColumn}); - - typesTable.initialize(database); - - { - Sqlite::StrictTable propertyDeclarationTable; - propertyDeclarationTable.setUseIfNotExists(true); - propertyDeclarationTable.setName("propertyDeclarations"); - propertyDeclarationTable.addColumn("propertyDeclarationId", - Sqlite::StrictColumnType::Integer, - {Sqlite::PrimaryKey{}}); - auto &typeIdColumn = propertyDeclarationTable.addColumn("typeId"); - auto &nameColumn = propertyDeclarationTable.addColumn("name"); - auto &propertyTypeIdColumn = propertyDeclarationTable.addForeignKeyColumn( - "propertyTypeId", - typesTable, - Sqlite::ForeignKeyAction::NoAction, - Sqlite::ForeignKeyAction::Restrict); - propertyDeclarationTable.addColumn("propertyTraits", - Sqlite::StrictColumnType::Integer); - propertyDeclarationTable.addColumn("propertyImportedTypeNameId", - Sqlite::StrictColumnType::Integer); - auto &aliasPropertyDeclarationIdColumn = propertyDeclarationTable.addForeignKeyColumn( - "aliasPropertyDeclarationId", - propertyDeclarationTable, - Sqlite::ForeignKeyAction::NoAction, - Sqlite::ForeignKeyAction::Restrict); - auto &aliasPropertyDeclarationTailIdColumn = propertyDeclarationTable.addForeignKeyColumn( - "aliasPropertyDeclarationTailId", - propertyDeclarationTable, - Sqlite::ForeignKeyAction::NoAction, - Sqlite::ForeignKeyAction::Restrict); - - propertyDeclarationTable.addUniqueIndex({typeIdColumn, nameColumn}); - propertyDeclarationTable.addIndex({propertyTypeIdColumn}); - propertyDeclarationTable.addIndex({aliasPropertyDeclarationIdColumn}, - "aliasPropertyDeclarationId IS NOT NULL"); - propertyDeclarationTable.addIndex({aliasPropertyDeclarationTailIdColumn}, - "aliasPropertyDeclarationTailId IS NOT NULL"); - - propertyDeclarationTable.initialize(database); - } - } - - void createExportedTypeNamesTable(Database &database, - const Sqlite::StrictColumn &foreignModuleIdColumn) - { - Sqlite::StrictTable table; - table.setUseIfNotExists(true); - table.setName("exportedTypeNames"); - table.addColumn("exportedTypeNameId", - Sqlite::StrictColumnType::Integer, - {Sqlite::PrimaryKey{}}); - auto &moduleIdColumn = table.addForeignKeyColumn("moduleId", - foreignModuleIdColumn, - Sqlite::ForeignKeyAction::NoAction, - Sqlite::ForeignKeyAction::NoAction); - auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text); - auto &typeIdColumn = table.addColumn("typeId", Sqlite::StrictColumnType::Integer); - auto &majorVersionColumn = table.addColumn("majorVersion", - Sqlite::StrictColumnType::Integer); - auto &minorVersionColumn = table.addColumn("minorVersion", - Sqlite::StrictColumnType::Integer); - - table.addUniqueIndex({moduleIdColumn, nameColumn}, - "majorVersion IS NULL AND minorVersion IS NULL"); - table.addUniqueIndex({moduleIdColumn, nameColumn, majorVersionColumn}, - "majorVersion IS NOT NULL AND minorVersion IS NULL"); - table.addUniqueIndex({moduleIdColumn, nameColumn, majorVersionColumn, minorVersionColumn}, - "majorVersion IS NOT NULL AND minorVersion IS NOT NULL"); - - table.addIndex({typeIdColumn}); - table.addIndex({moduleIdColumn, nameColumn}); - - table.initialize(database); - } - - void createImportedTypeNamesTable(Database &database) - { - Sqlite::StrictTable table; - table.setUseIfNotExists(true); - table.setName("importedTypeNames"); - table.addColumn("importedTypeNameId", - Sqlite::StrictColumnType::Integer, - {Sqlite::PrimaryKey{}}); - auto &importOrSourceIdColumn = table.addColumn("importOrSourceId"); - auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text); - auto &kindColumn = table.addColumn("kind", Sqlite::StrictColumnType::Integer); - - table.addUniqueIndex({kindColumn, importOrSourceIdColumn, nameColumn}); - table.addIndex({nameColumn}); - - table.initialize(database); - } - - void createEnumerationsTable(Database &database) - { - Sqlite::StrictTable table; - table.setUseIfNotExists(true); - table.setName("enumerationDeclarations"); - table.addColumn("enumerationDeclarationId", - Sqlite::StrictColumnType::Integer, - {Sqlite::PrimaryKey{}}); - auto &typeIdColumn = table.addColumn("typeId", Sqlite::StrictColumnType::Integer); - auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text); - table.addColumn("enumeratorDeclarations", Sqlite::StrictColumnType::Text); - - table.addUniqueIndex({typeIdColumn, nameColumn}); - - table.initialize(database); - } - - void createFunctionsTable(Database &database) - { - Sqlite::StrictTable table; - table.setUseIfNotExists(true); - table.setName("functionDeclarations"); - table.addColumn("functionDeclarationId", - Sqlite::StrictColumnType::Integer, - {Sqlite::PrimaryKey{}}); - auto &typeIdColumn = table.addColumn("typeId", Sqlite::StrictColumnType::Integer); - auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text); - auto &signatureColumn = table.addColumn("signature", Sqlite::StrictColumnType::Text); - table.addColumn("returnTypeName"); - - table.addUniqueIndex({typeIdColumn, nameColumn, signatureColumn}); - - table.initialize(database); - } - - void createSignalsTable(Database &database) - { - Sqlite::StrictTable table; - table.setUseIfNotExists(true); - table.setName("signalDeclarations"); - table.addColumn("signalDeclarationId", - Sqlite::StrictColumnType::Integer, - {Sqlite::PrimaryKey{}}); - auto &typeIdColumn = table.addColumn("typeId", Sqlite::StrictColumnType::Integer); - auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text); - auto &signatureColumn = table.addColumn("signature", Sqlite::StrictColumnType::Text); - - table.addUniqueIndex({typeIdColumn, nameColumn, signatureColumn}); - - table.initialize(database); - } - - Sqlite::StrictColumn createModulesTable(Database &database) - { - Sqlite::StrictTable table; - table.setUseIfNotExists(true); - table.setName("modules"); - auto &modelIdColumn = table.addColumn("moduleId", - Sqlite::StrictColumnType::Integer, - {Sqlite::PrimaryKey{}}); - auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text); - - table.addUniqueIndex({nameColumn}); - - table.initialize(database); - - return std::move(modelIdColumn); - } - - void createModuleExportedImportsTable(Database &database, - const Sqlite::StrictColumn &foreignModuleIdColumn) - { - Sqlite::StrictTable table; - table.setUseIfNotExists(true); - table.setName("moduleExportedImports"); - table.addColumn("moduleExportedImportId", - Sqlite::StrictColumnType::Integer, - {Sqlite::PrimaryKey{}}); - auto &moduleIdColumn = table.addForeignKeyColumn("moduleId", - foreignModuleIdColumn, - Sqlite::ForeignKeyAction::NoAction, - Sqlite::ForeignKeyAction::Cascade, - Sqlite::Enforment::Immediate); - auto &sourceIdColumn = table.addColumn("exportedModuleId", - Sqlite::StrictColumnType::Integer); - table.addColumn("isAutoVersion", Sqlite::StrictColumnType::Integer); - table.addColumn("majorVersion", Sqlite::StrictColumnType::Integer); - table.addColumn("minorVersion", Sqlite::StrictColumnType::Integer); - - table.addUniqueIndex({sourceIdColumn, moduleIdColumn}); - - table.initialize(database); - } - - void createDocumentImportsTable(Database &database, - const Sqlite::StrictColumn &foreignModuleIdColumn) - { - Sqlite::StrictTable table; - table.setUseIfNotExists(true); - table.setName("documentImports"); - table.addColumn("importId", Sqlite::StrictColumnType::Integer, {Sqlite::PrimaryKey{}}); - auto &sourceIdColumn = table.addColumn("sourceId", Sqlite::StrictColumnType::Integer); - auto &moduleIdColumn = table.addForeignKeyColumn("moduleId", - foreignModuleIdColumn, - Sqlite::ForeignKeyAction::NoAction, - Sqlite::ForeignKeyAction::Cascade, - Sqlite::Enforment::Immediate); - auto &sourceModuleIdColumn = table.addForeignKeyColumn("sourceModuleId", - foreignModuleIdColumn, - Sqlite::ForeignKeyAction::NoAction, - Sqlite::ForeignKeyAction::Cascade, - Sqlite::Enforment::Immediate); - auto &kindColumn = table.addColumn("kind", Sqlite::StrictColumnType::Integer); - auto &majorVersionColumn = table.addColumn("majorVersion", - Sqlite::StrictColumnType::Integer); - auto &minorVersionColumn = table.addColumn("minorVersion", - Sqlite::StrictColumnType::Integer); - auto &parentImportIdColumn = table.addColumn("parentImportId", - Sqlite::StrictColumnType::Integer); - - table.addUniqueIndex({sourceIdColumn, - moduleIdColumn, - kindColumn, - sourceModuleIdColumn, - parentImportIdColumn}, - "majorVersion IS NULL AND minorVersion IS NULL"); - table.addUniqueIndex({sourceIdColumn, - moduleIdColumn, - kindColumn, - sourceModuleIdColumn, - majorVersionColumn, - parentImportIdColumn}, - "majorVersion IS NOT NULL AND minorVersion IS NULL"); - table.addUniqueIndex({sourceIdColumn, - moduleIdColumn, - kindColumn, - sourceModuleIdColumn, - majorVersionColumn, - minorVersionColumn, - parentImportIdColumn}, - "majorVersion IS NOT NULL AND minorVersion IS NOT NULL"); - - table.addIndex({sourceIdColumn, kindColumn}); - - table.initialize(database); - } - - void createFileStatusesTable(Database &database) - { - Sqlite::StrictTable table; - table.setUseIfNotExists(true); - table.setName("fileStatuses"); - table.addColumn("sourceId", - Sqlite::StrictColumnType::Integer, - {Sqlite::PrimaryKey{}, - Sqlite::ForeignKey{"sources", - "sourceId", - Sqlite::ForeignKeyAction::NoAction, - Sqlite::ForeignKeyAction::Cascade}}); - table.addColumn("size", Sqlite::StrictColumnType::Integer); - table.addColumn("lastModified", Sqlite::StrictColumnType::Integer); - - table.initialize(database); - } - - void createProjectDatasTable(Database &database) - { - Sqlite::StrictTable table; - table.setUseIfNotExists(true); - table.setUseWithoutRowId(true); - table.setName("projectDatas"); - auto &projectSourceIdColumn = table.addColumn("projectSourceId", - Sqlite::StrictColumnType::Integer); - auto &sourceIdColumn = table.addColumn("sourceId", Sqlite::StrictColumnType::Integer); - table.addColumn("moduleId", Sqlite::StrictColumnType::Integer); - table.addColumn("fileType", Sqlite::StrictColumnType::Integer); - - table.addPrimaryKeyContraint({projectSourceIdColumn, sourceIdColumn}); - table.addUniqueIndex({sourceIdColumn}); - - table.initialize(database); - } - - void createPropertyEditorPathsTable(Database &database) - { - Sqlite::StrictTable table; - table.setUseIfNotExists(true); - table.setUseWithoutRowId(true); - table.setName("propertyEditorPaths"); - table.addColumn("typeId", Sqlite::StrictColumnType::Integer, {Sqlite::PrimaryKey{}}); - table.addColumn("pathSourceId", Sqlite::StrictColumnType::Integer); - auto &directoryIdColumn = table.addColumn("directoryId", - Sqlite::StrictColumnType::Integer); - - table.addIndex({directoryIdColumn}); - - table.initialize(database); - } - - void createTypeAnnotionsTable(Database &database) - { - Sqlite::StrictTable table; - table.setUseIfNotExists(true); - table.setUseWithoutRowId(true); - table.setName("typeAnnotations"); - auto &typeIdColumn = table.addColumn("typeId", - Sqlite::StrictColumnType::Integer, - {Sqlite::PrimaryKey{}}); - auto &sourceIdColumn = table.addColumn("sourceId", Sqlite::StrictColumnType::Integer); - auto &directorySourceIdColumn = table.addColumn("directorySourceId", - Sqlite::StrictColumnType::Integer); - - table.addColumn("iconPath", Sqlite::StrictColumnType::Text); - table.addColumn("itemLibrary", Sqlite::StrictColumnType::Text); - table.addColumn("hints", Sqlite::StrictColumnType::Text); - - table.addUniqueIndex({sourceIdColumn, typeIdColumn}); - table.addIndex({directorySourceIdColumn}); - - table.initialize(database); - } - }; + class Initializer; public: Database &database; Sqlite::ExclusiveNonThrowingDestructorTransaction exclusiveTransaction; - Initializer initializer; + std::unique_ptr initializer; mutable ModuleCache moduleCache{ModuleStorageAdapter{*this}}; - Storage::Info::CommonTypeCache commonTypeCache_{*this}; + Storage::Info::CommonTypeCache commonTypeCache_{*this}; QVarLengthArray observers; ReadWriteStatement<1, 2> insertTypeStatement{ "INSERT OR IGNORE INTO types(sourceId, name) VALUES(?1, ?2) RETURNING typeId", database}; @@ -4564,8 +1152,10 @@ public: "INSERT INTO functionDeclarations(typeId, name, returnTypeName, signature) VALUES(?1, ?2, " "?3, ?4)", database}; - WriteStatement<2> updateFunctionDeclarationStatement{ - "UPDATE functionDeclarations SET returnTypeName=?2 WHERE functionDeclarationId=?1", database}; + WriteStatement<3> updateFunctionDeclarationStatement{"UPDATE functionDeclarations " + "SET returnTypeName=?2, signature=?3 " + "WHERE functionDeclarationId=?1", + database}; WriteStatement<1> deleteFunctionDeclarationStatement{ "DELETE FROM functionDeclarations WHERE functionDeclarationId=?", database}; mutable ReadStatement<3, 1> selectSignalDeclarationsForTypeIdStatement{ @@ -5078,6 +1668,5 @@ public: database}; }; -extern template class ProjectStorage; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragefwd.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragefwd.h index b33c609509e..cbb7d4265ae 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragefwd.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragefwd.h @@ -11,7 +11,6 @@ namespace QmlDesigner { class ProjectStorageInterface; class SourcePathCacheInterface; -template class ProjectStorage; template diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h index 20d988f7aab..971e635517a 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h @@ -84,7 +84,7 @@ public: virtual std::optional fetchProjectData(SourceId sourceId) const = 0; virtual SourceId propertyEditorPathId(TypeId typeId) const = 0; - virtual const Storage::Info::CommonTypeCache &commonTypeCache() const = 0; + virtual const Storage::Info::CommonTypeCache &commonTypeCache() const = 0; template TypeId commonTypeId() const diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h index e21531deea0..640969fe990 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h @@ -32,7 +32,6 @@ class ProjectStorageInterface; template class SourcePathCache; class FileStatusCache; -template class ProjectStorage; class QmlDocumentParserInterface; class QmlTypesParserInterface; @@ -40,7 +39,7 @@ class QmlTypesParserInterface; class ProjectStorageUpdater final : public ProjectStoragePathWatcherNotifierInterface { public: - using PathCache = SourcePathCache, NonLockingMutex>; + using PathCache = SourcePathCache; ProjectStorageUpdater(FileSystemInterface &fileSystem, ProjectStorageType &projectStorage, diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h index b8ab4ec4b17..1b494a2f693 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h @@ -15,7 +15,7 @@ class SourcePathCache; class QmlDocumentParser final : public QmlDocumentParserInterface { public: - using ProjectStorage = QmlDesigner::ProjectStorage; + using ProjectStorage = QmlDesigner::ProjectStorage; using PathCache = QmlDesigner::SourcePathCache; #ifdef QDS_BUILD_QMLPARSER diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h index 7c41925f306..4a6427501b3 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h @@ -4,6 +4,7 @@ #pragma once #include "nonlockingmutex.h" +#include "projectstoragefwd.h" #include "qmltypesparserinterface.h" namespace Sqlite { @@ -12,17 +13,13 @@ class Database; namespace QmlDesigner { -template -class ProjectStorage; - template class SourcePathCache; class QmlTypesParser final : public QmlTypesParserInterface { public: - using ProjectStorage = QmlDesigner::ProjectStorage; - + using ProjectStorage = QmlDesigner::ProjectStorage; #ifdef QDS_BUILD_QMLPARSER QmlTypesParser(ProjectStorage &storage) : m_storage{storage} diff --git a/src/plugins/qmldesigner/designercore/projectstorage/sourcepathcachetypes.h b/src/plugins/qmldesigner/designercore/projectstorage/sourcepathcachetypes.h index 5feaf30d006..1ef8ba7f216 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/sourcepathcachetypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/sourcepathcachetypes.h @@ -125,4 +125,6 @@ public: SourceContextId sourceContextId; }; +using SourceNameAndSourceContextIds = std::vector; + } // namespace QmlDesigner::Cache diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp index 2aadc45f24a..9602bf050fa 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp @@ -181,7 +181,7 @@ public: pathCache.sourceId(SourcePath{project->projectDirectory().toString() + "/."}).internalId())} {} Sqlite::Database database; - ProjectStorage storage{database, database.isInitialized()}; + ProjectStorage storage{database, database.isInitialized()}; PathCacheType pathCache{storage}; FileSystem fileSystem{pathCache}; FileStatusCache fileStatusCache{fileSystem}; @@ -282,7 +282,7 @@ AsynchronousImageCache &QmlDesignerProjectManager::asynchronousImageCache() } namespace { -[[maybe_unused]] ProjectStorage *dummyProjectStorage() +[[maybe_unused]] ProjectStorage *dummyProjectStorage() { return nullptr; } diff --git a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp index d16e007cc03..4034ae58f9f 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp @@ -202,7 +202,7 @@ class HasNameMatcher public: using is_gtest_matcher = void; - HasNameMatcher(const QmlDesigner::ProjectStorage &storage, + HasNameMatcher(const QmlDesigner::ProjectStorage &storage, Utils::SmallStringView name) : storage{storage} , name{name} @@ -231,7 +231,7 @@ public: void DescribeNegationTo(std::ostream *os) const { *os << "is not '" << name << "'"; } private: - const QmlDesigner::ProjectStorage &storage; + const QmlDesigner::ProjectStorage &storage; Utils::SmallStringView name; }; @@ -271,7 +271,7 @@ protected: { static_database = std::make_unique(":memory:", Sqlite::JournalMode::Memory); - static_projectStorage = std::make_unique>( + static_projectStorage = std::make_unique( *static_database, static_database->isInitialized()); } @@ -1136,9 +1136,9 @@ protected: protected: inline static std::unique_ptr static_database; Sqlite::Database &database = *static_database; - inline static std::unique_ptr> static_projectStorage; - QmlDesigner::ProjectStorage &storage = *static_projectStorage; - QmlDesigner::SourcePathCache> sourcePathCache{ + inline static std::unique_ptr static_projectStorage; + QmlDesigner::ProjectStorage &storage = *static_projectStorage; + QmlDesigner::SourcePathCache sourcePathCache{ storage}; QmlDesigner::SourcePathView path1{"/path1/to"}; QmlDesigner::SourcePathView path2{"/path2/to"}; @@ -5102,7 +5102,7 @@ TEST_F(ProjectStorage, populate_module_cache) { auto id = storage.moduleId("Qml"); - QmlDesigner::ProjectStorage newStorage{database, database.isInitialized()}; + QmlDesigner::ProjectStorage newStorage{database, database.isInitialized()}; ASSERT_THAT(newStorage.moduleName(id), Eq("Qml")); } diff --git a/tests/unit/tests/unittests/projectstorage/projectstoragepathwatcher-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstoragepathwatcher-test.cpp index d6ed96d0cfc..26d5af8af8a 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstoragepathwatcher-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstoragepathwatcher-test.cpp @@ -16,7 +16,7 @@ #include namespace { -using SourcePathCache = QmlDesigner::SourcePathCache>; +using SourcePathCache = QmlDesigner::SourcePathCache; using Watcher = QmlDesigner::ProjectStoragePathWatcher, NiceMock, SourcePathCache>; @@ -43,7 +43,7 @@ protected: { static_database = std::make_unique(":memory:", Sqlite::JournalMode::Memory); - static_projectStorage = std::make_unique>( + static_projectStorage = std::make_unique( *static_database, static_database->isInitialized()); } @@ -81,8 +81,8 @@ protected: NiceMock mockFileSystem; inline static std::unique_ptr static_database; Sqlite::Database &database = *static_database; - inline static std::unique_ptr> static_projectStorage; - QmlDesigner::ProjectStorage &storage = *static_projectStorage; + inline static std::unique_ptr static_projectStorage; + QmlDesigner::ProjectStorage &storage = *static_projectStorage; SourcePathCache pathCache{storage}; Watcher watcher{pathCache, mockFileSystem, ¬ifier}; NiceMock &mockQFileSytemWatcher = watcher.fileSystemWatcher(); diff --git a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp index a0ca9012629..96909857b32 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp @@ -144,7 +144,7 @@ public: { static_database = std::make_unique(":memory:", Sqlite::JournalMode::Memory); - static_projectStorage = std::make_unique>( + static_projectStorage = std::make_unique( *static_database, static_database->isInitialized()); } @@ -312,9 +312,9 @@ protected: QmlDesigner::FileStatusCache fileStatusCache{fileSystemMock}; inline static std::unique_ptr static_database; Sqlite::Database &database = *static_database; - inline static std::unique_ptr> static_projectStorage; - QmlDesigner::ProjectStorage &storage = *static_projectStorage; - QmlDesigner::SourcePathCache> sourcePathCache{ + inline static std::unique_ptr static_projectStorage; + QmlDesigner::ProjectStorage &storage = *static_projectStorage; + QmlDesigner::SourcePathCache sourcePathCache{ storage}; NiceMock patchWatcherMock; QmlDesigner::ProjectPartId projectPartId = QmlDesigner::ProjectPartId::create(1); diff --git a/tests/unit/tests/unittests/projectstorage/qmldocumentparser-test.cpp b/tests/unit/tests/unittests/projectstorage/qmldocumentparser-test.cpp index affa645330e..513fbf2ec01 100644 --- a/tests/unit/tests/unittests/projectstorage/qmldocumentparser-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/qmldocumentparser-test.cpp @@ -143,8 +143,8 @@ class QmlDocumentParser : public ::testing::Test public: protected: Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory}; - QmlDesigner::ProjectStorage storage{database, database.isInitialized()}; - QmlDesigner::SourcePathCache> sourcePathCache{ + QmlDesigner::ProjectStorage storage{database, database.isInitialized()}; + QmlDesigner::SourcePathCache sourcePathCache{ storage}; QmlDesigner::QmlDocumentParser parser{storage, sourcePathCache}; Storage::Imports imports; diff --git a/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp b/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp index a42a560d07d..64f3631a68f 100644 --- a/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp @@ -168,8 +168,8 @@ class QmlTypesParser : public ::testing::Test public: protected: Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory}; - QmlDesigner::ProjectStorage storage{database, database.isInitialized()}; - QmlDesigner::SourcePathCache> sourcePathCache{ + QmlDesigner::ProjectStorage storage{database, database.isInitialized()}; + QmlDesigner::SourcePathCache sourcePathCache{ storage}; QmlDesigner::QmlTypesParser parser{storage}; Storage::Imports imports; diff --git a/tests/unit/tests/unittests/projectstorage/typeannotationreader-test.cpp b/tests/unit/tests/unittests/projectstorage/typeannotationreader-test.cpp index b90a4adf1be..a614a5c7cf2 100644 --- a/tests/unit/tests/unittests/projectstorage/typeannotationreader-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/typeannotationreader-test.cpp @@ -20,7 +20,7 @@ protected: { static_database = std::make_unique(":memory:", Sqlite::JournalMode::Memory); - static_projectStorage = std::make_unique>( + static_projectStorage = std::make_unique( *static_database, static_database->isInitialized()); } @@ -35,8 +35,8 @@ protected: protected: inline static std::unique_ptr static_database; Sqlite::Database &database = *static_database; - inline static std::unique_ptr> static_projectStorage; - QmlDesigner::ProjectStorage &storage = *static_projectStorage; + inline static std::unique_ptr static_projectStorage; + QmlDesigner::ProjectStorage &storage = *static_projectStorage; QmlDesigner::Storage::TypeAnnotationReader reader{storage}; QmlDesigner::SourceId sourceId = QmlDesigner::SourceId::create(33); QmlDesigner::SourceId directorySourceId = QmlDesigner::SourceId::create(77); From cbc617d2ad1d881fd9c40445e65bce83f2689b8e Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 22 Apr 2024 17:16:10 +0200 Subject: [PATCH 193/202] QmlDesigner: Move project storage statements to cpp That can improve compile times, so it takes less time to develop new functionality. Change-Id: I94170674e1bf5178020389bcb3623dbbf87eb7a3 Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- .../projectstorage/projectstorage.cpp | 1272 +++++++++++++---- .../projectstorage/projectstorage.h | 752 +--------- 2 files changed, 1024 insertions(+), 1000 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp index a037ddae9a6..a7577d3ab77 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp @@ -7,6 +7,705 @@ namespace QmlDesigner { +struct ProjectStorage::Statements +{ + Statements(Sqlite::Database &database) + : database{database} + {} + + Sqlite::Database &database; + Sqlite::ReadWriteStatement<1, 2> insertTypeStatement{ + "INSERT OR IGNORE INTO types(sourceId, name) VALUES(?1, ?2) RETURNING typeId", database}; + Sqlite::WriteStatement<5> updatePrototypeAndExtensionStatement{ + "UPDATE types SET prototypeId=?2, prototypeNameId=?3, extensionId=?4, extensionNameId=?5 " + "WHERE typeId=?1 AND (prototypeId IS NOT ?2 OR extensionId IS NOT ?3 AND prototypeId " + "IS NOT ?4 OR extensionNameId IS NOT ?5)", + database}; + mutable Sqlite::ReadStatement<1, 1> selectTypeIdByExportedNameStatement{ + "SELECT typeId FROM exportedTypeNames WHERE name=?1", database}; + mutable Sqlite::ReadStatement<1, 2> selectTypeIdByModuleIdAndExportedNameStatement{ + "SELECT typeId FROM exportedTypeNames " + "WHERE moduleId=?1 AND name=?2 " + "ORDER BY majorVersion DESC, minorVersion DESC " + "LIMIT 1", + database}; + mutable Sqlite::ReadStatement<1, 3> selectTypeIdByModuleIdAndExportedNameAndMajorVersionStatement{ + "SELECT typeId FROM exportedTypeNames " + "WHERE moduleId=?1 AND name=?2 AND majorVersion=?3" + "ORDER BY minorVersion DESC " + "LIMIT 1", + database}; + mutable Sqlite::ReadStatement<1, 4> selectTypeIdByModuleIdAndExportedNameAndVersionStatement{ + "SELECT typeId FROM exportedTypeNames " + "WHERE moduleId=?1 AND name=?2 AND majorVersion=?3 AND minorVersion<=?4" + "ORDER BY minorVersion DESC " + "LIMIT 1", + database}; + mutable Sqlite::ReadStatement<3, 1> selectPropertyDeclarationResultByPropertyDeclarationIdStatement{ + "SELECT propertyTypeId, propertyDeclarationId, propertyTraits " + "FROM propertyDeclarations " + "WHERE propertyDeclarationId=?1 " + "LIMIT 1", + database}; + mutable Sqlite::ReadStatement<1, 1> selectSourceContextIdFromSourceContextsBySourceContextPathStatement{ + "SELECT sourceContextId FROM sourceContexts WHERE sourceContextPath = ?", database}; + mutable Sqlite::ReadStatement<1, 1> selectSourceContextPathFromSourceContextsBySourceContextIdStatement{ + "SELECT sourceContextPath FROM sourceContexts WHERE sourceContextId = ?", database}; + mutable Sqlite::ReadStatement<2> selectAllSourceContextsStatement{ + "SELECT sourceContextPath, sourceContextId FROM sourceContexts", database}; + Sqlite::WriteStatement<1> insertIntoSourceContextsStatement{ + "INSERT INTO sourceContexts(sourceContextPath) VALUES (?)", database}; + mutable Sqlite::ReadStatement<1, 2> selectSourceIdFromSourcesBySourceContextIdAndSourceNameStatement{ + "SELECT sourceId FROM sources WHERE sourceContextId = ? AND sourceName = ?", database}; + mutable Sqlite::ReadStatement<2, 1> selectSourceNameAndSourceContextIdFromSourcesBySourceIdStatement{ + "SELECT sourceName, sourceContextId FROM sources WHERE sourceId = ?", database}; + mutable Sqlite::ReadStatement<1, 1> selectSourceContextIdFromSourcesBySourceIdStatement{ + "SELECT sourceContextId FROM sources WHERE sourceId = ?", database}; + Sqlite::WriteStatement<2> insertIntoSourcesStatement{ + "INSERT INTO sources(sourceContextId, sourceName) VALUES (?,?)", database}; + mutable Sqlite::ReadStatement<3> selectAllSourcesStatement{ + "SELECT sourceName, sourceContextId, sourceId FROM sources", database}; + mutable Sqlite::ReadStatement<8, 1> selectTypeByTypeIdStatement{ + "SELECT sourceId, t.name, t.typeId, prototypeId, extensionId, traits, annotationTraits, " + "pd.name " + "FROM types AS t LEFT JOIN propertyDeclarations AS pd ON " + "defaultPropertyId=propertyDeclarationId " + "WHERE t.typeId=?", + database}; + mutable Sqlite::ReadStatement<4, 1> selectExportedTypesByTypeIdStatement{ + "SELECT moduleId, name, ifnull(majorVersion, -1), ifnull(minorVersion, -1) FROM " + "exportedTypeNames WHERE typeId=?", + database}; + mutable Sqlite::ReadStatement<4, 2> selectExportedTypesByTypeIdAndSourceIdStatement{ + "SELECT etn.moduleId, name, ifnull(etn.majorVersion, -1), ifnull(etn.minorVersion, -1) " + "FROM exportedTypeNames AS etn JOIN documentImports USING(moduleId) WHERE typeId=?1 AND " + "sourceId=?2", + database}; + mutable Sqlite::ReadStatement<8> selectTypesStatement{ + "SELECT sourceId, t.name, t.typeId, prototypeId, extensionId, traits, annotationTraits, " + "pd.name " + "FROM types AS t LEFT JOIN propertyDeclarations AS pd ON " + "defaultPropertyId=propertyDeclarationId", + database}; + Sqlite::WriteStatement<2> updateTypeTraitStatement{ + "UPDATE types SET traits = ?2 WHERE typeId=?1", database}; + Sqlite::WriteStatement<2> updateTypeAnnotationTraitStatement{ + "UPDATE types SET annotationTraits = ?2 WHERE typeId=?1", database}; + Sqlite::ReadStatement<1, 2> selectNotUpdatedTypesInSourcesStatement{ + "SELECT DISTINCT typeId FROM types WHERE (sourceId IN carray(?1) AND typeId NOT IN " + "carray(?2))", + database}; + Sqlite::WriteStatement<1> deleteTypeNamesByTypeIdStatement{ + "DELETE FROM exportedTypeNames WHERE typeId=?", database}; + Sqlite::WriteStatement<1> deleteEnumerationDeclarationByTypeIdStatement{ + "DELETE FROM enumerationDeclarations WHERE typeId=?", database}; + Sqlite::WriteStatement<1> deletePropertyDeclarationByTypeIdStatement{ + "DELETE FROM propertyDeclarations WHERE typeId=?", database}; + Sqlite::WriteStatement<1> deleteFunctionDeclarationByTypeIdStatement{ + "DELETE FROM functionDeclarations WHERE typeId=?", database}; + Sqlite::WriteStatement<1> deleteSignalDeclarationByTypeIdStatement{ + "DELETE FROM signalDeclarations WHERE typeId=?", database}; + Sqlite::WriteStatement<1> deleteTypeStatement{"DELETE FROM types WHERE typeId=?", database}; + mutable Sqlite::ReadStatement<4, 1> selectPropertyDeclarationsByTypeIdStatement{ + "SELECT name, propertyTypeId, propertyTraits, (SELECT name FROM " + "propertyDeclarations WHERE propertyDeclarationId=pd.aliasPropertyDeclarationId) FROM " + "propertyDeclarations AS pd WHERE typeId=?", + database}; + Sqlite::ReadStatement<6, 1> selectPropertyDeclarationsForTypeIdStatement{ + "SELECT name, propertyTraits, propertyTypeId, propertyImportedTypeNameId, " + "propertyDeclarationId, aliasPropertyDeclarationId FROM propertyDeclarations " + "WHERE typeId=? ORDER BY name", + database}; + Sqlite::ReadWriteStatement<1, 5> insertPropertyDeclarationStatement{ + "INSERT INTO propertyDeclarations(typeId, name, propertyTypeId, propertyTraits, " + "propertyImportedTypeNameId, aliasPropertyDeclarationId) VALUES(?1, ?2, ?3, ?4, ?5, NULL) " + "RETURNING propertyDeclarationId", + database}; + Sqlite::WriteStatement<4> updatePropertyDeclarationStatement{ + "UPDATE propertyDeclarations SET propertyTypeId=?2, propertyTraits=?3, " + "propertyImportedTypeNameId=?4, aliasPropertyDeclarationId=NULL WHERE " + "propertyDeclarationId=?1", + database}; + Sqlite::WriteStatement<3> updatePropertyAliasDeclarationRecursivelyWithTypeAndTraitsStatement{ + "WITH RECURSIVE " + " properties(aliasPropertyDeclarationId) AS ( " + " SELECT propertyDeclarationId FROM propertyDeclarations WHERE " + " aliasPropertyDeclarationId=?1 " + " UNION ALL " + " SELECT pd.propertyDeclarationId FROM " + " propertyDeclarations AS pd JOIN properties USING(aliasPropertyDeclarationId)) " + "UPDATE propertyDeclarations AS pd " + "SET propertyTypeId=?2, propertyTraits=?3 " + "FROM properties AS p " + "WHERE pd.propertyDeclarationId=p.aliasPropertyDeclarationId", + database}; + Sqlite::WriteStatement<1> updatePropertyAliasDeclarationRecursivelyStatement{ + "WITH RECURSIVE " + " propertyValues(propertyTypeId, propertyTraits) AS (" + " SELECT propertyTypeId, propertyTraits FROM propertyDeclarations " + " WHERE propertyDeclarationId=?1), " + " properties(aliasPropertyDeclarationId) AS ( " + " SELECT propertyDeclarationId FROM propertyDeclarations WHERE " + " aliasPropertyDeclarationId=?1 " + " UNION ALL " + " SELECT pd.propertyDeclarationId FROM " + " propertyDeclarations AS pd JOIN properties USING(aliasPropertyDeclarationId)) " + "UPDATE propertyDeclarations AS pd " + "SET propertyTypeId=pv.propertyTypeId, propertyTraits=pv.propertyTraits " + "FROM properties AS p, propertyValues AS pv " + "WHERE pd.propertyDeclarationId=p.aliasPropertyDeclarationId", + database}; + Sqlite::WriteStatement<1> deletePropertyDeclarationStatement{ + "DELETE FROM propertyDeclarations WHERE propertyDeclarationId=?", database}; + Sqlite::ReadStatement<3, 1> selectPropertyDeclarationsWithAliasForTypeIdStatement{ + "SELECT name, propertyDeclarationId, aliasPropertyDeclarationId FROM propertyDeclarations " + "WHERE typeId=? AND aliasPropertyDeclarationId IS NOT NULL ORDER BY name", + database}; + Sqlite::WriteStatement<5> updatePropertyDeclarationWithAliasAndTypeStatement{ + "UPDATE propertyDeclarations SET propertyTypeId=?2, propertyTraits=?3, " + "propertyImportedTypeNameId=?4, aliasPropertyDeclarationId=?5 WHERE " + "propertyDeclarationId=?1", + database}; + Sqlite::ReadWriteStatement<1, 2> insertAliasPropertyDeclarationStatement{ + "INSERT INTO propertyDeclarations(typeId, name) VALUES(?1, ?2) RETURNING " + "propertyDeclarationId", + database}; + mutable Sqlite::ReadStatement<4, 1> selectFunctionDeclarationsForTypeIdStatement{ + "SELECT name, returnTypeName, signature, functionDeclarationId FROM " + "functionDeclarations WHERE typeId=? ORDER BY name, signature", + database}; + mutable Sqlite::ReadStatement<3, 1> selectFunctionDeclarationsForTypeIdWithoutSignatureStatement{ + "SELECT name, returnTypeName, functionDeclarationId FROM " + "functionDeclarations WHERE typeId=? ORDER BY name", + database}; + mutable Sqlite::ReadStatement<3, 1> selectFunctionParameterDeclarationsStatement{ + "SELECT json_extract(json_each.value, '$.n'), json_extract(json_each.value, '$.tn'), " + "json_extract(json_each.value, '$.tr') FROM functionDeclarations, " + "json_each(functionDeclarations.signature) WHERE functionDeclarationId=?", + database}; + Sqlite::WriteStatement<4> insertFunctionDeclarationStatement{ + "INSERT INTO functionDeclarations(typeId, name, returnTypeName, signature) VALUES(?1, ?2, " + "?3, ?4)", + database}; + Sqlite::WriteStatement<3> updateFunctionDeclarationStatement{ + "UPDATE functionDeclarations " + "SET returnTypeName=?2, signature=?3 " + "WHERE functionDeclarationId=?1", + database}; + Sqlite::WriteStatement<1> deleteFunctionDeclarationStatement{ + "DELETE FROM functionDeclarations WHERE functionDeclarationId=?", database}; + mutable Sqlite::ReadStatement<3, 1> selectSignalDeclarationsForTypeIdStatement{ + "SELECT name, signature, signalDeclarationId FROM signalDeclarations WHERE typeId=? ORDER " + "BY name, signature", + database}; + mutable Sqlite::ReadStatement<2, 1> selectSignalDeclarationsForTypeIdWithoutSignatureStatement{ + "SELECT name, signalDeclarationId FROM signalDeclarations WHERE typeId=? ORDER BY name", + database}; + mutable Sqlite::ReadStatement<3, 1> selectSignalParameterDeclarationsStatement{ + "SELECT json_extract(json_each.value, '$.n'), json_extract(json_each.value, '$.tn'), " + "json_extract(json_each.value, '$.tr') FROM signalDeclarations, " + "json_each(signalDeclarations.signature) WHERE signalDeclarationId=?", + database}; + Sqlite::WriteStatement<3> insertSignalDeclarationStatement{ + "INSERT INTO signalDeclarations(typeId, name, signature) VALUES(?1, ?2, ?3)", database}; + Sqlite::WriteStatement<2> updateSignalDeclarationStatement{ + "UPDATE signalDeclarations SET signature=?2 WHERE signalDeclarationId=?1", database}; + Sqlite::WriteStatement<1> deleteSignalDeclarationStatement{ + "DELETE FROM signalDeclarations WHERE signalDeclarationId=?", database}; + mutable Sqlite::ReadStatement<3, 1> selectEnumerationDeclarationsForTypeIdStatement{ + "SELECT name, enumeratorDeclarations, enumerationDeclarationId FROM " + "enumerationDeclarations WHERE typeId=? ORDER BY name", + database}; + mutable Sqlite::ReadStatement<2, 1> selectEnumerationDeclarationsForTypeIdWithoutEnumeratorDeclarationsStatement{ + "SELECT name, enumerationDeclarationId FROM enumerationDeclarations WHERE typeId=? ORDER " + "BY name", + database}; + mutable Sqlite::ReadStatement<3, 1> selectEnumeratorDeclarationStatement{ + "SELECT json_each.key, json_each.value, json_each.type!='null' FROM " + "enumerationDeclarations, json_each(enumerationDeclarations.enumeratorDeclarations) WHERE " + "enumerationDeclarationId=?", + database}; + Sqlite::WriteStatement<3> insertEnumerationDeclarationStatement{ + "INSERT INTO enumerationDeclarations(typeId, name, enumeratorDeclarations) VALUES(?1, ?2, " + "?3)", + database}; + Sqlite::WriteStatement<2> updateEnumerationDeclarationStatement{ + "UPDATE enumerationDeclarations SET enumeratorDeclarations=?2 WHERE " + "enumerationDeclarationId=?1", + database}; + Sqlite::WriteStatement<1> deleteEnumerationDeclarationStatement{ + "DELETE FROM enumerationDeclarations WHERE enumerationDeclarationId=?", database}; + mutable Sqlite::ReadStatement<1, 1> selectModuleIdByNameStatement{ + "SELECT moduleId FROM modules WHERE name=? LIMIT 1", database}; + mutable Sqlite::ReadWriteStatement<1, 1> insertModuleNameStatement{ + "INSERT INTO modules(name) VALUES(?1) RETURNING moduleId", database}; + mutable Sqlite::ReadStatement<1, 1> selectModuleNameStatement{ + "SELECT name FROM modules WHERE moduleId =?1", database}; + mutable Sqlite::ReadStatement<2> selectAllModulesStatement{"SELECT name, moduleId FROM modules", + database}; + mutable Sqlite::ReadStatement<1, 2> selectTypeIdBySourceIdAndNameStatement{ + "SELECT typeId FROM types WHERE sourceId=?1 and name=?2", database}; + mutable Sqlite::ReadStatement<1, 3> selectTypeIdByModuleIdsAndExportedNameStatement{ + "SELECT typeId FROM exportedTypeNames WHERE moduleId IN carray(?1, ?2, 'int32') AND " + "name=?3", + database}; + mutable Sqlite::ReadStatement<4> selectAllDocumentImportForSourceIdStatement{ + "SELECT moduleId, majorVersion, minorVersion, sourceId " + "FROM documentImports ", + database}; + mutable Sqlite::ReadStatement<5, 2> selectDocumentImportForSourceIdStatement{ + "SELECT importId, sourceId, moduleId, majorVersion, minorVersion " + "FROM documentImports WHERE sourceId IN carray(?1) AND kind=?2 ORDER BY sourceId, " + "moduleId, majorVersion, minorVersion", + database}; + Sqlite::ReadWriteStatement<1, 5> insertDocumentImportWithoutVersionStatement{ + "INSERT INTO documentImports(sourceId, moduleId, sourceModuleId, kind, " + "parentImportId) VALUES (?1, ?2, ?3, ?4, ?5) RETURNING importId", + database}; + Sqlite::ReadWriteStatement<1, 6> insertDocumentImportWithMajorVersionStatement{ + "INSERT INTO documentImports(sourceId, moduleId, sourceModuleId, kind, majorVersion, " + "parentImportId) VALUES (?1, ?2, ?3, ?4, ?5, ?6) RETURNING importId", + database}; + Sqlite::ReadWriteStatement<1, 7> insertDocumentImportWithVersionStatement{ + "INSERT INTO documentImports(sourceId, moduleId, sourceModuleId, kind, majorVersion, " + "minorVersion, parentImportId) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7) RETURNING " + "importId", + database}; + Sqlite::WriteStatement<1> deleteDocumentImportStatement{ + "DELETE FROM documentImports WHERE importId=?1", database}; + Sqlite::WriteStatement<2> deleteDocumentImportsWithParentImportIdStatement{ + "DELETE FROM documentImports WHERE sourceId=?1 AND parentImportId=?2", database}; + Sqlite::WriteStatement<1> deleteDocumentImportsWithSourceIdsStatement{ + "DELETE FROM documentImports WHERE sourceId IN carray(?1)", database}; + mutable Sqlite::ReadStatement<1, 2> selectPropertyDeclarationIdByTypeIdAndNameStatement{ + "SELECT propertyDeclarationId " + "FROM propertyDeclarations " + "WHERE typeId=?1 AND name=?2 " + "LIMIT 1", + database}; + Sqlite::WriteStatement<2> updateAliasIdPropertyDeclarationStatement{ + "UPDATE propertyDeclarations SET aliasPropertyDeclarationId=?2 WHERE " + "aliasPropertyDeclarationId=?1", + database}; + Sqlite::WriteStatement<2> updateAliasPropertyDeclarationByAliasPropertyDeclarationIdStatement{ + "UPDATE propertyDeclarations SET propertyTypeId=new.propertyTypeId, " + "propertyTraits=new.propertyTraits, aliasPropertyDeclarationId=?1 FROM (SELECT " + "propertyTypeId, propertyTraits FROM propertyDeclarations WHERE propertyDeclarationId=?1) " + "AS new WHERE aliasPropertyDeclarationId=?2", + database}; + Sqlite::WriteStatement<1> updateAliasPropertyDeclarationToNullStatement{ + "UPDATE propertyDeclarations SET aliasPropertyDeclarationId=NULL, propertyTypeId=NULL, " + "propertyTraits=NULL WHERE propertyDeclarationId=? AND (aliasPropertyDeclarationId IS NOT " + "NULL OR propertyTypeId IS NOT NULL OR propertyTraits IS NOT NULL)", + database}; + Sqlite::ReadStatement<5, 1> selectAliasPropertiesDeclarationForPropertiesWithTypeIdStatement{ + "SELECT alias.typeId, alias.propertyDeclarationId, alias.propertyImportedTypeNameId, " + " alias.aliasPropertyDeclarationId, alias.aliasPropertyDeclarationTailId " + "FROM propertyDeclarations AS alias JOIN propertyDeclarations AS target " + " ON alias.aliasPropertyDeclarationId=target.propertyDeclarationId OR " + " alias.aliasPropertyDeclarationTailId=target.propertyDeclarationId " + "WHERE alias.propertyTypeId=?1 " + "UNION ALL " + "SELECT alias.typeId, alias.propertyDeclarationId, alias.propertyImportedTypeNameId, " + " alias.aliasPropertyDeclarationId, alias.aliasPropertyDeclarationTailId " + "FROM propertyDeclarations AS alias JOIN propertyDeclarations AS target " + " ON alias.aliasPropertyDeclarationId=target.propertyDeclarationId OR " + " alias.aliasPropertyDeclarationTailId=target.propertyDeclarationId " + "WHERE target.typeId=?1 " + "UNION ALL " + "SELECT alias.typeId, alias.propertyDeclarationId, alias.propertyImportedTypeNameId, " + " alias.aliasPropertyDeclarationId, alias.aliasPropertyDeclarationTailId " + "FROM propertyDeclarations AS alias JOIN propertyDeclarations AS target " + " ON alias.aliasPropertyDeclarationId=target.propertyDeclarationId OR " + " alias.aliasPropertyDeclarationTailId=target.propertyDeclarationId " + "WHERE alias.propertyImportedTypeNameId IN " + " (SELECT importedTypeNameId FROM exportedTypeNames JOIN importedTypeNames USING(name) " + " WHERE typeId=?1)", + database}; + Sqlite::ReadStatement<3, 1> selectAliasPropertiesDeclarationForPropertiesWithAliasIdStatement{ + "WITH RECURSIVE " + " properties(propertyDeclarationId, propertyImportedTypeNameId, typeId, " + " aliasPropertyDeclarationId) AS (" + " SELECT propertyDeclarationId, propertyImportedTypeNameId, typeId, " + " aliasPropertyDeclarationId FROM propertyDeclarations WHERE " + " aliasPropertyDeclarationId=?1" + " UNION ALL " + " SELECT pd.propertyDeclarationId, pd.propertyImportedTypeNameId, pd.typeId, " + " pd.aliasPropertyDeclarationId FROM propertyDeclarations AS pd JOIN properties AS " + " p ON pd.aliasPropertyDeclarationId=p.propertyDeclarationId)" + "SELECT propertyDeclarationId, propertyImportedTypeNameId, aliasPropertyDeclarationId " + " FROM properties", + database}; + Sqlite::ReadWriteStatement<3, 1> updatesPropertyDeclarationPropertyTypeToNullStatement{ + "UPDATE propertyDeclarations SET propertyTypeId=NULL WHERE propertyTypeId=?1 AND " + "aliasPropertyDeclarationId IS NULL RETURNING typeId, propertyDeclarationId, " + "propertyImportedTypeNameId", + database}; + mutable Sqlite::ReadStatement<1, 1> selectPropertyNameStatement{ + "SELECT name FROM propertyDeclarations WHERE propertyDeclarationId=?", database}; + Sqlite::WriteStatement<2> updatePropertyDeclarationTypeStatement{ + "UPDATE propertyDeclarations SET propertyTypeId=?2 WHERE propertyDeclarationId=?1", database}; + Sqlite::ReadWriteStatement<2, 1> updatePrototypeIdToNullStatement{ + "UPDATE types SET prototypeId=NULL WHERE prototypeId=?1 RETURNING " + "typeId, prototypeNameId", + database}; + Sqlite::ReadWriteStatement<2, 1> updateExtensionIdToNullStatement{ + "UPDATE types SET extensionId=NULL WHERE extensionId=?1 RETURNING " + "typeId, extensionNameId", + database}; + Sqlite::WriteStatement<2> updateTypePrototypeStatement{ + "UPDATE types SET prototypeId=?2 WHERE typeId=?1", database}; + Sqlite::WriteStatement<2> updateTypeExtensionStatement{ + "UPDATE types SET extensionId=?2 WHERE typeId=?1", database}; + mutable Sqlite::ReadStatement<1, 1> selectPrototypeAndExtensionIdsStatement{ + "WITH RECURSIVE " + " prototypes(typeId) AS ( " + " SELECT prototypeId FROM types WHERE typeId=?1 " + " UNION ALL " + " SELECT extensionId FROM types WHERE typeId=?1 " + " UNION ALL " + " SELECT prototypeId FROM types JOIN prototypes USING(typeId) " + " UNION ALL " + " SELECT extensionId FROM types JOIN prototypes USING(typeId)) " + "SELECT typeId FROM prototypes WHERE typeId IS NOT NULL", + database}; + Sqlite::WriteStatement<3> updatePropertyDeclarationAliasIdAndTypeNameIdStatement{ + "UPDATE propertyDeclarations SET aliasPropertyDeclarationId=?2, " + "propertyImportedTypeNameId=?3 WHERE propertyDeclarationId=?1 AND " + "(aliasPropertyDeclarationId IS NOT ?2 OR propertyImportedTypeNameId IS NOT ?3)", + database}; + Sqlite::WriteStatement<1> updatetPropertiesDeclarationValuesOfAliasStatement{ + "WITH RECURSIVE " + " properties(propertyDeclarationId, propertyTypeId, propertyTraits) AS ( " + " SELECT aliasPropertyDeclarationId, propertyTypeId, propertyTraits FROM " + " propertyDeclarations WHERE propertyDeclarationId=?1 " + " UNION ALL " + " SELECT pd.aliasPropertyDeclarationId, pd.propertyTypeId, pd.propertyTraits FROM " + " propertyDeclarations AS pd JOIN properties USING(propertyDeclarationId)) " + "UPDATE propertyDeclarations AS pd SET propertyTypeId=p.propertyTypeId, " + " propertyTraits=p.propertyTraits " + "FROM properties AS p " + "WHERE pd.propertyDeclarationId=?1 AND p.propertyDeclarationId IS NULL AND " + " (pd.propertyTypeId IS NOT p.propertyTypeId OR pd.propertyTraits IS NOT " + " p.propertyTraits)", + database}; + Sqlite::WriteStatement<1> updatePropertyDeclarationAliasIdToNullStatement{ + "UPDATE propertyDeclarations SET aliasPropertyDeclarationId=NULL WHERE " + "propertyDeclarationId=?1", + database}; + mutable Sqlite::ReadStatement<1, 1> selectPropertyDeclarationIdsForAliasChainStatement{ + "WITH RECURSIVE " + " properties(propertyDeclarationId) AS ( " + " SELECT aliasPropertyDeclarationId FROM propertyDeclarations WHERE " + " propertyDeclarationId=?1 " + " UNION ALL " + " SELECT aliasPropertyDeclarationId FROM propertyDeclarations JOIN properties " + " USING(propertyDeclarationId)) " + "SELECT propertyDeclarationId FROM properties", + database}; + mutable Sqlite::ReadStatement<3> selectAllFileStatusesStatement{ + "SELECT sourceId, size, lastModified FROM fileStatuses ORDER BY sourceId", database}; + mutable Sqlite::ReadStatement<3, 1> selectFileStatusesForSourceIdsStatement{ + "SELECT sourceId, size, lastModified FROM fileStatuses WHERE sourceId IN carray(?1) ORDER " + "BY sourceId", + database}; + mutable Sqlite::ReadStatement<3, 1> selectFileStatusesForSourceIdStatement{ + "SELECT sourceId, size, lastModified FROM fileStatuses WHERE sourceId=?1 ORDER BY sourceId", + database}; + Sqlite::WriteStatement<3> insertFileStatusStatement{ + "INSERT INTO fileStatuses(sourceId, size, lastModified) VALUES(?1, ?2, ?3)", database}; + Sqlite::WriteStatement<1> deleteFileStatusStatement{ + "DELETE FROM fileStatuses WHERE sourceId=?1", database}; + Sqlite::WriteStatement<3> updateFileStatusStatement{ + "UPDATE fileStatuses SET size=?2, lastModified=?3 WHERE sourceId=?1", database}; + Sqlite::ReadStatement<1, 1> selectTypeIdBySourceIdStatement{ + "SELECT typeId FROM types WHERE sourceId=?", database}; + mutable Sqlite::ReadStatement<1, 3> selectImportedTypeNameIdStatement{ + "SELECT importedTypeNameId FROM importedTypeNames WHERE kind=?1 AND importOrSourceId=?2 " + "AND name=?3 LIMIT 1", + database}; + mutable Sqlite::ReadWriteStatement<1, 3> insertImportedTypeNameIdStatement{ + "INSERT INTO importedTypeNames(kind, importOrSourceId, name) VALUES (?1, ?2, ?3) " + "RETURNING importedTypeNameId", + database}; + mutable Sqlite::ReadStatement<1, 2> selectImportIdBySourceIdAndModuleIdStatement{ + "SELECT importId FROM documentImports WHERE sourceId=?1 AND moduleId=?2 AND majorVersion " + "IS NULL AND minorVersion IS NULL LIMIT 1", + database}; + mutable Sqlite::ReadStatement<1, 3> selectImportIdBySourceIdAndModuleIdAndMajorVersionStatement{ + "SELECT importId FROM documentImports WHERE sourceId=?1 AND moduleId=?2 AND " + "majorVersion=?3 AND minorVersion IS NULL LIMIT 1", + database}; + mutable Sqlite::ReadStatement<1, 4> selectImportIdBySourceIdAndModuleIdAndVersionStatement{ + "SELECT importId FROM documentImports WHERE sourceId=?1 AND moduleId=?2 AND " + "majorVersion=?3 AND minorVersion=?4 LIMIT 1", + database}; + mutable Sqlite::ReadStatement<1, 1> selectKindFromImportedTypeNamesStatement{ + "SELECT kind FROM importedTypeNames WHERE importedTypeNameId=?1", database}; + mutable Sqlite::ReadStatement<1, 1> selectNameFromImportedTypeNamesStatement{ + "SELECT name FROM importedTypeNames WHERE importedTypeNameId=?1", database}; + mutable Sqlite::ReadStatement<1, 1> selectTypeIdForQualifiedImportedTypeNameNamesStatement{ + "SELECT typeId FROM importedTypeNames AS itn JOIN documentImports AS di ON " + "importOrSourceId=di.importId JOIN documentImports AS di2 ON di.sourceId=di2.sourceId AND " + "di.moduleId=di2.sourceModuleId " + "JOIN exportedTypeNames AS etn ON di2.moduleId=etn.moduleId WHERE " + "itn.kind=2 AND importedTypeNameId=?1 AND itn.name=etn.name AND " + "(di.majorVersion IS NULL OR (di.majorVersion=etn.majorVersion AND (di.minorVersion IS " + "NULL OR di.minorVersion>=etn.minorVersion))) ORDER BY etn.majorVersion DESC NULLS FIRST, " + "etn.minorVersion DESC NULLS FIRST LIMIT 1", + database}; + mutable Sqlite::ReadStatement<1, 1> selectTypeIdForImportedTypeNameNamesStatement{ + "WITH " + " importTypeNames(moduleId, name, kind, majorVersion, minorVersion) AS ( " + " SELECT moduleId, name, di.kind, majorVersion, minorVersion " + " FROM importedTypeNames AS itn JOIN documentImports AS di ON " + " importOrSourceId=sourceId " + " WHERE " + " importedTypeNameId=?1 AND itn.kind=1) " + "SELECT typeId FROM importTypeNames AS itn " + " JOIN exportedTypeNames AS etn USING(moduleId, name) " + "WHERE (itn.majorVersion IS NULL OR (itn.majorVersion=etn.majorVersion " + " AND (itn.minorVersion IS NULL OR itn.minorVersion>=etn.minorVersion))) " + "ORDER BY itn.kind, etn.majorVersion DESC NULLS FIRST, etn.minorVersion DESC NULLS FIRST " + "LIMIT 1", + database}; + Sqlite::WriteStatement<0> deleteAllSourcesStatement{"DELETE FROM sources", database}; + Sqlite::WriteStatement<0> deleteAllSourceContextsStatement{"DELETE FROM sourceContexts", database}; + mutable Sqlite::ReadStatement<6, 1> selectExportedTypesForSourceIdsStatement{ + "SELECT moduleId, name, ifnull(majorVersion, -1), ifnull(minorVersion, -1), typeId, " + "exportedTypeNameId FROM exportedTypeNames WHERE typeId in carray(?1) ORDER BY moduleId, " + "name, majorVersion, minorVersion", + database}; + Sqlite::WriteStatement<5> insertExportedTypeNamesWithVersionStatement{ + "INSERT INTO exportedTypeNames(moduleId, name, majorVersion, minorVersion, typeId) " + "VALUES(?1, ?2, ?3, ?4, ?5)", + database}; + Sqlite::WriteStatement<4> insertExportedTypeNamesWithMajorVersionStatement{ + "INSERT INTO exportedTypeNames(moduleId, name, majorVersion, typeId) " + "VALUES(?1, ?2, ?3, ?4)", + database}; + Sqlite::WriteStatement<3> insertExportedTypeNamesWithoutVersionStatement{ + "INSERT INTO exportedTypeNames(moduleId, name, typeId) VALUES(?1, ?2, ?3)", database}; + Sqlite::WriteStatement<1> deleteExportedTypeNameStatement{ + "DELETE FROM exportedTypeNames WHERE exportedTypeNameId=?", database}; + Sqlite::WriteStatement<2> updateExportedTypeNameTypeIdStatement{ + "UPDATE exportedTypeNames SET typeId=?2 WHERE exportedTypeNameId=?1", database}; + mutable Sqlite::ReadStatement<4, 1> selectProjectDatasForSourceIdsStatement{ + "SELECT projectSourceId, sourceId, moduleId, fileType FROM projectDatas WHERE " + "projectSourceId IN carray(?1) ORDER BY projectSourceId, sourceId", + database}; + Sqlite::WriteStatement<4> insertProjectDataStatement{ + "INSERT INTO projectDatas(projectSourceId, sourceId, " + "moduleId, fileType) VALUES(?1, ?2, ?3, ?4)", + database}; + Sqlite::WriteStatement<2> deleteProjectDataStatement{ + "DELETE FROM projectDatas WHERE projectSourceId=?1 AND sourceId=?2", database}; + Sqlite::WriteStatement<4> updateProjectDataStatement{ + "UPDATE projectDatas SET moduleId=?3, fileType=?4 WHERE projectSourceId=?1 AND sourceId=?2", + database}; + mutable Sqlite::ReadStatement<4, 1> selectProjectDatasForSourceIdStatement{ + "SELECT projectSourceId, sourceId, moduleId, fileType FROM projectDatas WHERE " + "projectSourceId=?1", + database}; + mutable Sqlite::ReadStatement<4, 1> selectProjectDataForSourceIdStatement{ + "SELECT projectSourceId, sourceId, moduleId, fileType FROM projectDatas WHERE " + "sourceId=?1 LIMIT 1", + database}; + mutable Sqlite::ReadStatement<1, 1> selectTypeIdsForSourceIdsStatement{ + "SELECT typeId FROM types WHERE sourceId IN carray(?1)", database}; + mutable Sqlite::ReadStatement<6, 1> selectModuleExportedImportsForSourceIdStatement{ + "SELECT moduleExportedImportId, moduleId, exportedModuleId, ifnull(majorVersion, -1), " + "ifnull(minorVersion, -1), isAutoVersion FROM moduleExportedImports WHERE moduleId IN " + "carray(?1) ORDER BY moduleId, exportedModuleId", + database}; + Sqlite::WriteStatement<3> insertModuleExportedImportWithoutVersionStatement{ + "INSERT INTO moduleExportedImports(moduleId, exportedModuleId, isAutoVersion) " + "VALUES (?1, ?2, ?3)", + database}; + Sqlite::WriteStatement<4> insertModuleExportedImportWithMajorVersionStatement{ + "INSERT INTO moduleExportedImports(moduleId, exportedModuleId, isAutoVersion, " + "majorVersion) VALUES (?1, ?2, ?3, ?4)", + database}; + Sqlite::WriteStatement<5> insertModuleExportedImportWithVersionStatement{ + "INSERT INTO moduleExportedImports(moduleId, exportedModuleId, isAutoVersion, " + "majorVersion, minorVersion) VALUES (?1, ?2, ?3, ?4, ?5)", + database}; + Sqlite::WriteStatement<1> deleteModuleExportedImportStatement{ + "DELETE FROM moduleExportedImports WHERE moduleExportedImportId=?1", database}; + mutable Sqlite::ReadStatement<3, 3> selectModuleExportedImportsForModuleIdStatement{ + "WITH RECURSIVE " + " imports(moduleId, majorVersion, minorVersion, moduleExportedImportId) AS ( " + " SELECT exportedModuleId, " + " iif(isAutoVersion=1, ?2, majorVersion), " + " iif(isAutoVersion=1, ?3, minorVersion), " + " moduleExportedImportId " + " FROM moduleExportedImports WHERE moduleId=?1 " + " UNION ALL " + " SELECT exportedModuleId, " + " iif(mei.isAutoVersion=1, i.majorVersion, mei.majorVersion), " + " iif(mei.isAutoVersion=1, i.minorVersion, mei.minorVersion), " + " mei.moduleExportedImportId " + " FROM moduleExportedImports AS mei JOIN imports AS i USING(moduleId)) " + "SELECT DISTINCT moduleId, ifnull(majorVersion, -1), ifnull(minorVersion, -1) " + "FROM imports", + database}; + mutable Sqlite::ReadStatement<1, 1> selectLocalPropertyDeclarationIdsForTypeStatement{ + "SELECT propertyDeclarationId " + "FROM propertyDeclarations " + "WHERE typeId=? " + "ORDER BY propertyDeclarationId", + database}; + mutable Sqlite::ReadStatement<1, 2> selectLocalPropertyDeclarationIdForTypeAndPropertyNameStatement{ + "SELECT propertyDeclarationId " + "FROM propertyDeclarations " + "WHERE typeId=?1 AND name=?2 LIMIT 1", + database}; + mutable Sqlite::ReadStatement<4, 1> selectPropertyDeclarationForPropertyDeclarationIdStatement{ + "SELECT typeId, name, propertyTraits, propertyTypeId " + "FROM propertyDeclarations " + "WHERE propertyDeclarationId=?1 LIMIT 1", + database}; + mutable Sqlite::ReadStatement<1, 1> selectSignalDeclarationNamesForTypeStatement{ + "WITH RECURSIVE " + " all_prototype_and_extension(typeId, prototypeId) AS (" + " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL" + " UNION ALL " + " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL)," + " typeChain(typeId) AS (" + " VALUES(?1)" + " UNION ALL " + " SELECT prototypeId FROM all_prototype_and_extension JOIN typeChain " + " USING(typeId)) " + "SELECT name FROM typeChain JOIN signalDeclarations " + " USING(typeId) ORDER BY name", + database}; + mutable Sqlite::ReadStatement<1, 1> selectFuncionDeclarationNamesForTypeStatement{ + "WITH RECURSIVE " + " all_prototype_and_extension(typeId, prototypeId) AS (" + " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL" + " UNION ALL " + " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL)," + " typeChain(typeId) AS (" + " VALUES(?1)" + " UNION ALL " + " SELECT prototypeId FROM all_prototype_and_extension JOIN typeChain " + " USING(typeId))" + "SELECT name FROM typeChain JOIN functionDeclarations " + " USING(typeId) ORDER BY name", + database}; + mutable Sqlite::ReadStatement<2> selectTypesWithDefaultPropertyStatement{ + "SELECT typeId, defaultPropertyId FROM types ORDER BY typeId", database}; + Sqlite::WriteStatement<2> updateDefaultPropertyIdStatement{ + "UPDATE types SET defaultPropertyId=?2 WHERE typeId=?1", database}; + Sqlite::WriteStatement<1> updateDefaultPropertyIdToNullStatement{ + "UPDATE types SET defaultPropertyId=NULL WHERE defaultPropertyId=?1", database}; + mutable Sqlite::ReadStatement<3, 1> selectInfoTypeByTypeIdStatement{ + "SELECT sourceId, traits, annotationTraits FROM types WHERE typeId=?", database}; + mutable Sqlite::ReadStatement<1, 1> selectDefaultPropertyDeclarationIdStatement{ + "SELECT defaultPropertyId FROM types WHERE typeId=?", database}; + mutable Sqlite::ReadStatement<1, 1> selectPrototypeIdsForTypeIdInOrderStatement{ + "WITH RECURSIVE " + " all_prototype_and_extension(typeId, prototypeId) AS (" + " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL" + " UNION ALL " + " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL)," + " prototypes(typeId, level) AS (" + " SELECT prototypeId, 0 FROM all_prototype_and_extension WHERE typeId=?" + " UNION ALL " + " SELECT prototypeId, p.level+1 FROM all_prototype_and_extension JOIN " + " prototypes AS p USING(typeId)) " + "SELECT typeId FROM prototypes ORDER BY level", + database}; + Sqlite::WriteStatement<2> upsertPropertyEditorPathIdStatement{ + "INSERT INTO propertyEditorPaths(typeId, pathSourceId) VALUES(?1, ?2) ON CONFLICT DO " + "UPDATE SET pathSourceId=excluded.pathSourceId WHERE pathSourceId IS NOT " + "excluded.pathSourceId", + database}; + mutable Sqlite::ReadStatement<1, 1> selectPropertyEditorPathIdStatement{ + "SELECT pathSourceId FROM propertyEditorPaths WHERE typeId=?", database}; + mutable Sqlite::ReadStatement<3, 1> selectPropertyEditorPathsForForSourceIdsStatement{ + "SELECT typeId, pathSourceId, directoryId " + "FROM propertyEditorPaths " + "WHERE directoryId IN carray(?1) " + "ORDER BY typeId", + database}; + Sqlite::WriteStatement<3> insertPropertyEditorPathStatement{ + "INSERT INTO propertyEditorPaths(typeId, pathSourceId, directoryId) VALUES (?1, ?2, ?3)", + database}; + Sqlite::WriteStatement<3> updatePropertyEditorPathsStatement{ + "UPDATE propertyEditorPaths " + "SET pathSourceId=?2, directoryId=?3 " + "WHERE typeId=?1", + database}; + Sqlite::WriteStatement<1> deletePropertyEditorPathStatement{ + "DELETE FROM propertyEditorPaths WHERE typeId=?1", database}; + mutable Sqlite::ReadStatement<4, 1> selectTypeAnnotationsForSourceIdsStatement{ + "SELECT typeId, iconPath, itemLibrary, hints FROM typeAnnotations WHERE " + "sourceId IN carray(?1) ORDER BY typeId", + database}; + Sqlite::WriteStatement<6> insertTypeAnnotationStatement{ + "INSERT INTO " + " typeAnnotations(typeId, sourceId, directorySourceId, iconPath, itemLibrary, hints) " + "VALUES(?1, ?2, ?3, ?4, ?5, ?6)", + database}; + Sqlite::WriteStatement<4> updateTypeAnnotationStatement{ + "UPDATE typeAnnotations SET iconPath=?2, itemLibrary=?3, hints=?4 WHERE typeId=?1", database}; + Sqlite::WriteStatement<1> deleteTypeAnnotationStatement{ + "DELETE FROM typeAnnotations WHERE typeId=?1", database}; + mutable Sqlite::ReadStatement<1, 1> selectTypeIconPathStatement{ + "SELECT iconPath FROM typeAnnotations WHERE typeId=?1", database}; + mutable Sqlite::ReadStatement<2, 1> selectTypeHintsStatement{ + "SELECT hints.key, hints.value " + "FROM typeAnnotations, json_each(typeAnnotations.hints) AS hints " + "WHERE typeId=?1 AND hints IS NOT NULL", + database}; + mutable Sqlite::ReadStatement<1, 1> selectTypeAnnotationSourceIdsStatement{ + "SELECT sourceId FROM typeAnnotations WHERE directorySourceId=?1 ORDER BY sourceId", database}; + mutable Sqlite::ReadStatement<1, 0> selectTypeAnnotationDirectorySourceIdsStatement{ + "SELECT DISTINCT directorySourceId FROM typeAnnotations ORDER BY directorySourceId", database}; + mutable Sqlite::ReadStatement<9> selectItemLibraryEntriesStatement{ + "SELECT typeId, i.value->>'$.name', i.value->>'$.iconPath', i.value->>'$.category', " + " i.value->>'$.import', i.value->>'$.toolTip', i.value->>'$.properties', " + " i.value->>'$.extraFilePaths', i.value->>'$.templatePath' " + "FROM typeAnnotations AS ta , json_each(ta.itemLibrary) AS i " + "WHERE ta.itemLibrary IS NOT NULL", + database}; + mutable Sqlite::ReadStatement<9, 1> selectItemLibraryEntriesByTypeIdStatement{ + "SELECT typeId, i.value->>'$.name', i.value->>'$.iconPath', i.value->>'$.category', " + " i.value->>'$.import', i.value->>'$.toolTip', i.value->>'$.properties', " + " i.value->>'$.extraFilePaths', i.value->>'$.templatePath' " + "FROM typeAnnotations AS ta, json_each(ta.itemLibrary) AS i " + "WHERE typeId=?1 AND ta.itemLibrary IS NOT NULL", + database}; + mutable Sqlite::ReadStatement<9, 1> selectItemLibraryEntriesBySourceIdStatement{ + "SELECT typeId, i.value->>'$.name', i.value->>'$.iconPath', " + "i.value->>'$.category', " + " i.value->>'$.import', i.value->>'$.toolTip', i.value->>'$.properties', " + " i.value->>'$.extraFilePaths', i.value->>'$.templatePath' " + "FROM typeAnnotations, json_each(typeAnnotations.itemLibrary) AS i " + "WHERE typeId IN (SELECT DISTINCT typeId " + " FROM documentImports AS di JOIN exportedTypeNames " + " USING(moduleId) " + " WHERE di.sourceId=?)", + database}; + mutable Sqlite::ReadStatement<3, 1> selectItemLibraryPropertiesStatement{ + "SELECT p.value->>0, p.value->>1, p.value->>2 FROM json_each(?1) AS p", database}; + mutable Sqlite::ReadStatement<1, 1> selectItemLibraryExtraFilePathsStatement{ + "SELECT p.value FROM json_each(?1) AS p", database}; + mutable Sqlite::ReadStatement<1, 1> selectTypeIdsByModuleIdStatement{ + "SELECT DISTINCT typeId FROM exportedTypeNames WHERE moduleId=?", database}; + mutable Sqlite::ReadStatement<1, 1> selectHeirTypeIdsStatement{ + "WITH RECURSIVE " + " typeSelection(typeId) AS (" + " SELECT typeId FROM types WHERE prototypeId=?1 OR extensionId=?1" + " UNION ALL " + " SELECT t.typeId " + " FROM types AS t JOIN typeSelection AS ts " + " WHERE prototypeId=ts.typeId OR extensionId=ts.typeId)" + "SELECT typeId FROM typeSelection", + database}; +}; + class ProjectStorage::Initializer { public: @@ -404,6 +1103,7 @@ ProjectStorage::ProjectStorage(Database &database, bool isInitialized) , exclusiveTransaction{database} , initializer{std::make_unique(database, isInitialized)} , moduleCache{ModuleStorageAdapter{*this}} + , s{std::make_unique(database)} { NanotraceHR::Tracer tracer{"initialize"_t, projectStorageCategory()}; @@ -552,15 +1252,15 @@ TypeId ProjectStorage::typeId(ModuleId moduleId, TypeId typeId; if (version.minor) { - typeId = selectTypeIdByModuleIdAndExportedNameAndVersionStatement.valueWithTransaction( + typeId = s->selectTypeIdByModuleIdAndExportedNameAndVersionStatement.valueWithTransaction( moduleId, exportedTypeName, version.major.value, version.minor.value); } else if (version.major) { - typeId = selectTypeIdByModuleIdAndExportedNameAndMajorVersionStatement + typeId = s->selectTypeIdByModuleIdAndExportedNameAndMajorVersionStatement .valueWithTransaction(moduleId, exportedTypeName, version.major.value); } else { - typeId = selectTypeIdByModuleIdAndExportedNameStatement + typeId = s->selectTypeIdByModuleIdAndExportedNameStatement .valueWithTransaction(moduleId, exportedTypeName); } @@ -590,8 +1290,8 @@ QVarLengthArray ProjectStorage::typeIds(ModuleId moduleId) const projectStorageCategory(), keyValue("module id", moduleId)}; - auto typeIds = selectTypeIdsByModuleIdStatement.valuesWithTransaction>( - moduleId); + auto typeIds = s->selectTypeIdsByModuleIdStatement + .valuesWithTransaction>(moduleId); tracer.end(keyValue("type ids", typeIds)); @@ -605,7 +1305,7 @@ Storage::Info::ExportedTypeNames ProjectStorage::exportedTypeNames(TypeId typeId projectStorageCategory(), keyValue("type id", typeId)}; - auto exportedTypenames = selectExportedTypesByTypeIdStatement + auto exportedTypenames = s->selectExportedTypesByTypeIdStatement .valuesWithTransaction(typeId); tracer.end(keyValue("exported type names", exportedTypenames)); @@ -621,7 +1321,7 @@ Storage::Info::ExportedTypeNames ProjectStorage::exportedTypeNames(TypeId typeId keyValue("type id", typeId), keyValue("source id", sourceId)}; - auto exportedTypenames = selectExportedTypesByTypeIdAndSourceIdStatement + auto exportedTypenames = s->selectExportedTypesByTypeIdAndSourceIdStatement .valuesWithTransaction(typeId, sourceId); @@ -711,7 +1411,7 @@ QVarLengthArray ProjectStorage::localPropertyDeclara projectStorageCategory(), keyValue("type id", typeId)}; - auto propertyDeclarationIds = selectLocalPropertyDeclarationIdsForTypeStatement + auto propertyDeclarationIds = s->selectLocalPropertyDeclarationIdsForTypeStatement .valuesWithTransaction>( typeId); @@ -747,7 +1447,7 @@ PropertyDeclarationId ProjectStorage::localPropertyDeclarationId(TypeId typeId, keyValue("type id", typeId), keyValue("property name", propertyName)}; - auto propertyDeclarationId = selectLocalPropertyDeclarationIdForTypeAndPropertyNameStatement + auto propertyDeclarationId = s->selectLocalPropertyDeclarationIdForTypeAndPropertyNameStatement .valueWithTransaction(typeId, propertyName); @@ -780,7 +1480,7 @@ std::optional ProjectStorage::propertyDeclar projectStorageCategory(), keyValue("property declaration id", propertyDeclarationId)}; - auto propertyDeclaration = selectPropertyDeclarationForPropertyDeclarationIdStatement + auto propertyDeclaration = s->selectPropertyDeclarationForPropertyDeclarationIdStatement .optionalValueWithTransaction( propertyDeclarationId); @@ -794,7 +1494,7 @@ std::optional ProjectStorage::type(TypeId typeId) const using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"get type"_t, projectStorageCategory(), keyValue("type id", typeId)}; - auto type = selectInfoTypeByTypeIdStatement.optionalValueWithTransaction( + auto type = s->selectInfoTypeByTypeIdStatement.optionalValueWithTransaction( typeId); tracer.end(keyValue("type", type)); @@ -809,7 +1509,7 @@ Utils::PathString ProjectStorage::typeIconPath(TypeId typeId) const projectStorageCategory(), keyValue("type id", typeId)}; - auto typeIconPath = selectTypeIconPathStatement.valueWithTransaction(typeId); + auto typeIconPath = s->selectTypeIconPathStatement.valueWithTransaction(typeId); tracer.end(keyValue("type icon path", typeIconPath)); @@ -823,7 +1523,7 @@ Storage::Info::TypeHints ProjectStorage::typeHints(TypeId typeId) const projectStorageCategory(), keyValue("type id", typeId)}; - auto typeHints = selectTypeHintsStatement.valuesWithTransaction( + auto typeHints = s->selectTypeHintsStatement.valuesWithTransaction( typeId); tracer.end(keyValue("type hints", typeHints)); @@ -838,7 +1538,7 @@ SmallSourceIds<4> ProjectStorage::typeAnnotationSourceIds(SourceId directoryId) projectStorageCategory(), keyValue("source id", directoryId)}; - auto sourceIds = selectTypeAnnotationSourceIdsStatement.valuesWithTransaction>( + auto sourceIds = s->selectTypeAnnotationSourceIdsStatement.valuesWithTransaction>( directoryId); tracer.end(keyValue("source ids", sourceIds)); @@ -851,7 +1551,7 @@ SmallSourceIds<64> ProjectStorage::typeAnnotationDirectorySourceIds() const using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"get type annotaion source ids"_t, projectStorageCategory()}; - auto sourceIds = selectTypeAnnotationDirectorySourceIdsStatement + auto sourceIds = s->selectTypeAnnotationDirectorySourceIdsStatement .valuesWithTransaction>(); tracer.end(keyValue("source ids", sourceIds)); @@ -880,12 +1580,12 @@ Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(TypeId type Utils::SmallStringView templatePath) { auto &last = entries.emplace_back(typeId_, name, iconPath, category, import, toolTip, templatePath); if (properties.size()) - selectItemLibraryPropertiesStatement.readTo(last.properties, properties); + s->selectItemLibraryPropertiesStatement.readTo(last.properties, properties); if (extraFilePaths.size()) - selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths); + s->selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths); }; - selectItemLibraryEntriesByTypeIdStatement.readCallbackWithTransaction(callback, typeId); + s->selectItemLibraryEntriesByTypeIdStatement.readCallbackWithTransaction(callback, typeId); tracer.end(keyValue("item library entries", entries)); @@ -913,12 +1613,12 @@ Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(ImportId im Utils::SmallStringView templatePath) { auto &last = entries.emplace_back(typeId_, name, iconPath, category, import, toolTip, templatePath); if (properties.size()) - selectItemLibraryPropertiesStatement.readTo(last.properties, properties); + s->selectItemLibraryPropertiesStatement.readTo(last.properties, properties); if (extraFilePaths.size()) - selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths); + s->selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths); }; - selectItemLibraryEntriesByTypeIdStatement.readCallbackWithTransaction(callback, importId); + s->selectItemLibraryEntriesByTypeIdStatement.readCallbackWithTransaction(callback, importId); tracer.end(keyValue("item library entries", entries)); @@ -946,12 +1646,12 @@ Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(SourceId so Utils::SmallStringView templatePath) { auto &last = entries.emplace_back(typeId, name, iconPath, category, import, toolTip, templatePath); if (properties.size()) - selectItemLibraryPropertiesStatement.readTo(last.properties, properties); + s->selectItemLibraryPropertiesStatement.readTo(last.properties, properties); if (extraFilePaths.size()) - selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths); + s->selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths); }; - selectItemLibraryEntriesBySourceIdStatement.readCallbackWithTransaction(callback, sourceId); + s->selectItemLibraryEntriesBySourceIdStatement.readCallbackWithTransaction(callback, sourceId); tracer.end(keyValue("item library entries", entries)); @@ -977,12 +1677,12 @@ Storage::Info::ItemLibraryEntries ProjectStorage::allItemLibraryEntries() const Utils::SmallStringView templatePath) { auto &last = entries.emplace_back(typeId, name, iconPath, category, import, toolTip, templatePath); if (properties.size()) - selectItemLibraryPropertiesStatement.readTo(last.properties, properties); + s->selectItemLibraryPropertiesStatement.readTo(last.properties, properties); if (extraFilePaths.size()) - selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths); + s->selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths); }; - selectItemLibraryEntriesStatement.readCallbackWithTransaction(callback); + s->selectItemLibraryEntriesStatement.readCallbackWithTransaction(callback); tracer.end(keyValue("item library entries", entries)); @@ -996,7 +1696,7 @@ std::vector ProjectStorage::signalDeclarationNames(TypeId ty projectStorageCategory(), keyValue("type id", typeId)}; - auto signalDeclarationNames = selectSignalDeclarationNamesForTypeStatement + auto signalDeclarationNames = s->selectSignalDeclarationNamesForTypeStatement .valuesWithTransaction(typeId); tracer.end(keyValue("signal names", signalDeclarationNames)); @@ -1011,7 +1711,7 @@ std::vector ProjectStorage::functionDeclarationNames(TypeId projectStorageCategory(), keyValue("type id", typeId)}; - auto functionDeclarationNames = selectFuncionDeclarationNamesForTypeStatement + auto functionDeclarationNames = s->selectFuncionDeclarationNamesForTypeStatement .valuesWithTransaction(typeId); tracer.end(keyValue("function names", functionDeclarationNames)); @@ -1027,7 +1727,7 @@ std::optional ProjectStorage::propertyName( projectStorageCategory(), keyValue("property declaration id", propertyDeclarationId)}; - auto propertyName = selectPropertyNameStatement.optionalValueWithTransaction( + auto propertyName = s->selectPropertyNameStatement.optionalValueWithTransaction( propertyDeclarationId); tracer.end(keyValue("property name", propertyName)); @@ -1040,8 +1740,8 @@ SmallTypeIds<16> ProjectStorage::prototypeIds(TypeId type) const using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"get prototypes"_t, projectStorageCategory(), keyValue("type id", type)}; - auto prototypeIds = selectPrototypeAndExtensionIdsStatement.valuesWithTransaction>( - type); + auto prototypeIds = s->selectPrototypeAndExtensionIdsStatement + .valuesWithTransaction>(type); tracer.end(keyValue("type ids", prototypeIds)); @@ -1056,7 +1756,7 @@ SmallTypeIds<16> ProjectStorage::prototypeAndSelfIds(TypeId typeId) const SmallTypeIds<16> prototypeAndSelfIds; prototypeAndSelfIds.push_back(typeId); - selectPrototypeAndExtensionIdsStatement.readToWithTransaction(prototypeAndSelfIds, typeId); + s->selectPrototypeAndExtensionIdsStatement.readToWithTransaction(prototypeAndSelfIds, typeId); tracer.end(keyValue("type ids", prototypeAndSelfIds)); @@ -1068,7 +1768,7 @@ SmallTypeIds<64> ProjectStorage::heirIds(TypeId typeId) const using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"get heirs"_t, projectStorageCategory()}; - auto heirIds = selectHeirTypeIdsStatement.valuesWithTransaction>(typeId); + auto heirIds = s->selectHeirTypeIdsStatement.valuesWithTransaction>(typeId); tracer.end(keyValue("type ids", heirIds)); @@ -1124,7 +1824,7 @@ TypeId ProjectStorage::fetchTypeIdByExportedName(Utils::SmallStringView name) co projectStorageCategory(), keyValue("exported type name", name)}; - auto typeId = selectTypeIdByExportedNameStatement.valueWithTransaction(name); + auto typeId = s->selectTypeIdByExportedNameStatement.valueWithTransaction(name); tracer.end(keyValue("type id", typeId)); @@ -1139,7 +1839,7 @@ TypeId ProjectStorage::fetchTypeIdByModuleIdsAndExportedName(ModuleIds moduleIds projectStorageCategory(), keyValue("module ids", NanotraceHR::array(moduleIds)), keyValue("exported type name", name)}; - auto typeId = selectTypeIdByModuleIdsAndExportedNameStatement.valueWithTransaction( + auto typeId = s->selectTypeIdByModuleIdsAndExportedNameStatement.valueWithTransaction( static_cast(moduleIds.data()), static_cast(moduleIds.size()), name); tracer.end(keyValue("type id", typeId)); @@ -1155,7 +1855,8 @@ TypeId ProjectStorage::fetchTypeIdByName(SourceId sourceId, Utils::SmallStringVi keyValue("source id", sourceId), keyValue("internal type name", name)}; - auto typeId = selectTypeIdBySourceIdAndNameStatement.valueWithTransaction(sourceId, name); + auto typeId = s->selectTypeIdBySourceIdAndNameStatement.valueWithTransaction(sourceId, + name); tracer.end(keyValue("type id", typeId)); @@ -1170,7 +1871,7 @@ Storage::Synchronization::Type ProjectStorage::fetchTypeByTypeId(TypeId typeId) keyValue("type id", typeId)}; auto type = Sqlite::withDeferredTransaction(database, [&] { - auto type = selectTypeByTypeIdStatement.value(typeId); + auto type = s->selectTypeByTypeIdStatement.value(typeId); type.exportedTypes = fetchExportedTypes(typeId); type.propertyDeclarations = fetchPropertyDeclarations(type.typeId); @@ -1192,7 +1893,7 @@ Storage::Synchronization::Types ProjectStorage::fetchTypes() NanotraceHR::Tracer tracer{"fetch types"_t, projectStorageCategory()}; auto types = Sqlite::withDeferredTransaction(database, [&] { - auto types = selectTypesStatement.values(); + auto types = s->selectTypesStatement.values(); for (Storage::Synchronization::Type &type : types) { type.exportedTypes = fetchExportedTypes(type.typeId); @@ -1249,7 +1950,7 @@ Utils::PathString ProjectStorage::fetchSourceContextPath(SourceContextId sourceC keyValue("source context id", sourceContextId)}; auto path = Sqlite::withDeferredTransaction(database, [&] { - auto optionalSourceContextPath = selectSourceContextPathFromSourceContextsBySourceContextIdStatement + auto optionalSourceContextPath = s->selectSourceContextPathFromSourceContextsBySourceContextIdStatement .optionalValue(sourceContextId); if (!optionalSourceContextPath) @@ -1267,7 +1968,7 @@ Cache::SourceContexts ProjectStorage::fetchAllSourceContexts() const { NanotraceHR::Tracer tracer{"fetch all source contexts"_t, projectStorageCategory()}; - return selectAllSourceContextsStatement.valuesWithTransaction(); + return s->selectAllSourceContextsStatement.valuesWithTransaction(); } SourceId ProjectStorage::fetchSourceId(SourceContextId sourceContextId, @@ -1295,7 +1996,7 @@ Cache::SourceNameAndSourceContextId ProjectStorage::fetchSourceNameAndSourceCont projectStorageCategory(), keyValue("source id", sourceId)}; - auto value = selectSourceNameAndSourceContextIdFromSourcesBySourceIdStatement + auto value = s->selectSourceNameAndSourceContextIdFromSourcesBySourceIdStatement .valueWithTransaction(sourceId); if (!value.sourceContextId) @@ -1310,8 +2011,8 @@ Cache::SourceNameAndSourceContextId ProjectStorage::fetchSourceNameAndSourceCont void ProjectStorage::clearSources() { Sqlite::withImmediateTransaction(database, [&] { - deleteAllSourceContextsStatement.execute(); - deleteAllSourcesStatement.execute(); + s->deleteAllSourceContextsStatement.execute(); + s->deleteAllSourcesStatement.execute(); }); } @@ -1322,7 +2023,7 @@ SourceContextId ProjectStorage::fetchSourceContextId(SourceId sourceId) const projectStorageCategory(), keyValue("source id", sourceId)}; - auto sourceContextId = selectSourceContextIdFromSourcesBySourceIdStatement + auto sourceContextId = s->selectSourceContextIdFromSourcesBySourceIdStatement .valueWithTransaction(sourceId); if (!sourceContextId) @@ -1337,7 +2038,7 @@ Cache::Sources ProjectStorage::fetchAllSources() const { NanotraceHR::Tracer tracer{"fetch all sources"_t, projectStorageCategory()}; - return selectAllSourcesStatement.valuesWithTransaction(); + return s->selectAllSourcesStatement.valuesWithTransaction(); } SourceId ProjectStorage::fetchSourceIdUnguarded(SourceContextId sourceContextId, @@ -1359,6 +2060,13 @@ SourceId ProjectStorage::fetchSourceIdUnguarded(SourceContextId sourceContextId, return sourceId; } +FileStatuses ProjectStorage::fetchAllFileStatuses() const +{ + NanotraceHR::Tracer tracer{"fetch all file statuses"_t, projectStorageCategory()}; + + return s->selectAllFileStatusesStatement.valuesWithTransaction(); +} + FileStatus ProjectStorage::fetchFileStatus(SourceId sourceId) const { using NanotraceHR::keyValue; @@ -1366,7 +2074,8 @@ FileStatus ProjectStorage::fetchFileStatus(SourceId sourceId) const projectStorageCategory(), keyValue("source id", sourceId)}; - auto fileStatus = selectFileStatusesForSourceIdStatement.valueWithTransaction(sourceId); + auto fileStatus = s->selectFileStatusesForSourceIdStatement.valueWithTransaction( + sourceId); tracer.end(keyValue("file status", fileStatus)); @@ -1380,7 +2089,7 @@ std::optional ProjectStorage::fetchProjec projectStorageCategory(), keyValue("source id", sourceId)}; - auto projectData = selectProjectDataForSourceIdStatement + auto projectData = s->selectProjectDataForSourceIdStatement .optionalValueWithTransaction( sourceId); @@ -1396,7 +2105,7 @@ Storage::Synchronization::ProjectDatas ProjectStorage::fetchProjectDatas(SourceI projectStorageCategory(), keyValue("source id", projectSourceId)}; - auto projectDatas = selectProjectDatasForSourceIdStatement + auto projectDatas = s->selectProjectDatasForSourceIdStatement .valuesWithTransaction( projectSourceId); @@ -1413,7 +2122,7 @@ Storage::Synchronization::ProjectDatas ProjectStorage::fetchProjectDatas( projectStorageCategory(), keyValue("source ids", projectSourceIds)}; - auto projectDatas = selectProjectDatasForSourceIdsStatement + auto projectDatas = s->selectProjectDatasForSourceIdsStatement .valuesWithTransaction( toIntegers(projectSourceIds)); @@ -1426,7 +2135,7 @@ void ProjectStorage::setPropertyEditorPathId(TypeId typeId, SourceId pathId) { Sqlite::ImmediateSessionTransaction transaction{database}; - upsertPropertyEditorPathIdStatement.write(typeId, pathId); + s->upsertPropertyEditorPathIdStatement.write(typeId, pathId); transaction.commit(); } @@ -1438,7 +2147,7 @@ SourceId ProjectStorage::propertyEditorPathId(TypeId typeId) const projectStorageCategory(), keyValue("type id", typeId)}; - auto sourceId = selectPropertyEditorPathIdStatement.valueWithTransaction(typeId); + auto sourceId = s->selectPropertyEditorPathIdStatement.valueWithTransaction(typeId); tracer.end(keyValue("source id", sourceId)); @@ -1449,7 +2158,7 @@ Storage::Imports ProjectStorage::fetchDocumentImports() const { NanotraceHR::Tracer tracer{"fetch document imports"_t, projectStorageCategory()}; - return selectAllDocumentImportForSourceIdStatement.valuesWithTransaction(); + return s->selectAllDocumentImportForSourceIdStatement.valuesWithTransaction(); } void ProjectStorage::resetForTestsOnly() @@ -1499,7 +2208,7 @@ ProjectStorage::Modules ProjectStorage::fetchAllModules() const { NanotraceHR::Tracer tracer{"fetch all modules"_t, projectStorageCategory()}; - return selectAllModulesStatement.valuesWithTransaction(); + return s->selectAllModulesStatement.valuesWithTransaction(); } void ProjectStorage::callRefreshMetaInfoCallback(const TypeIds &deletedTypeIds) @@ -1538,7 +2247,7 @@ TypeIds ProjectStorage::fetchTypeIds(const SourceIds &sourceIds) projectStorageCategory(), keyValue("source ids", sourceIds)}; - return selectTypeIdsForSourceIdsStatement.values(toIntegers(sourceIds)); + return s->selectTypeIdsForSourceIdsStatement.values(toIntegers(sourceIds)); } void ProjectStorage::unique(SourceIds &sourceIds) @@ -1556,7 +2265,7 @@ void ProjectStorage::synchronizeTypeTraits(TypeId typeId, Storage::TypeTraits tr keyValue("type id", typeId), keyValue("type traits", traits)}; - updateTypeAnnotationTraitStatement.write(typeId, traits.annotation); + s->updateTypeAnnotationTraitStatement.write(typeId, traits.annotation); } void ProjectStorage::updateTypeIdInTypeAnnotations(Storage::Synchronization::TypeAnnotations &typeAnnotations) @@ -1595,7 +2304,7 @@ void ProjectStorage::synchronizeTypeAnnotations(Storage::Synchronization::TypeAn return first.typeId < second.typeId; }); - auto range = selectTypeAnnotationsForSourceIdsStatement.range( + auto range = s->selectTypeAnnotationsForSourceIdsStatement.range( toIntegers(updatedTypeAnnotationSourceIds)); auto insert = [&](const TypeAnnotation &annotation) { @@ -1609,12 +2318,12 @@ void ProjectStorage::synchronizeTypeAnnotations(Storage::Synchronization::TypeAn projectStorageCategory(), keyValue("type annotation", annotation)}; - insertTypeAnnotationStatement.write(annotation.typeId, - annotation.sourceId, - annotation.directorySourceId, - annotation.iconPath, - createEmptyAsNull(annotation.itemLibraryJson), - createEmptyAsNull(annotation.hintsJson)); + s->insertTypeAnnotationStatement.write(annotation.typeId, + annotation.sourceId, + annotation.directorySourceId, + annotation.iconPath, + createEmptyAsNull(annotation.itemLibraryJson), + createEmptyAsNull(annotation.hintsJson)); }; auto update = [&](const TypeAnnotationView &annotationFromDatabase, @@ -1631,10 +2340,10 @@ void ProjectStorage::synchronizeTypeAnnotations(Storage::Synchronization::TypeAn annotationFromDatabase), keyValue("type annotation", annotation)}; - updateTypeAnnotationStatement.write(annotation.typeId, - annotation.iconPath, - createEmptyAsNull(annotation.itemLibraryJson), - createEmptyAsNull(annotation.hintsJson)); + s->updateTypeAnnotationStatement.write(annotation.typeId, + annotation.iconPath, + createEmptyAsNull(annotation.itemLibraryJson), + createEmptyAsNull(annotation.hintsJson)); return Sqlite::UpdateChange::Update; } @@ -1649,7 +2358,7 @@ void ProjectStorage::synchronizeTypeAnnotations(Storage::Synchronization::TypeAn projectStorageCategory(), keyValue("type annotation", annotationFromDatabase)}; - deleteTypeAnnotationStatement.write(annotationFromDatabase.typeId); + s->deleteTypeAnnotationStatement.write(annotationFromDatabase.typeId); }; Sqlite::insertUpdateDelete(range, typeAnnotations, compareKey, insert, update, remove); @@ -1657,7 +2366,7 @@ void ProjectStorage::synchronizeTypeAnnotations(Storage::Synchronization::TypeAn void ProjectStorage::synchronizeTypeTrait(const Storage::Synchronization::Type &type) { - updateTypeTraitStatement.write(type.typeId, type.traits.type); + s->updateTypeTraitStatement.write(type.typeId, type.traits.type); } void ProjectStorage::synchronizeTypes(Storage::Synchronization::Types &types, @@ -1741,7 +2450,7 @@ void ProjectStorage::synchronizeProjectDatas(Storage::Synchronization::ProjectDa < std::tie(second.projectSourceId, second.sourceId); }); - auto range = selectProjectDatasForSourceIdsStatement.range( + auto range = s->selectProjectDatasForSourceIdsStatement.range( toIntegers(updatedProjectSourceIds)); auto insert = [&](const Storage::Synchronization::ProjectData &projectData) { @@ -1755,10 +2464,10 @@ void ProjectStorage::synchronizeProjectDatas(Storage::Synchronization::ProjectDa if (!projectData.sourceId) throw ProjectDataHasInvalidSourceId{}; - insertProjectDataStatement.write(projectData.projectSourceId, - projectData.sourceId, - projectData.moduleId, - projectData.fileType); + s->insertProjectDataStatement.write(projectData.projectSourceId, + projectData.sourceId, + projectData.moduleId, + projectData.fileType); }; auto update = [&](const Storage::Synchronization::ProjectData &projectDataFromDatabase, @@ -1771,10 +2480,10 @@ void ProjectStorage::synchronizeProjectDatas(Storage::Synchronization::ProjectDa keyValue("project data", projectData), keyValue("project data from database", projectDataFromDatabase)}; - updateProjectDataStatement.write(projectData.projectSourceId, - projectData.sourceId, - projectData.moduleId, - projectData.fileType); + s->updateProjectDataStatement.write(projectData.projectSourceId, + projectData.sourceId, + projectData.moduleId, + projectData.fileType); return Sqlite::UpdateChange::Update; } @@ -1787,7 +2496,7 @@ void ProjectStorage::synchronizeProjectDatas(Storage::Synchronization::ProjectDa projectStorageCategory(), keyValue("project data", projectData)}; - deleteProjectDataStatement.write(projectData.projectSourceId, projectData.sourceId); + s->deleteProjectDataStatement.write(projectData.projectSourceId, projectData.sourceId); }; Sqlite::insertUpdateDelete(range, projectDatas, compareKey, insert, update, remove); @@ -1804,7 +2513,7 @@ void ProjectStorage::synchronizeFileStatuses(FileStatuses &fileStatuses, return first.sourceId < second.sourceId; }); - auto range = selectFileStatusesForSourceIdsStatement.range( + auto range = s->selectFileStatusesForSourceIdsStatement.range( toIntegers(updatedSourceIds)); auto insert = [&](const FileStatus &fileStatus) { @@ -1815,7 +2524,9 @@ void ProjectStorage::synchronizeFileStatuses(FileStatuses &fileStatuses, if (!fileStatus.sourceId) throw FileStatusHasInvalidSourceId{}; - insertFileStatusStatement.write(fileStatus.sourceId, fileStatus.size, fileStatus.lastModified); + s->insertFileStatusStatement.write(fileStatus.sourceId, + fileStatus.size, + fileStatus.lastModified); }; auto update = [&](const FileStatus &fileStatusFromDatabase, const FileStatus &fileStatus) { @@ -1827,9 +2538,9 @@ void ProjectStorage::synchronizeFileStatuses(FileStatuses &fileStatuses, keyValue("file status", fileStatus), keyValue("file status from database", fileStatusFromDatabase)}; - updateFileStatusStatement.write(fileStatus.sourceId, - fileStatus.size, - fileStatus.lastModified); + s->updateFileStatusStatement.write(fileStatus.sourceId, + fileStatus.size, + fileStatus.lastModified); return Sqlite::UpdateChange::Update; } @@ -1842,7 +2553,7 @@ void ProjectStorage::synchronizeFileStatuses(FileStatuses &fileStatuses, projectStorageCategory(), keyValue("file status", fileStatus)}; - deleteFileStatusStatement.write(fileStatus.sourceId); + s->deleteFileStatusStatement.write(fileStatus.sourceId); }; Sqlite::insertUpdateDelete(range, fileStatuses, compareKey, insert, update, remove); @@ -1881,7 +2592,7 @@ void ProjectStorage::synchromizeModuleExportedImports( < std::tie(second.moduleId, second.exportedModuleId); }); - auto range = selectModuleExportedImportsForSourceIdStatement + auto range = s->selectModuleExportedImportsForSourceIdStatement .range( toIntegers(updatedModuleIds)); @@ -1903,20 +2614,20 @@ void ProjectStorage::synchromizeModuleExportedImports( tracer.tick("exported module"_t, keyValue("module id", import.exportedModuleId)); if (import.version.minor) { - insertModuleExportedImportWithVersionStatement.write(import.moduleId, - import.exportedModuleId, - import.isAutoVersion, - import.version.major.value, - import.version.minor.value); - } else if (import.version.major) { - insertModuleExportedImportWithMajorVersionStatement.write(import.moduleId, - import.exportedModuleId, - import.isAutoVersion, - import.version.major.value); - } else { - insertModuleExportedImportWithoutVersionStatement.write(import.moduleId, + s->insertModuleExportedImportWithVersionStatement.write(import.moduleId, import.exportedModuleId, - import.isAutoVersion); + import.isAutoVersion, + import.version.major.value, + import.version.minor.value); + } else if (import.version.major) { + s->insertModuleExportedImportWithMajorVersionStatement.write(import.moduleId, + import.exportedModuleId, + import.isAutoVersion, + import.version.major.value); + } else { + s->insertModuleExportedImportWithoutVersionStatement.write(import.moduleId, + import.exportedModuleId, + import.isAutoVersion); } }; @@ -1933,7 +2644,7 @@ void ProjectStorage::synchromizeModuleExportedImports( keyValue("module id", view.moduleId)}; tracer.tick("exported module"_t, keyValue("module id", view.exportedModuleId)); - deleteModuleExportedImportStatement.write(view.moduleExportedImportId); + s->deleteModuleExportedImportStatement.write(view.moduleExportedImportId); }; Sqlite::insertUpdateDelete(range, moduleExportedImports, compareKey, insert, update, remove); @@ -1946,10 +2657,10 @@ ModuleId ProjectStorage::fetchModuleIdUnguarded(Utils::SmallStringView name) con projectStorageCategory(), keyValue("module name", name)}; - auto moduleId = selectModuleIdByNameStatement.value(name); + auto moduleId = s->selectModuleIdByNameStatement.value(name); if (!moduleId) - moduleId = insertModuleNameStatement.value(name); + moduleId = s->insertModuleNameStatement.value(name); tracer.end(keyValue("module id", moduleId)); @@ -1963,7 +2674,7 @@ Utils::PathString ProjectStorage::fetchModuleNameUnguarded(ModuleId id) const projectStorageCategory(), keyValue("module id", id)}; - auto moduleName = selectModuleNameStatement.value(id); + auto moduleName = s->selectModuleNameStatement.value(id); if (moduleName.empty()) throw ModuleDoesNotExists{}; @@ -1988,11 +2699,11 @@ void ProjectStorage::handleAliasPropertyDeclarationsWithPropertyType( ImportedTypeNameId propertyImportedTypeNameId, PropertyDeclarationId aliasPropertyDeclarationId, PropertyDeclarationId aliasPropertyDeclarationTailId) { - auto aliasPropertyName = selectPropertyNameStatement.value( + auto aliasPropertyName = s->selectPropertyNameStatement.value( aliasPropertyDeclarationId); Utils::SmallString aliasPropertyNameTail; if (aliasPropertyDeclarationTailId) - aliasPropertyNameTail = selectPropertyNameStatement.value( + aliasPropertyNameTail = s->selectPropertyNameStatement.value( aliasPropertyDeclarationTailId); relinkableAliasPropertyDeclarations.emplace_back(TypeId{typeId_}, @@ -2001,10 +2712,10 @@ void ProjectStorage::handleAliasPropertyDeclarationsWithPropertyType( std::move(aliasPropertyName), std::move(aliasPropertyNameTail)); - updateAliasPropertyDeclarationToNullStatement.write(propertyDeclarationId); + s->updateAliasPropertyDeclarationToNullStatement.write(propertyDeclarationId); }; - selectAliasPropertiesDeclarationForPropertiesWithTypeIdStatement.readCallback(callback, typeId); + s->selectAliasPropertiesDeclarationForPropertiesWithTypeIdStatement.readCallback(callback, typeId); } void ProjectStorage::handlePropertyDeclarationWithPropertyType( @@ -2017,8 +2728,8 @@ void ProjectStorage::handlePropertyDeclarationWithPropertyType( keyValue("relinkable property declarations", relinkablePropertyDeclarations)}; - updatesPropertyDeclarationPropertyTypeToNullStatement.readTo(relinkablePropertyDeclarations, - typeId); + s->updatesPropertyDeclarationPropertyTypeToNullStatement.readTo(relinkablePropertyDeclarations, + typeId); } void ProjectStorage::handlePrototypes(TypeId prototypeId, Prototypes &relinkablePrototypes) @@ -2033,7 +2744,7 @@ void ProjectStorage::handlePrototypes(TypeId prototypeId, Prototypes &relinkable relinkablePrototypes.emplace_back(typeId, prototypeNameId); }; - updatePrototypeIdToNullStatement.readCallback(callback, prototypeId); + s->updatePrototypeIdToNullStatement.readCallback(callback, prototypeId); } void ProjectStorage::handleExtensions(TypeId extensionId, Prototypes &relinkableExtensions) @@ -2048,7 +2759,7 @@ void ProjectStorage::handleExtensions(TypeId extensionId, Prototypes &relinkable relinkableExtensions.emplace_back(typeId, extensionNameId); }; - updateExtensionIdToNullStatement.readCallback(callback, extensionId); + s->updateExtensionIdToNullStatement.readCallback(callback, extensionId); } void ProjectStorage::deleteType(TypeId typeId, @@ -2064,12 +2775,12 @@ void ProjectStorage::deleteType(TypeId typeId, handleAliasPropertyDeclarationsWithPropertyType(typeId, relinkableAliasPropertyDeclarations); handlePrototypes(typeId, relinkablePrototypes); handleExtensions(typeId, relinkableExtensions); - deleteTypeNamesByTypeIdStatement.write(typeId); - deleteEnumerationDeclarationByTypeIdStatement.write(typeId); - deletePropertyDeclarationByTypeIdStatement.write(typeId); - deleteFunctionDeclarationByTypeIdStatement.write(typeId); - deleteSignalDeclarationByTypeIdStatement.write(typeId); - deleteTypeStatement.write(typeId); + s->deleteTypeNamesByTypeIdStatement.write(typeId); + s->deleteEnumerationDeclarationByTypeIdStatement.write(typeId); + s->deletePropertyDeclarationByTypeIdStatement.write(typeId); + s->deleteFunctionDeclarationByTypeIdStatement.write(typeId); + s->deleteSignalDeclarationByTypeIdStatement.write(typeId); + s->deleteTypeStatement.write(typeId); } void ProjectStorage::relinkAliasPropertyDeclarations(AliasPropertyDeclarations &aliasPropertyDeclarations, @@ -2097,11 +2808,11 @@ void ProjectStorage::relinkAliasPropertyDeclarations(AliasPropertyDeclarations & auto [propertyTypeId, aliasId, propertyTraits] = fetchPropertyDeclarationByTypeIdAndNameUngarded( typeId, alias.aliasPropertyName); - updatePropertyDeclarationWithAliasAndTypeStatement.write(alias.propertyDeclarationId, - propertyTypeId, - propertyTraits, - alias.aliasImportedTypeNameId, - aliasId); + s->updatePropertyDeclarationWithAliasAndTypeStatement.write(alias.propertyDeclarationId, + propertyTypeId, + propertyTraits, + alias.aliasImportedTypeNameId, + aliasId); }, TypeCompare{}); } @@ -2129,8 +2840,8 @@ void ProjectStorage::relinkPropertyDeclarations(PropertyDeclarations &relinkable if (!propertyTypeId) throw TypeNameDoesNotExists{fetchImportedTypeName(property.importedTypeNameId)}; - updatePropertyDeclarationTypeStatement.write(property.propertyDeclarationId, - propertyTypeId); + s->updatePropertyDeclarationTypeStatement.write(property.propertyDeclarationId, + propertyTypeId); }, TypeCompare{}); } @@ -2160,9 +2871,9 @@ void ProjectStorage::deleteNotUpdatedTypes(const TypeIds &updatedTypeIds, relinkableExtensions); }; - selectNotUpdatedTypesInSourcesStatement.readCallback(callback, - toIntegers(updatedSourceIds), - toIntegers(updatedTypeIds)); + s->selectNotUpdatedTypesInSourcesStatement.readCallback(callback, + toIntegers(updatedSourceIds), + toIntegers(updatedTypeIds)); for (TypeId typeIdToBeDeleted : typeIdsToBeDeleted) callback(typeIdToBeDeleted); } @@ -2178,10 +2889,10 @@ void ProjectStorage::relink(AliasPropertyDeclarations &relinkableAliasPropertyDe std::sort(deletedTypeIds.begin(), deletedTypeIds.end()); relinkPrototypes(relinkablePrototypes, deletedTypeIds, [&](TypeId typeId, TypeId prototypeId) { - updateTypePrototypeStatement.write(typeId, prototypeId); + s->updateTypePrototypeStatement.write(typeId, prototypeId); }); relinkPrototypes(relinkableExtensions, deletedTypeIds, [&](TypeId typeId, TypeId prototypeId) { - updateTypeExtensionStatement.write(typeId, prototypeId); + s->updateTypeExtensionStatement.write(typeId, prototypeId); }); relinkPropertyDeclarations(relinkablePropertyDeclarations, deletedTypeIds); relinkAliasPropertyDeclarations(relinkableAliasPropertyDeclarations, deletedTypeIds); @@ -2226,7 +2937,7 @@ void ProjectStorage::linkAliasPropertyDeclarationAliasIds(const AliasPropertyDec aliasDeclaration.aliasPropertyName, aliasDeclaration.aliasPropertyNameTail); - updatePropertyDeclarationAliasIdAndTypeNameIdStatement.write( + s->updatePropertyDeclarationAliasIdAndTypeNameIdStatement.write( aliasDeclaration.propertyDeclarationId, aliasId, aliasDeclaration.aliasImportedTypeNameId); } } @@ -2239,9 +2950,9 @@ void ProjectStorage::updateAliasPropertyDeclarationValues(const AliasPropertyDec keyValue("alias property declarations", aliasDeclarations)}; for (const auto &aliasDeclaration : aliasDeclarations) { - updatetPropertiesDeclarationValuesOfAliasStatement.write( + s->updatetPropertiesDeclarationValuesOfAliasStatement.write( aliasDeclaration.propertyDeclarationId); - updatePropertyAliasDeclarationRecursivelyStatement.write( + s->updatePropertyAliasDeclarationRecursivelyStatement.write( aliasDeclaration.propertyDeclarationId); } } @@ -2298,7 +3009,7 @@ void ProjectStorage::synchronizeExportedTypes(const TypeIds &updatedTypeIds, return first.version < second.version; }); - auto range = selectExportedTypesForSourceIdsStatement + auto range = s->selectExportedTypesForSourceIdsStatement .range(toIntegers(updatedTypeIds)); auto compareKey = [](const Storage::Synchronization::ExportedTypeView &view, @@ -2330,21 +3041,21 @@ void ProjectStorage::synchronizeExportedTypes(const TypeIds &updatedTypeIds, try { if (type.version) { - insertExportedTypeNamesWithVersionStatement.write(type.moduleId, - type.name, - type.version.major.value, - type.version.minor.value, - type.typeId); + s->insertExportedTypeNamesWithVersionStatement.write(type.moduleId, + type.name, + type.version.major.value, + type.version.minor.value, + type.typeId); } else if (type.version.major) { - insertExportedTypeNamesWithMajorVersionStatement.write(type.moduleId, - type.name, - type.version.major.value, - type.typeId); + s->insertExportedTypeNamesWithMajorVersionStatement.write(type.moduleId, + type.name, + type.version.major.value, + type.typeId); } else { - insertExportedTypeNamesWithoutVersionStatement.write(type.moduleId, - type.name, - type.typeId); + s->insertExportedTypeNamesWithoutVersionStatement.write(type.moduleId, + type.name, + type.typeId); } } catch (const Sqlite::ConstraintPreventsModification &) { throw QmlDesigner::ExportedTypeCannotBeInserted{type.name}; @@ -2366,7 +3077,7 @@ void ProjectStorage::synchronizeExportedTypes(const TypeIds &updatedTypeIds, relinkableAliasPropertyDeclarations); handlePrototypes(view.typeId, relinkablePrototypes); handleExtensions(view.typeId, relinkableExtensions); - updateExportedTypeNameTypeIdStatement.write(view.exportedTypeNameId, type.typeId); + s->updateExportedTypeNameTypeIdStatement.write(view.exportedTypeNameId, type.typeId); return Sqlite::UpdateChange::Update; } return Sqlite::UpdateChange::No; @@ -2384,7 +3095,7 @@ void ProjectStorage::synchronizeExportedTypes(const TypeIds &updatedTypeIds, relinkableAliasPropertyDeclarations); handlePrototypes(view.typeId, relinkablePrototypes); handleExtensions(view.typeId, relinkableExtensions); - deleteExportedTypeNameStatement.write(view.exportedTypeNameId); + s->deleteExportedTypeNameStatement.write(view.exportedTypeNameId); }; Sqlite::insertUpdateDelete(range, exportedTypes, compareKey, insert, update, remove); @@ -2411,7 +3122,7 @@ void ProjectStorage::synchronizePropertyDeclarationsInsertAlias( return Sqlite::CallbackControl::Abort; }; - insertAliasPropertyDeclarationStatement.readCallback(callback, typeId, value.name); + s->insertAliasPropertyDeclarationStatement.readCallback(callback, typeId, value.name); } QVarLengthArray ProjectStorage::fetchPropertyDeclarationIds( @@ -2419,12 +3130,12 @@ QVarLengthArray ProjectStorage::fetchPropertyDeclara { QVarLengthArray propertyDeclarationIds; - selectLocalPropertyDeclarationIdsForTypeStatement.readTo(propertyDeclarationIds, baseTypeId); + s->selectLocalPropertyDeclarationIdsForTypeStatement.readTo(propertyDeclarationIds, baseTypeId); - auto range = selectPrototypeAndExtensionIdsStatement.range(baseTypeId); + auto range = s->selectPrototypeAndExtensionIdsStatement.range(baseTypeId); for (TypeId prototype : range) { - selectLocalPropertyDeclarationIdsForTypeStatement.readTo(propertyDeclarationIds, prototype); + s->selectLocalPropertyDeclarationIdsForTypeStatement.readTo(propertyDeclarationIds, prototype); } return propertyDeclarationIds; @@ -2433,10 +3144,10 @@ QVarLengthArray ProjectStorage::fetchPropertyDeclara PropertyDeclarationId ProjectStorage::fetchNextPropertyDeclarationId( TypeId baseTypeId, Utils::SmallStringView propertyName) const { - auto range = selectPrototypeAndExtensionIdsStatement.range(baseTypeId); + auto range = s->selectPrototypeAndExtensionIdsStatement.range(baseTypeId); for (TypeId prototype : range) { - auto propertyDeclarationId = selectPropertyDeclarationIdByTypeIdAndNameStatement + auto propertyDeclarationId = s->selectPropertyDeclarationIdByTypeIdAndNameStatement .value(prototype, propertyName); if (propertyDeclarationId) @@ -2449,7 +3160,7 @@ PropertyDeclarationId ProjectStorage::fetchNextPropertyDeclarationId( PropertyDeclarationId ProjectStorage::fetchPropertyDeclarationId(TypeId typeId, Utils::SmallStringView propertyName) const { - auto propertyDeclarationId = selectPropertyDeclarationIdByTypeIdAndNameStatement + auto propertyDeclarationId = s->selectPropertyDeclarationIdByTypeIdAndNameStatement .value(typeId, propertyName); if (propertyDeclarationId) @@ -2460,10 +3171,10 @@ PropertyDeclarationId ProjectStorage::fetchPropertyDeclarationId(TypeId typeId, PropertyDeclarationId ProjectStorage::fetchNextDefaultPropertyDeclarationId(TypeId baseTypeId) const { - auto range = selectPrototypeAndExtensionIdsStatement.range(baseTypeId); + auto range = s->selectPrototypeAndExtensionIdsStatement.range(baseTypeId); for (TypeId prototype : range) { - auto propertyDeclarationId = selectDefaultPropertyDeclarationIdStatement + auto propertyDeclarationId = s->selectDefaultPropertyDeclarationIdStatement .value(prototype); if (propertyDeclarationId) @@ -2475,8 +3186,8 @@ PropertyDeclarationId ProjectStorage::fetchNextDefaultPropertyDeclarationId(Type PropertyDeclarationId ProjectStorage::fetchDefaultPropertyDeclarationId(TypeId typeId) const { - auto propertyDeclarationId = selectDefaultPropertyDeclarationIdStatement.value( - typeId); + auto propertyDeclarationId = s->selectDefaultPropertyDeclarationIdStatement + .value(typeId); if (propertyDeclarationId) return propertyDeclarationId; @@ -2498,16 +3209,15 @@ void ProjectStorage::synchronizePropertyDeclarationsInsertProperty( if (!propertyTypeId) throw TypeNameDoesNotExists{fetchImportedTypeName(propertyImportedTypeNameId), sourceId}; - auto propertyDeclarationId = insertPropertyDeclarationStatement.value( + auto propertyDeclarationId = s->insertPropertyDeclarationStatement.value( typeId, value.name, propertyTypeId, value.traits, propertyImportedTypeNameId); auto nextPropertyDeclarationId = fetchNextPropertyDeclarationId(typeId, value.name); if (nextPropertyDeclarationId) { - updateAliasIdPropertyDeclarationStatement.write(nextPropertyDeclarationId, - propertyDeclarationId); - updatePropertyAliasDeclarationRecursivelyWithTypeAndTraitsStatement.write(propertyDeclarationId, - propertyTypeId, - value.traits); + s->updateAliasIdPropertyDeclarationStatement.write(nextPropertyDeclarationId, + propertyDeclarationId); + s->updatePropertyAliasDeclarationRecursivelyWithTypeAndTraitsStatement + .write(propertyDeclarationId, propertyTypeId, value.traits); } } @@ -2554,13 +3264,13 @@ Sqlite::UpdateChange ProjectStorage::synchronizePropertyDeclarationsUpdateProper && propertyImportedTypeNameId == view.typeNameId) return Sqlite::UpdateChange::No; - updatePropertyDeclarationStatement.write(view.id, - propertyTypeId, - value.traits, - propertyImportedTypeNameId); - updatePropertyAliasDeclarationRecursivelyWithTypeAndTraitsStatement.write(view.id, - propertyTypeId, - value.traits); + s->updatePropertyDeclarationStatement.write(view.id, + propertyTypeId, + value.traits, + propertyImportedTypeNameId); + s->updatePropertyAliasDeclarationRecursivelyWithTypeAndTraitsStatement.write(view.id, + propertyTypeId, + value.traits); propertyDeclarationIds.push_back(view.id); tracer.end(keyValue("updated", "yes")); @@ -2582,7 +3292,7 @@ void ProjectStorage::synchronizePropertyDeclarations( return Sqlite::compare(first.name, second.name) < 0; }); - auto range = selectPropertyDeclarationsForTypeIdStatement + auto range = s->selectPropertyDeclarationsForTypeIdStatement .range(typeId); auto compareKey = [](const Storage::Synchronization::PropertyDeclarationView &view, @@ -2628,12 +3338,12 @@ void ProjectStorage::synchronizePropertyDeclarations( auto nextPropertyDeclarationId = fetchNextPropertyDeclarationId(typeId, view.name); if (nextPropertyDeclarationId) { - updateAliasPropertyDeclarationByAliasPropertyDeclarationIdStatement + s->updateAliasPropertyDeclarationByAliasPropertyDeclarationIdStatement .write(nextPropertyDeclarationId, view.id); } - updateDefaultPropertyIdToNullStatement.write(view.id); - deletePropertyDeclarationStatement.write(view.id); + s->updateDefaultPropertyIdToNullStatement.write(view.id); + s->deletePropertyDeclarationStatement.write(view.id); propertyDeclarationIds.push_back(view.id); }; @@ -2655,7 +3365,7 @@ void ProjectStorage::resetRemovedAliasPropertyDeclarationsToNull( return Sqlite::compare(first.name, second.name) < 0; }); - auto range = selectPropertyDeclarationsWithAliasForTypeIdStatement + auto range = s->selectPropertyDeclarationsWithAliasForTypeIdStatement .range(type.typeId); auto compareKey = [](const AliasPropertyDeclarationView &view, @@ -2676,7 +3386,7 @@ void ProjectStorage::resetRemovedAliasPropertyDeclarationsToNull( projectStorageCategory(), keyValue("alias property declaration view", view)}; - updatePropertyDeclarationAliasIdToNullStatement.write(view.id); + s->updatePropertyDeclarationAliasIdToNullStatement.write(view.id); propertyDeclarationIds.push_back(view.id); }; @@ -2706,26 +3416,26 @@ ImportId ProjectStorage::insertDocumentImport(const Storage::Import &import, ImportId parentImportId) { if (import.version.minor) { - return insertDocumentImportWithVersionStatement.value(import.sourceId, - import.moduleId, - sourceModuleId, - importKind, - import.version.major.value, - import.version.minor.value, - parentImportId); - } else if (import.version.major) { - return insertDocumentImportWithMajorVersionStatement.value(import.sourceId, - import.moduleId, - sourceModuleId, - importKind, - import.version.major.value, - parentImportId); - } else { - return insertDocumentImportWithoutVersionStatement.value(import.sourceId, + return s->insertDocumentImportWithVersionStatement.value(import.sourceId, import.moduleId, sourceModuleId, importKind, + import.version.major.value, + import.version.minor.value, parentImportId); + } else if (import.version.major) { + return s->insertDocumentImportWithMajorVersionStatement.value(import.sourceId, + import.moduleId, + sourceModuleId, + importKind, + import.version.major.value, + parentImportId); + } else { + return s->insertDocumentImportWithoutVersionStatement.value(import.sourceId, + import.moduleId, + sourceModuleId, + importKind, + parentImportId); } } @@ -2738,8 +3448,9 @@ void ProjectStorage::synchronizeDocumentImports(Storage::Imports &imports, < std::tie(second.sourceId, second.moduleId, second.version); }); - auto range = selectDocumentImportForSourceIdStatement.range( - toIntegers(updatedSourceIds), importKind); + auto range = s->selectDocumentImportForSourceIdStatement + .range(toIntegers(updatedSourceIds), + importKind); auto compareKey = [](const Storage::Synchronization::ImportView &view, const Storage::Import &import) -> long long { @@ -2792,10 +3503,10 @@ void ProjectStorage::synchronizeDocumentImports(Storage::Imports &imports, tracer.end(keyValue("import id", indirectImportId)); }; - selectModuleExportedImportsForModuleIdStatement.readCallback(callback, - import.moduleId, - import.version.major.value, - import.version.minor.value); + s->selectModuleExportedImportsForModuleIdStatement.readCallback(callback, + import.moduleId, + import.version.major.value, + import.version.minor.value); tracer.end(keyValue("import id", importId)); }; @@ -2812,8 +3523,8 @@ void ProjectStorage::synchronizeDocumentImports(Storage::Imports &imports, keyValue("source id", view.sourceId), keyValue("module id", view.moduleId)}; - deleteDocumentImportStatement.write(view.importId); - deleteDocumentImportsWithParentImportIdStatement.write(view.sourceId, view.importId); + s->deleteDocumentImportStatement.write(view.importId); + s->deleteDocumentImportsWithParentImportIdStatement.write(view.sourceId, view.importId); }; Sqlite::insertUpdateDelete(range, imports, compareKey, insert, update, remove); @@ -2858,7 +3569,7 @@ TypeId ProjectStorage::fetchTypeIdByModuleIdAndExportedName(ModuleId moduleId, keyValue("module id", moduleId), keyValue("exported name", name)}; - return selectTypeIdByModuleIdAndExportedNameStatement.value(moduleId, name); + return s->selectTypeIdByModuleIdAndExportedNameStatement.value(moduleId, name); } void ProjectStorage::addTypeIdToPropertyEditorQmlPaths( @@ -2878,7 +3589,7 @@ void ProjectStorage::synchronizePropertyEditorPaths(Storage::Synchronization::Pr return first.typeId < second.typeId; }); - auto range = selectPropertyEditorPathsForForSourceIdsStatement.range( + auto range = s->selectPropertyEditorPathsForForSourceIdsStatement.range( toIntegers(updatedPropertyEditorQmlPathsSourceIds)); auto compareKey = [](const PropertyEditorQmlPathView &view, @@ -2893,7 +3604,7 @@ void ProjectStorage::synchronizePropertyEditorPaths(Storage::Synchronization::Pr keyValue("property editor qml path", path)}; if (path.typeId) - insertPropertyEditorPathStatement.write(path.typeId, path.pathId, path.directoryId); + s->insertPropertyEditorPathStatement.write(path.typeId, path.pathId, path.directoryId); }; auto update = [&](const PropertyEditorQmlPathView &view, const PropertyEditorQmlPath &value) { @@ -2904,7 +3615,7 @@ void ProjectStorage::synchronizePropertyEditorPaths(Storage::Synchronization::Pr keyValue("property editor qml path view", view)}; if (value.pathId != view.pathId || value.directoryId != view.directoryId) { - updatePropertyEditorPathsStatement.write(value.typeId, value.pathId, value.directoryId); + s->updatePropertyEditorPathsStatement.write(value.typeId, value.pathId, value.directoryId); tracer.end(keyValue("updated", "yes")); @@ -2919,7 +3630,7 @@ void ProjectStorage::synchronizePropertyEditorPaths(Storage::Synchronization::Pr projectStorageCategory(), keyValue("property editor qml path view", view)}; - deletePropertyEditorPathStatement.write(view.typeId); + s->deletePropertyEditorPathStatement.write(view.typeId); }; Sqlite::insertUpdateDelete(range, paths, compareKey, insert, update, remove); @@ -2955,7 +3666,7 @@ void ProjectStorage::synchronizeFunctionDeclarations( return compare < 0; }); - auto range = selectFunctionDeclarationsForTypeIdStatement + auto range = s->selectFunctionDeclarationsForTypeIdStatement .range(typeId); auto compareKey = [](const Storage::Synchronization::FunctionDeclarationView &view, @@ -2977,7 +3688,7 @@ void ProjectStorage::synchronizeFunctionDeclarations( Utils::PathString signature{createJson(value.parameters)}; - insertFunctionDeclarationStatement.write(typeId, value.name, value.returnTypeName, signature); + s->insertFunctionDeclarationStatement.write(typeId, value.name, value.returnTypeName, signature); }; auto update = [&](const Storage::Synchronization::FunctionDeclarationView &view, @@ -2993,7 +3704,7 @@ void ProjectStorage::synchronizeFunctionDeclarations( if (value.returnTypeName == view.returnTypeName && signature == view.signature) return Sqlite::UpdateChange::No; - updateFunctionDeclarationStatement.write(view.id, value.returnTypeName, signature); + s->updateFunctionDeclarationStatement.write(view.id, value.returnTypeName, signature); tracer.end(keyValue("updated", "yes")); @@ -3006,7 +3717,7 @@ void ProjectStorage::synchronizeFunctionDeclarations( projectStorageCategory(), keyValue("function declaration view", view)}; - deleteFunctionDeclarationStatement.write(view.id); + s->deleteFunctionDeclarationStatement.write(view.id); }; Sqlite::insertUpdateDelete(range, functionsDeclarations, compareKey, insert, update, remove); @@ -3030,7 +3741,7 @@ void ProjectStorage::synchronizeSignalDeclarations( return compare < 0; }); - auto range = selectSignalDeclarationsForTypeIdStatement + auto range = s->selectSignalDeclarationsForTypeIdStatement .range(typeId); auto compareKey = [](const Storage::Synchronization::SignalDeclarationView &view, @@ -3052,7 +3763,7 @@ void ProjectStorage::synchronizeSignalDeclarations( Utils::PathString signature{createJson(value.parameters)}; - insertSignalDeclarationStatement.write(typeId, value.name, signature); + s->insertSignalDeclarationStatement.write(typeId, value.name, signature); }; auto update = [&]([[maybe_unused]] const Storage::Synchronization::SignalDeclarationView &view, @@ -3066,7 +3777,7 @@ void ProjectStorage::synchronizeSignalDeclarations( projectStorageCategory(), keyValue("signal declaration view", view)}; - deleteSignalDeclarationStatement.write(view.id); + s->deleteSignalDeclarationStatement.write(view.id); }; Sqlite::insertUpdateDelete(range, signalDeclarations, compareKey, insert, update, remove); @@ -3112,7 +3823,7 @@ void ProjectStorage::synchronizeEnumerationDeclarations( return Sqlite::compare(first.name, second.name) < 0; }); - auto range = selectEnumerationDeclarationsForTypeIdStatement + auto range = s->selectEnumerationDeclarationsForTypeIdStatement .range(typeId); auto compareKey = [](const Storage::Synchronization::EnumerationDeclarationView &view, @@ -3128,7 +3839,7 @@ void ProjectStorage::synchronizeEnumerationDeclarations( Utils::PathString signature{createJson(value.enumeratorDeclarations)}; - insertEnumerationDeclarationStatement.write(typeId, value.name, signature); + s->insertEnumerationDeclarationStatement.write(typeId, value.name, signature); }; auto update = [&](const Storage::Synchronization::EnumerationDeclarationView &view, @@ -3144,7 +3855,7 @@ void ProjectStorage::synchronizeEnumerationDeclarations( if (enumeratorDeclarations == view.enumeratorDeclarations) return Sqlite::UpdateChange::No; - updateEnumerationDeclarationStatement.write(view.id, enumeratorDeclarations); + s->updateEnumerationDeclarationStatement.write(view.id, enumeratorDeclarations); tracer.end(keyValue("updated", "yes")); @@ -3157,7 +3868,7 @@ void ProjectStorage::synchronizeEnumerationDeclarations( projectStorageCategory(), keyValue("enumeration declaration view", view)}; - deleteEnumerationDeclarationStatement.write(view.id); + s->deleteEnumerationDeclarationStatement.write(view.id); }; Sqlite::insertUpdateDelete(range, enumerationDeclarations, compareKey, insert, update, remove); @@ -3180,18 +3891,18 @@ TypeId ProjectStorage::declareType(Storage::Synchronization::Type &type) keyValue("type name", type.typeName)}; if (type.typeName.isEmpty()) { - type.typeId = selectTypeIdBySourceIdStatement.value(type.sourceId); + type.typeId = s->selectTypeIdBySourceIdStatement.value(type.sourceId); tracer.end(keyValue("type id", type.typeId)); return type.typeId; } - type.typeId = insertTypeStatement.value(type.sourceId, type.typeName); + type.typeId = s->insertTypeStatement.value(type.sourceId, type.typeName); if (!type.typeId) - type.typeId = selectTypeIdBySourceIdAndNameStatement.value(type.sourceId, - type.typeName); + type.typeId = s->selectTypeIdBySourceIdAndNameStatement.value(type.sourceId, + type.typeName); tracer.end(keyValue("type id", type.typeId)); @@ -3244,7 +3955,7 @@ void ProjectStorage::syncDefaultProperties(Storage::Synchronization::Types &type { NanotraceHR::Tracer tracer{"synchronize default properties"_t, projectStorageCategory()}; - auto range = selectTypesWithDefaultPropertyStatement.range(); + auto range = s->selectTypesWithDefaultPropertyStatement.range(); auto compareKey = [](const TypeWithDefaultPropertyView &view, const Storage::Synchronization::Type &value) { @@ -3273,7 +3984,7 @@ void ProjectStorage::syncDefaultProperties(Storage::Synchronization::Types &type if (compareInvalidAreTrue(valueDefaultPropertyId, view.defaultPropertyId)) return Sqlite::UpdateChange::No; - updateDefaultPropertyIdStatement.write(value.typeId, valueDefaultPropertyId); + s->updateDefaultPropertyIdStatement.write(value.typeId, valueDefaultPropertyId); tracer.end(keyValue("updated", "yes"), keyValue("default property id", valueDefaultPropertyId)); @@ -3290,7 +4001,7 @@ void ProjectStorage::resetDefaultPropertiesIfChanged(Storage::Synchronization::T { NanotraceHR::Tracer tracer{"reset changed default properties"_t, projectStorageCategory()}; - auto range = selectTypesWithDefaultPropertyStatement.range(); + auto range = s->selectTypesWithDefaultPropertyStatement.range(); auto compareKey = [](const TypeWithDefaultPropertyView &view, const Storage::Synchronization::Type &value) { @@ -3321,7 +4032,7 @@ void ProjectStorage::resetDefaultPropertiesIfChanged(Storage::Synchronization::T if (compareInvalidAreTrue(valueDefaultPropertyId, view.defaultPropertyId)) return Sqlite::UpdateChange::No; - updateDefaultPropertyIdStatement.write(value.typeId, Sqlite::NullValue{}); + s->updateDefaultPropertyIdStatement.write(value.typeId, Sqlite::NullValue{}); tracer.end(keyValue("updated", "yes")); @@ -3345,7 +4056,7 @@ void ProjectStorage::checkForPrototypeChainCycle(TypeId typeId) const throw PrototypeChainCycle{}; }; - selectPrototypeAndExtensionIdsStatement.readCallback(callback, typeId); + s->selectPrototypeAndExtensionIdsStatement.readCallback(callback, typeId); } void ProjectStorage::checkForAliasChainCycle(PropertyDeclarationId propertyDeclarationId) const @@ -3359,7 +4070,8 @@ void ProjectStorage::checkForAliasChainCycle(PropertyDeclarationId propertyDecla throw AliasChainCycle{}; }; - selectPropertyDeclarationIdsForAliasChainStatement.readCallback(callback, propertyDeclarationId); + s->selectPropertyDeclarationIdsForAliasChainStatement.readCallback(callback, + propertyDeclarationId); } std::pair ProjectStorage::fetchImportedTypeNameIdAndTypeId( @@ -3405,11 +4117,11 @@ void ProjectStorage::syncPrototypeAndExtension(Storage::Synchronization::Type &t auto [extensionId, extensionTypeNameId] = fetchImportedTypeNameIdAndTypeId(type.extension, type.sourceId); - updatePrototypeAndExtensionStatement.write(type.typeId, - prototypeId, - prototypeTypeNameId, - extensionId, - extensionTypeNameId); + s->updatePrototypeAndExtensionStatement.write(type.typeId, + prototypeId, + prototypeTypeNameId, + extensionId, + extensionTypeNameId); if (prototypeId || extensionId) checkForPrototypeChainCycle(type.typeId); @@ -3448,14 +4160,14 @@ ImportId ProjectStorage::fetchImportId(SourceId sourceId, const Storage::Import ImportId importId; if (import.version) { - importId = selectImportIdBySourceIdAndModuleIdAndVersionStatement.value( + importId = s->selectImportIdBySourceIdAndModuleIdAndVersionStatement.value( sourceId, import.moduleId, import.version.major.value, import.version.minor.value); } else if (import.version.major) { - importId = selectImportIdBySourceIdAndModuleIdAndMajorVersionStatement + importId = s->selectImportIdBySourceIdAndModuleIdAndMajorVersionStatement .value(sourceId, import.moduleId, import.version.major.value); } else { - importId = selectImportIdBySourceIdAndModuleIdStatement.value(sourceId, - import.moduleId); + importId = s->selectImportIdBySourceIdAndModuleIdStatement.value(sourceId, + import.moduleId); } tracer.end(keyValue("import id", importId)); @@ -3515,7 +4227,7 @@ TypeId ProjectStorage::fetchTypeId(ImportedTypeNameId typeNameId) const projectStorageCategory(), keyValue("type name id", typeNameId)}; - auto kind = selectKindFromImportedTypeNamesStatement.value( + auto kind = s->selectKindFromImportedTypeNamesStatement.value( typeNameId); auto typeId = fetchTypeId(typeNameId, kind); @@ -3527,7 +4239,7 @@ TypeId ProjectStorage::fetchTypeId(ImportedTypeNameId typeNameId) const Utils::SmallString ProjectStorage::fetchImportedTypeName(ImportedTypeNameId typeNameId) const { - return selectNameFromImportedTypeNamesStatement.value(typeNameId); + return s->selectNameFromImportedTypeNamesStatement.value(typeNameId); } TypeId ProjectStorage::fetchTypeId(ImportedTypeNameId typeNameId, @@ -3541,9 +4253,9 @@ TypeId ProjectStorage::fetchTypeId(ImportedTypeNameId typeNameId, TypeId typeId; if (kind == Storage::Synchronization::TypeNameKind::Exported) { - typeId = selectTypeIdForImportedTypeNameNamesStatement.value(typeNameId); + typeId = s->selectTypeIdForImportedTypeNameNamesStatement.value(typeNameId); } else { - typeId = selectTypeIdForQualifiedImportedTypeNameNamesStatement.value(typeNameId); + typeId = s->selectTypeIdForQualifiedImportedTypeNameNamesStatement.value(typeNameId); } tracer.end(keyValue("type id", typeId)); @@ -3562,7 +4274,7 @@ ProjectStorage::fetchOptionalPropertyDeclarationByTypeIdAndNameUngarded(TypeId t keyValue("property name", name)}; auto propertyDeclarationId = fetchPropertyDeclarationId(typeId, name); - auto propertyDeclaration = selectPropertyDeclarationResultByPropertyDeclarationIdStatement + auto propertyDeclaration = s->selectPropertyDeclarationResultByPropertyDeclarationIdStatement .optionalValue( propertyDeclarationId); @@ -3615,7 +4327,7 @@ SourceContextId ProjectStorage::readSourceContextId(Utils::SmallStringView sourc projectStorageCategory(), keyValue("source context path", sourceContextPath)}; - auto sourceContextId = selectSourceContextIdFromSourceContextsBySourceContextPathStatement + auto sourceContextId = s->selectSourceContextIdFromSourceContextsBySourceContextPathStatement .value(sourceContextPath); tracer.end(keyValue("source context id", sourceContextId)); @@ -3630,7 +4342,7 @@ SourceContextId ProjectStorage::writeSourceContextId(Utils::SmallStringView sour projectStorageCategory(), keyValue("source context path", sourceContextPath)}; - insertIntoSourceContextsStatement.write(sourceContextPath); + s->insertIntoSourceContextsStatement.write(sourceContextPath); auto sourceContextId = SourceContextId::create(static_cast(database.lastInsertedRowId())); @@ -3648,7 +4360,7 @@ SourceId ProjectStorage::writeSourceId(SourceContextId sourceContextId, keyValue("source context id", sourceContextId), keyValue("source name", sourceName)}; - insertIntoSourcesStatement.write(sourceContextId, sourceName); + s->insertIntoSourcesStatement.write(sourceContextId, sourceName); auto sourceId = SourceId::create(static_cast(database.lastInsertedRowId())); @@ -3666,7 +4378,7 @@ SourceId ProjectStorage::readSourceId(SourceContextId sourceContextId, keyValue("source context id", sourceContextId), keyValue("source name", sourceName)}; - auto sourceId = selectSourceIdFromSourcesBySourceContextIdAndSourceNameStatement + auto sourceId = s->selectSourceIdFromSourcesBySourceContextIdAndSourceNameStatement .value(sourceContextId, sourceName); tracer.end(keyValue("source id", sourceId)); @@ -3681,7 +4393,7 @@ Storage::Synchronization::ExportedTypes ProjectStorage::fetchExportedTypes(TypeI projectStorageCategory(), keyValue("type id", typeId)}; - auto exportedTypes = selectExportedTypesByTypeIdStatement + auto exportedTypes = s->selectExportedTypesByTypeIdStatement .values(typeId); tracer.end(keyValue("exported types", exportedTypes)); @@ -3696,7 +4408,7 @@ Storage::Synchronization::PropertyDeclarations ProjectStorage::fetchPropertyDecl projectStorageCategory(), keyValue("type id", typeId)}; - auto propertyDeclarations = selectPropertyDeclarationsByTypeIdStatement + auto propertyDeclarations = s->selectPropertyDeclarationsByTypeIdStatement .values(typeId); tracer.end(keyValue("property declarations", propertyDeclarations)); @@ -3717,12 +4429,12 @@ Storage::Synchronization::FunctionDeclarations ProjectStorage::fetchFunctionDecl Utils::SmallStringView returnType, FunctionDeclarationId functionDeclarationId) { auto &functionDeclaration = functionDeclarations.emplace_back(name, returnType); - functionDeclaration.parameters = selectFunctionParameterDeclarationsStatement + functionDeclaration.parameters = s->selectFunctionParameterDeclarationsStatement .values( functionDeclarationId); }; - selectFunctionDeclarationsForTypeIdWithoutSignatureStatement.readCallback(callback, typeId); + s->selectFunctionDeclarationsForTypeIdWithoutSignatureStatement.readCallback(callback, typeId); tracer.end(keyValue("function declarations", functionDeclarations)); @@ -3740,12 +4452,12 @@ Storage::Synchronization::SignalDeclarations ProjectStorage::fetchSignalDeclarat auto callback = [&](Utils::SmallStringView name, SignalDeclarationId signalDeclarationId) { auto &signalDeclaration = signalDeclarations.emplace_back(name); - signalDeclaration.parameters = selectSignalParameterDeclarationsStatement + signalDeclaration.parameters = s->selectSignalParameterDeclarationsStatement .values( signalDeclarationId); }; - selectSignalDeclarationsForTypeIdWithoutSignatureStatement.readCallback(callback, typeId); + s->selectSignalDeclarationsForTypeIdWithoutSignatureStatement.readCallback(callback, typeId); tracer.end(keyValue("signal declarations", signalDeclarations)); @@ -3765,16 +4477,68 @@ Storage::Synchronization::EnumerationDeclarations ProjectStorage::fetchEnumerati EnumerationDeclarationId enumerationDeclarationId) { enumerationDeclarations.emplace_back( name, - selectEnumeratorDeclarationStatement + s->selectEnumeratorDeclarationStatement .values(enumerationDeclarationId)); }; - selectEnumerationDeclarationsForTypeIdWithoutEnumeratorDeclarationsStatement.readCallback(callback, - typeId); + s->selectEnumerationDeclarationsForTypeIdWithoutEnumeratorDeclarationsStatement + .readCallback(callback, typeId); tracer.end(keyValue("enumeration declarations", enumerationDeclarations)); return enumerationDeclarations; } +template +bool ProjectStorage::isBasedOn_(TypeId typeId, TypeIds... baseTypeIds) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is based on"_t, + projectStorageCategory(), + keyValue("type id", typeId), + keyValue("base type ids", NanotraceHR::array(baseTypeIds...))}; + + static_assert(((std::is_same_v) &&...), "Parameter must be a TypeId!"); + + if (((typeId == baseTypeIds) || ...)) { + tracer.end(keyValue("is based on", true)); + return true; + } + + auto range = s->selectPrototypeAndExtensionIdsStatement.rangeWithTransaction(typeId); + + auto isBasedOn = std::any_of(range.begin(), range.end(), [&](TypeId currentTypeId) { + return ((currentTypeId == baseTypeIds) || ...); + }); + + tracer.end(keyValue("is based on", isBasedOn)); + + return isBasedOn; +} + +template +ImportedTypeNameId ProjectStorage::fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind kind, + Id id, + Utils::SmallStringView typeName) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch imported type name id"_t, + projectStorageCategory(), + keyValue("imported type name", typeName), + keyValue("kind", kind)}; + + auto importedTypeNameId = s->selectImportedTypeNameIdStatement.value(kind, + id, + typeName); + + if (!importedTypeNameId) + importedTypeNameId = s->insertImportedTypeNameIdStatement.value(kind, + id, + typeName); + + tracer.end(keyValue("imported type name id", importedTypeNameId)); + + return importedTypeNameId; +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index d7687a8c6f9..e7826f531b4 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -39,13 +39,6 @@ class ProjectStorage final : public ProjectStorageInterface friend Storage::Info::CommonTypeCache; public: - template - using ReadStatement = typename Database::template ReadStatement; - template - using ReadWriteStatement = typename Database::template ReadWriteStatement; - template - using WriteStatement = typename Database::template WriteStatement; - ProjectStorage(Database &database, bool isInitialized); ~ProjectStorage(); @@ -174,31 +167,7 @@ public: SmallTypeIds<64> heirIds(TypeId typeId) const override; template - bool isBasedOn_(TypeId typeId, TypeIds... baseTypeIds) const - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"is based on"_t, - projectStorageCategory(), - keyValue("type id", typeId), - keyValue("base type ids", NanotraceHR::array(baseTypeIds...))}; - - static_assert(((std::is_same_v) &&...), "Parameter must be a TypeId!"); - - if (((typeId == baseTypeIds) || ...)) { - tracer.end(keyValue("is based on", true)); - return true; - } - - auto range = selectPrototypeAndExtensionIdsStatement.rangeWithTransaction(typeId); - - auto isBasedOn = std::any_of(range.begin(), range.end(), [&](TypeId currentTypeId) { - return ((currentTypeId == baseTypeIds) || ...); - }); - - tracer.end(keyValue("is based on", isBasedOn)); - - return isBasedOn; - } + bool isBasedOn_(TypeId typeId, TypeIds... baseTypeIds) const; bool isBasedOn(TypeId) const; @@ -256,12 +225,7 @@ public: SourceId fetchSourceIdUnguarded(SourceContextId sourceContextId, Utils::SmallStringView sourceName); - auto fetchAllFileStatuses() const - { - NanotraceHR::Tracer tracer{"fetch all file statuses"_t, projectStorageCategory()}; - - return selectAllFileStatusesStatement.rangeWithTransaction(); - } + FileStatuses fetchAllFileStatuses() const; FileStatus fetchFileStatus(SourceId sourceId) const override; @@ -885,27 +849,7 @@ private: template ImportedTypeNameId fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind kind, Id id, - Utils::SmallStringView typeName) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch imported type name id"_t, - projectStorageCategory(), - keyValue("imported type name", typeName), - keyValue("kind", kind)}; - - auto importedTypeNameId = selectImportedTypeNameIdStatement.value(kind, - id, - typeName); - - if (!importedTypeNameId) - importedTypeNameId = insertImportedTypeNameIdStatement.value(kind, - id, - typeName); - - tracer.end(keyValue("imported type name id", importedTypeNameId)); - - return importedTypeNameId; - } + Utils::SmallStringView typeName); TypeId fetchTypeId(ImportedTypeNameId typeNameId) const; @@ -972,6 +916,8 @@ private: class Initializer; + struct Statements; + public: Database &database; Sqlite::ExclusiveNonThrowingDestructorTransaction exclusiveTransaction; @@ -979,693 +925,7 @@ public: mutable ModuleCache moduleCache{ModuleStorageAdapter{*this}}; Storage::Info::CommonTypeCache commonTypeCache_{*this}; QVarLengthArray observers; - ReadWriteStatement<1, 2> insertTypeStatement{ - "INSERT OR IGNORE INTO types(sourceId, name) VALUES(?1, ?2) RETURNING typeId", database}; - WriteStatement<5> updatePrototypeAndExtensionStatement{ - "UPDATE types SET prototypeId=?2, prototypeNameId=?3, extensionId=?4, extensionNameId=?5 " - "WHERE typeId=?1 AND (prototypeId IS NOT ?2 OR extensionId IS NOT ?3 AND prototypeId " - "IS NOT ?4 OR extensionNameId IS NOT ?5)", - database}; - mutable ReadStatement<1, 1> selectTypeIdByExportedNameStatement{ - "SELECT typeId FROM exportedTypeNames WHERE name=?1", database}; - mutable ReadStatement<1, 2> selectTypeIdByModuleIdAndExportedNameStatement{ - "SELECT typeId FROM exportedTypeNames " - "WHERE moduleId=?1 AND name=?2 " - "ORDER BY majorVersion DESC, minorVersion DESC " - "LIMIT 1", - database}; - mutable ReadStatement<1, 3> selectTypeIdByModuleIdAndExportedNameAndMajorVersionStatement{ - "SELECT typeId FROM exportedTypeNames " - "WHERE moduleId=?1 AND name=?2 AND majorVersion=?3" - "ORDER BY minorVersion DESC " - "LIMIT 1", - database}; - mutable ReadStatement<1, 4> selectTypeIdByModuleIdAndExportedNameAndVersionStatement{ - "SELECT typeId FROM exportedTypeNames " - "WHERE moduleId=?1 AND name=?2 AND majorVersion=?3 AND minorVersion<=?4" - "ORDER BY minorVersion DESC " - "LIMIT 1", - database}; - mutable ReadStatement<3, 1> selectPropertyDeclarationResultByPropertyDeclarationIdStatement{ - "SELECT propertyTypeId, propertyDeclarationId, propertyTraits " - "FROM propertyDeclarations " - "WHERE propertyDeclarationId=?1 " - "LIMIT 1", - database}; - mutable ReadStatement<1, 1> selectSourceContextIdFromSourceContextsBySourceContextPathStatement{ - "SELECT sourceContextId FROM sourceContexts WHERE sourceContextPath = ?", database}; - mutable ReadStatement<1, 1> selectSourceContextPathFromSourceContextsBySourceContextIdStatement{ - "SELECT sourceContextPath FROM sourceContexts WHERE sourceContextId = ?", database}; - mutable ReadStatement<2> selectAllSourceContextsStatement{ - "SELECT sourceContextPath, sourceContextId FROM sourceContexts", database}; - WriteStatement<1> insertIntoSourceContextsStatement{ - "INSERT INTO sourceContexts(sourceContextPath) VALUES (?)", database}; - mutable ReadStatement<1, 2> selectSourceIdFromSourcesBySourceContextIdAndSourceNameStatement{ - "SELECT sourceId FROM sources WHERE sourceContextId = ? AND sourceName = ?", database}; - mutable ReadStatement<2, 1> selectSourceNameAndSourceContextIdFromSourcesBySourceIdStatement{ - "SELECT sourceName, sourceContextId FROM sources WHERE sourceId = ?", database}; - mutable ReadStatement<1, 1> selectSourceContextIdFromSourcesBySourceIdStatement{ - "SELECT sourceContextId FROM sources WHERE sourceId = ?", database}; - WriteStatement<2> insertIntoSourcesStatement{ - "INSERT INTO sources(sourceContextId, sourceName) VALUES (?,?)", database}; - mutable ReadStatement<3> selectAllSourcesStatement{ - "SELECT sourceName, sourceContextId, sourceId FROM sources", database}; - mutable ReadStatement<8, 1> selectTypeByTypeIdStatement{ - "SELECT sourceId, t.name, t.typeId, prototypeId, extensionId, traits, annotationTraits, " - "pd.name " - "FROM types AS t LEFT JOIN propertyDeclarations AS pd ON " - "defaultPropertyId=propertyDeclarationId " - "WHERE t.typeId=?", - database}; - mutable ReadStatement<4, 1> selectExportedTypesByTypeIdStatement{ - "SELECT moduleId, name, ifnull(majorVersion, -1), ifnull(minorVersion, -1) FROM " - "exportedTypeNames WHERE typeId=?", - database}; - mutable ReadStatement<4, 2> selectExportedTypesByTypeIdAndSourceIdStatement{ - "SELECT etn.moduleId, name, ifnull(etn.majorVersion, -1), ifnull(etn.minorVersion, -1) " - "FROM exportedTypeNames AS etn JOIN documentImports USING(moduleId) WHERE typeId=?1 AND " - "sourceId=?2", - database}; - mutable ReadStatement<8> selectTypesStatement{ - "SELECT sourceId, t.name, t.typeId, prototypeId, extensionId, traits, annotationTraits, " - "pd.name " - "FROM types AS t LEFT JOIN propertyDeclarations AS pd ON " - "defaultPropertyId=propertyDeclarationId", - database}; - WriteStatement<2> updateTypeTraitStatement{"UPDATE types SET traits = ?2 WHERE typeId=?1", - database}; - WriteStatement<2> updateTypeAnnotationTraitStatement{ - "UPDATE types SET annotationTraits = ?2 WHERE typeId=?1", database}; - ReadStatement<1, 2> selectNotUpdatedTypesInSourcesStatement{ - "SELECT DISTINCT typeId FROM types WHERE (sourceId IN carray(?1) AND typeId NOT IN " - "carray(?2))", - database}; - WriteStatement<1> deleteTypeNamesByTypeIdStatement{ - "DELETE FROM exportedTypeNames WHERE typeId=?", database}; - WriteStatement<1> deleteEnumerationDeclarationByTypeIdStatement{ - "DELETE FROM enumerationDeclarations WHERE typeId=?", database}; - WriteStatement<1> deletePropertyDeclarationByTypeIdStatement{ - "DELETE FROM propertyDeclarations WHERE typeId=?", database}; - WriteStatement<1> deleteFunctionDeclarationByTypeIdStatement{ - "DELETE FROM functionDeclarations WHERE typeId=?", database}; - WriteStatement<1> deleteSignalDeclarationByTypeIdStatement{ - "DELETE FROM signalDeclarations WHERE typeId=?", database}; - WriteStatement<1> deleteTypeStatement{"DELETE FROM types WHERE typeId=?", database}; - mutable ReadStatement<4, 1> selectPropertyDeclarationsByTypeIdStatement{ - "SELECT name, propertyTypeId, propertyTraits, (SELECT name FROM " - "propertyDeclarations WHERE propertyDeclarationId=pd.aliasPropertyDeclarationId) FROM " - "propertyDeclarations AS pd WHERE typeId=?", - database}; - ReadStatement<6, 1> selectPropertyDeclarationsForTypeIdStatement{ - "SELECT name, propertyTraits, propertyTypeId, propertyImportedTypeNameId, " - "propertyDeclarationId, aliasPropertyDeclarationId FROM propertyDeclarations " - "WHERE typeId=? ORDER BY name", - database}; - ReadWriteStatement<1, 5> insertPropertyDeclarationStatement{ - "INSERT INTO propertyDeclarations(typeId, name, propertyTypeId, propertyTraits, " - "propertyImportedTypeNameId, aliasPropertyDeclarationId) VALUES(?1, ?2, ?3, ?4, ?5, NULL) " - "RETURNING propertyDeclarationId", - database}; - WriteStatement<4> updatePropertyDeclarationStatement{ - "UPDATE propertyDeclarations SET propertyTypeId=?2, propertyTraits=?3, " - "propertyImportedTypeNameId=?4, aliasPropertyDeclarationId=NULL WHERE " - "propertyDeclarationId=?1", - database}; - WriteStatement<3> updatePropertyAliasDeclarationRecursivelyWithTypeAndTraitsStatement{ - "WITH RECURSIVE " - " properties(aliasPropertyDeclarationId) AS ( " - " SELECT propertyDeclarationId FROM propertyDeclarations WHERE " - " aliasPropertyDeclarationId=?1 " - " UNION ALL " - " SELECT pd.propertyDeclarationId FROM " - " propertyDeclarations AS pd JOIN properties USING(aliasPropertyDeclarationId)) " - "UPDATE propertyDeclarations AS pd " - "SET propertyTypeId=?2, propertyTraits=?3 " - "FROM properties AS p " - "WHERE pd.propertyDeclarationId=p.aliasPropertyDeclarationId", - database}; - WriteStatement<1> updatePropertyAliasDeclarationRecursivelyStatement{ - "WITH RECURSIVE " - " propertyValues(propertyTypeId, propertyTraits) AS (" - " SELECT propertyTypeId, propertyTraits FROM propertyDeclarations " - " WHERE propertyDeclarationId=?1), " - " properties(aliasPropertyDeclarationId) AS ( " - " SELECT propertyDeclarationId FROM propertyDeclarations WHERE " - " aliasPropertyDeclarationId=?1 " - " UNION ALL " - " SELECT pd.propertyDeclarationId FROM " - " propertyDeclarations AS pd JOIN properties USING(aliasPropertyDeclarationId)) " - "UPDATE propertyDeclarations AS pd " - "SET propertyTypeId=pv.propertyTypeId, propertyTraits=pv.propertyTraits " - "FROM properties AS p, propertyValues AS pv " - "WHERE pd.propertyDeclarationId=p.aliasPropertyDeclarationId", - database}; - WriteStatement<1> deletePropertyDeclarationStatement{ - "DELETE FROM propertyDeclarations WHERE propertyDeclarationId=?", database}; - ReadStatement<3, 1> selectPropertyDeclarationsWithAliasForTypeIdStatement{ - "SELECT name, propertyDeclarationId, aliasPropertyDeclarationId FROM propertyDeclarations " - "WHERE typeId=? AND aliasPropertyDeclarationId IS NOT NULL ORDER BY name", - database}; - WriteStatement<5> updatePropertyDeclarationWithAliasAndTypeStatement{ - "UPDATE propertyDeclarations SET propertyTypeId=?2, propertyTraits=?3, " - "propertyImportedTypeNameId=?4, aliasPropertyDeclarationId=?5 WHERE " - "propertyDeclarationId=?1", - database}; - ReadWriteStatement<1, 2> insertAliasPropertyDeclarationStatement{ - "INSERT INTO propertyDeclarations(typeId, name) VALUES(?1, ?2) RETURNING " - "propertyDeclarationId", - database}; - mutable ReadStatement<4, 1> selectFunctionDeclarationsForTypeIdStatement{ - "SELECT name, returnTypeName, signature, functionDeclarationId FROM " - "functionDeclarations WHERE typeId=? ORDER BY name, signature", - database}; - mutable ReadStatement<3, 1> selectFunctionDeclarationsForTypeIdWithoutSignatureStatement{ - "SELECT name, returnTypeName, functionDeclarationId FROM " - "functionDeclarations WHERE typeId=? ORDER BY name", - database}; - mutable ReadStatement<3, 1> selectFunctionParameterDeclarationsStatement{ - "SELECT json_extract(json_each.value, '$.n'), json_extract(json_each.value, '$.tn'), " - "json_extract(json_each.value, '$.tr') FROM functionDeclarations, " - "json_each(functionDeclarations.signature) WHERE functionDeclarationId=?", - database}; - WriteStatement<4> insertFunctionDeclarationStatement{ - "INSERT INTO functionDeclarations(typeId, name, returnTypeName, signature) VALUES(?1, ?2, " - "?3, ?4)", - database}; - WriteStatement<3> updateFunctionDeclarationStatement{"UPDATE functionDeclarations " - "SET returnTypeName=?2, signature=?3 " - "WHERE functionDeclarationId=?1", - database}; - WriteStatement<1> deleteFunctionDeclarationStatement{ - "DELETE FROM functionDeclarations WHERE functionDeclarationId=?", database}; - mutable ReadStatement<3, 1> selectSignalDeclarationsForTypeIdStatement{ - "SELECT name, signature, signalDeclarationId FROM signalDeclarations WHERE typeId=? ORDER " - "BY name, signature", - database}; - mutable ReadStatement<2, 1> selectSignalDeclarationsForTypeIdWithoutSignatureStatement{ - "SELECT name, signalDeclarationId FROM signalDeclarations WHERE typeId=? ORDER BY name", - database}; - mutable ReadStatement<3, 1> selectSignalParameterDeclarationsStatement{ - "SELECT json_extract(json_each.value, '$.n'), json_extract(json_each.value, '$.tn'), " - "json_extract(json_each.value, '$.tr') FROM signalDeclarations, " - "json_each(signalDeclarations.signature) WHERE signalDeclarationId=?", - database}; - WriteStatement<3> insertSignalDeclarationStatement{ - "INSERT INTO signalDeclarations(typeId, name, signature) VALUES(?1, ?2, ?3)", database}; - WriteStatement<2> updateSignalDeclarationStatement{ - "UPDATE signalDeclarations SET signature=?2 WHERE signalDeclarationId=?1", database}; - WriteStatement<1> deleteSignalDeclarationStatement{ - "DELETE FROM signalDeclarations WHERE signalDeclarationId=?", database}; - mutable ReadStatement<3, 1> selectEnumerationDeclarationsForTypeIdStatement{ - "SELECT name, enumeratorDeclarations, enumerationDeclarationId FROM " - "enumerationDeclarations WHERE typeId=? ORDER BY name", - database}; - mutable ReadStatement<2, 1> selectEnumerationDeclarationsForTypeIdWithoutEnumeratorDeclarationsStatement{ - "SELECT name, enumerationDeclarationId FROM enumerationDeclarations WHERE typeId=? ORDER " - "BY name", - database}; - mutable ReadStatement<3, 1> selectEnumeratorDeclarationStatement{ - "SELECT json_each.key, json_each.value, json_each.type!='null' FROM " - "enumerationDeclarations, json_each(enumerationDeclarations.enumeratorDeclarations) WHERE " - "enumerationDeclarationId=?", - database}; - WriteStatement<3> insertEnumerationDeclarationStatement{ - "INSERT INTO enumerationDeclarations(typeId, name, enumeratorDeclarations) VALUES(?1, ?2, " - "?3)", - database}; - WriteStatement<2> updateEnumerationDeclarationStatement{ - "UPDATE enumerationDeclarations SET enumeratorDeclarations=?2 WHERE " - "enumerationDeclarationId=?1", - database}; - WriteStatement<1> deleteEnumerationDeclarationStatement{ - "DELETE FROM enumerationDeclarations WHERE enumerationDeclarationId=?", database}; - mutable ReadStatement<1, 1> selectModuleIdByNameStatement{ - "SELECT moduleId FROM modules WHERE name=? LIMIT 1", database}; - mutable ReadWriteStatement<1, 1> insertModuleNameStatement{ - "INSERT INTO modules(name) VALUES(?1) RETURNING moduleId", database}; - mutable ReadStatement<1, 1> selectModuleNameStatement{ - "SELECT name FROM modules WHERE moduleId =?1", database}; - mutable ReadStatement<2> selectAllModulesStatement{"SELECT name, moduleId FROM modules", database}; - mutable ReadStatement<1, 2> selectTypeIdBySourceIdAndNameStatement{ - "SELECT typeId FROM types WHERE sourceId=?1 and name=?2", database}; - mutable ReadStatement<1, 3> selectTypeIdByModuleIdsAndExportedNameStatement{ - "SELECT typeId FROM exportedTypeNames WHERE moduleId IN carray(?1, ?2, 'int32') AND " - "name=?3", - database}; - mutable ReadStatement<4> selectAllDocumentImportForSourceIdStatement{ - "SELECT moduleId, majorVersion, minorVersion, sourceId " - "FROM documentImports ", - database}; - mutable ReadStatement<5, 2> selectDocumentImportForSourceIdStatement{ - "SELECT importId, sourceId, moduleId, majorVersion, minorVersion " - "FROM documentImports WHERE sourceId IN carray(?1) AND kind=?2 ORDER BY sourceId, " - "moduleId, majorVersion, minorVersion", - database}; - ReadWriteStatement<1, 5> insertDocumentImportWithoutVersionStatement{ - "INSERT INTO documentImports(sourceId, moduleId, sourceModuleId, kind, " - "parentImportId) VALUES (?1, ?2, ?3, ?4, ?5) RETURNING importId", - database}; - ReadWriteStatement<1, 6> insertDocumentImportWithMajorVersionStatement{ - "INSERT INTO documentImports(sourceId, moduleId, sourceModuleId, kind, majorVersion, " - "parentImportId) VALUES (?1, ?2, ?3, ?4, ?5, ?6) RETURNING importId", - database}; - ReadWriteStatement<1, 7> insertDocumentImportWithVersionStatement{ - "INSERT INTO documentImports(sourceId, moduleId, sourceModuleId, kind, majorVersion, " - "minorVersion, parentImportId) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7) RETURNING " - "importId", - database}; - WriteStatement<1> deleteDocumentImportStatement{"DELETE FROM documentImports WHERE importId=?1", - database}; - WriteStatement<2> deleteDocumentImportsWithParentImportIdStatement{ - "DELETE FROM documentImports WHERE sourceId=?1 AND parentImportId=?2", database}; - WriteStatement<1> deleteDocumentImportsWithSourceIdsStatement{ - "DELETE FROM documentImports WHERE sourceId IN carray(?1)", database}; - mutable ReadStatement<1, 2> selectPropertyDeclarationIdByTypeIdAndNameStatement{ - "SELECT propertyDeclarationId " - "FROM propertyDeclarations " - "WHERE typeId=?1 AND name=?2 " - "LIMIT 1", - database}; - WriteStatement<2> updateAliasIdPropertyDeclarationStatement{ - "UPDATE propertyDeclarations SET aliasPropertyDeclarationId=?2 WHERE " - "aliasPropertyDeclarationId=?1", - database}; - WriteStatement<2> updateAliasPropertyDeclarationByAliasPropertyDeclarationIdStatement{ - "UPDATE propertyDeclarations SET propertyTypeId=new.propertyTypeId, " - "propertyTraits=new.propertyTraits, aliasPropertyDeclarationId=?1 FROM (SELECT " - "propertyTypeId, propertyTraits FROM propertyDeclarations WHERE propertyDeclarationId=?1) " - "AS new WHERE aliasPropertyDeclarationId=?2", - database}; - WriteStatement<1> updateAliasPropertyDeclarationToNullStatement{ - "UPDATE propertyDeclarations SET aliasPropertyDeclarationId=NULL, propertyTypeId=NULL, " - "propertyTraits=NULL WHERE propertyDeclarationId=? AND (aliasPropertyDeclarationId IS NOT " - "NULL OR propertyTypeId IS NOT NULL OR propertyTraits IS NOT NULL)", - database}; - ReadStatement<5, 1> selectAliasPropertiesDeclarationForPropertiesWithTypeIdStatement{ - "SELECT alias.typeId, alias.propertyDeclarationId, alias.propertyImportedTypeNameId, " - " alias.aliasPropertyDeclarationId, alias.aliasPropertyDeclarationTailId " - "FROM propertyDeclarations AS alias JOIN propertyDeclarations AS target " - " ON alias.aliasPropertyDeclarationId=target.propertyDeclarationId OR " - " alias.aliasPropertyDeclarationTailId=target.propertyDeclarationId " - "WHERE alias.propertyTypeId=?1 " - "UNION ALL " - "SELECT alias.typeId, alias.propertyDeclarationId, alias.propertyImportedTypeNameId, " - " alias.aliasPropertyDeclarationId, alias.aliasPropertyDeclarationTailId " - "FROM propertyDeclarations AS alias JOIN propertyDeclarations AS target " - " ON alias.aliasPropertyDeclarationId=target.propertyDeclarationId OR " - " alias.aliasPropertyDeclarationTailId=target.propertyDeclarationId " - "WHERE target.typeId=?1 " - "UNION ALL " - "SELECT alias.typeId, alias.propertyDeclarationId, alias.propertyImportedTypeNameId, " - " alias.aliasPropertyDeclarationId, alias.aliasPropertyDeclarationTailId " - "FROM propertyDeclarations AS alias JOIN propertyDeclarations AS target " - " ON alias.aliasPropertyDeclarationId=target.propertyDeclarationId OR " - " alias.aliasPropertyDeclarationTailId=target.propertyDeclarationId " - "WHERE alias.propertyImportedTypeNameId IN " - " (SELECT importedTypeNameId FROM exportedTypeNames JOIN importedTypeNames USING(name) " - " WHERE typeId=?1)", - database}; - ReadStatement<3, 1> selectAliasPropertiesDeclarationForPropertiesWithAliasIdStatement{ - "WITH RECURSIVE " - " properties(propertyDeclarationId, propertyImportedTypeNameId, typeId, " - " aliasPropertyDeclarationId) AS (" - " SELECT propertyDeclarationId, propertyImportedTypeNameId, typeId, " - " aliasPropertyDeclarationId FROM propertyDeclarations WHERE " - " aliasPropertyDeclarationId=?1" - " UNION ALL " - " SELECT pd.propertyDeclarationId, pd.propertyImportedTypeNameId, pd.typeId, " - " pd.aliasPropertyDeclarationId FROM propertyDeclarations AS pd JOIN properties AS " - " p ON pd.aliasPropertyDeclarationId=p.propertyDeclarationId)" - "SELECT propertyDeclarationId, propertyImportedTypeNameId, aliasPropertyDeclarationId " - " FROM properties", - database}; - ReadWriteStatement<3, 1> updatesPropertyDeclarationPropertyTypeToNullStatement{ - "UPDATE propertyDeclarations SET propertyTypeId=NULL WHERE propertyTypeId=?1 AND " - "aliasPropertyDeclarationId IS NULL RETURNING typeId, propertyDeclarationId, " - "propertyImportedTypeNameId", - database}; - mutable ReadStatement<1, 1> selectPropertyNameStatement{ - "SELECT name FROM propertyDeclarations WHERE propertyDeclarationId=?", database}; - WriteStatement<2> updatePropertyDeclarationTypeStatement{ - "UPDATE propertyDeclarations SET propertyTypeId=?2 WHERE propertyDeclarationId=?1", database}; - ReadWriteStatement<2, 1> updatePrototypeIdToNullStatement{ - "UPDATE types SET prototypeId=NULL WHERE prototypeId=?1 RETURNING " - "typeId, prototypeNameId", - database}; - ReadWriteStatement<2, 1> updateExtensionIdToNullStatement{ - "UPDATE types SET extensionId=NULL WHERE extensionId=?1 RETURNING " - "typeId, extensionNameId", - database}; - WriteStatement<2> updateTypePrototypeStatement{ - "UPDATE types SET prototypeId=?2 WHERE typeId=?1", database}; - WriteStatement<2> updateTypeExtensionStatement{ - "UPDATE types SET extensionId=?2 WHERE typeId=?1", database}; - mutable ReadStatement<1, 1> selectPrototypeAndExtensionIdsStatement{ - "WITH RECURSIVE " - " prototypes(typeId) AS ( " - " SELECT prototypeId FROM types WHERE typeId=?1 " - " UNION ALL " - " SELECT extensionId FROM types WHERE typeId=?1 " - " UNION ALL " - " SELECT prototypeId FROM types JOIN prototypes USING(typeId) " - " UNION ALL " - " SELECT extensionId FROM types JOIN prototypes USING(typeId)) " - "SELECT typeId FROM prototypes WHERE typeId IS NOT NULL", - database}; - WriteStatement<3> updatePropertyDeclarationAliasIdAndTypeNameIdStatement{ - "UPDATE propertyDeclarations SET aliasPropertyDeclarationId=?2, " - "propertyImportedTypeNameId=?3 WHERE propertyDeclarationId=?1 AND " - "(aliasPropertyDeclarationId IS NOT ?2 OR propertyImportedTypeNameId IS NOT ?3)", - database}; - WriteStatement<1> updatetPropertiesDeclarationValuesOfAliasStatement{ - "WITH RECURSIVE " - " properties(propertyDeclarationId, propertyTypeId, propertyTraits) AS ( " - " SELECT aliasPropertyDeclarationId, propertyTypeId, propertyTraits FROM " - " propertyDeclarations WHERE propertyDeclarationId=?1 " - " UNION ALL " - " SELECT pd.aliasPropertyDeclarationId, pd.propertyTypeId, pd.propertyTraits FROM " - " propertyDeclarations AS pd JOIN properties USING(propertyDeclarationId)) " - "UPDATE propertyDeclarations AS pd SET propertyTypeId=p.propertyTypeId, " - " propertyTraits=p.propertyTraits " - "FROM properties AS p " - "WHERE pd.propertyDeclarationId=?1 AND p.propertyDeclarationId IS NULL AND " - " (pd.propertyTypeId IS NOT p.propertyTypeId OR pd.propertyTraits IS NOT " - " p.propertyTraits)", - database}; - WriteStatement<1> updatePropertyDeclarationAliasIdToNullStatement{ - "UPDATE propertyDeclarations SET aliasPropertyDeclarationId=NULL WHERE " - "propertyDeclarationId=?1", - database}; - mutable ReadStatement<1, 1> selectPropertyDeclarationIdsForAliasChainStatement{ - "WITH RECURSIVE " - " properties(propertyDeclarationId) AS ( " - " SELECT aliasPropertyDeclarationId FROM propertyDeclarations WHERE " - " propertyDeclarationId=?1 " - " UNION ALL " - " SELECT aliasPropertyDeclarationId FROM propertyDeclarations JOIN properties " - " USING(propertyDeclarationId)) " - "SELECT propertyDeclarationId FROM properties", - database}; - mutable ReadStatement<3> selectAllFileStatusesStatement{ - "SELECT sourceId, size, lastModified FROM fileStatuses ORDER BY sourceId", database}; - mutable ReadStatement<3, 1> selectFileStatusesForSourceIdsStatement{ - "SELECT sourceId, size, lastModified FROM fileStatuses WHERE sourceId IN carray(?1) ORDER " - "BY sourceId", - database}; - mutable ReadStatement<3, 1> selectFileStatusesForSourceIdStatement{ - "SELECT sourceId, size, lastModified FROM fileStatuses WHERE sourceId=?1 ORDER BY sourceId", - database}; - WriteStatement<3> insertFileStatusStatement{ - "INSERT INTO fileStatuses(sourceId, size, lastModified) VALUES(?1, ?2, ?3)", database}; - WriteStatement<1> deleteFileStatusStatement{"DELETE FROM fileStatuses WHERE sourceId=?1", - database}; - WriteStatement<3> updateFileStatusStatement{ - "UPDATE fileStatuses SET size=?2, lastModified=?3 WHERE sourceId=?1", database}; - ReadStatement<1, 1> selectTypeIdBySourceIdStatement{"SELECT typeId FROM types WHERE sourceId=?", - database}; - mutable ReadStatement<1, 3> selectImportedTypeNameIdStatement{ - "SELECT importedTypeNameId FROM importedTypeNames WHERE kind=?1 AND importOrSourceId=?2 " - "AND name=?3 LIMIT 1", - database}; - mutable ReadWriteStatement<1, 3> insertImportedTypeNameIdStatement{ - "INSERT INTO importedTypeNames(kind, importOrSourceId, name) VALUES (?1, ?2, ?3) " - "RETURNING importedTypeNameId", - database}; - mutable ReadStatement<1, 2> selectImportIdBySourceIdAndModuleIdStatement{ - "SELECT importId FROM documentImports WHERE sourceId=?1 AND moduleId=?2 AND majorVersion " - "IS NULL AND minorVersion IS NULL LIMIT 1", - database}; - mutable ReadStatement<1, 3> selectImportIdBySourceIdAndModuleIdAndMajorVersionStatement{ - "SELECT importId FROM documentImports WHERE sourceId=?1 AND moduleId=?2 AND " - "majorVersion=?3 AND minorVersion IS NULL LIMIT 1", - database}; - mutable ReadStatement<1, 4> selectImportIdBySourceIdAndModuleIdAndVersionStatement{ - "SELECT importId FROM documentImports WHERE sourceId=?1 AND moduleId=?2 AND " - "majorVersion=?3 AND minorVersion=?4 LIMIT 1", - database}; - mutable ReadStatement<1, 1> selectKindFromImportedTypeNamesStatement{ - "SELECT kind FROM importedTypeNames WHERE importedTypeNameId=?1", database}; - mutable ReadStatement<1, 1> selectNameFromImportedTypeNamesStatement{ - "SELECT name FROM importedTypeNames WHERE importedTypeNameId=?1", database}; - mutable ReadStatement<1, 1> selectTypeIdForQualifiedImportedTypeNameNamesStatement{ - "SELECT typeId FROM importedTypeNames AS itn JOIN documentImports AS di ON " - "importOrSourceId=di.importId JOIN documentImports AS di2 ON di.sourceId=di2.sourceId AND " - "di.moduleId=di2.sourceModuleId " - "JOIN exportedTypeNames AS etn ON di2.moduleId=etn.moduleId WHERE " - "itn.kind=2 AND importedTypeNameId=?1 AND itn.name=etn.name AND " - "(di.majorVersion IS NULL OR (di.majorVersion=etn.majorVersion AND (di.minorVersion IS " - "NULL OR di.minorVersion>=etn.minorVersion))) ORDER BY etn.majorVersion DESC NULLS FIRST, " - "etn.minorVersion DESC NULLS FIRST LIMIT 1", - database}; - mutable ReadStatement<1, 1> selectTypeIdForImportedTypeNameNamesStatement{ - "WITH " - " importTypeNames(moduleId, name, kind, majorVersion, minorVersion) AS ( " - " SELECT moduleId, name, di.kind, majorVersion, minorVersion " - " FROM importedTypeNames AS itn JOIN documentImports AS di ON " - " importOrSourceId=sourceId " - " WHERE " - " importedTypeNameId=?1 AND itn.kind=1) " - "SELECT typeId FROM importTypeNames AS itn " - " JOIN exportedTypeNames AS etn USING(moduleId, name) " - "WHERE (itn.majorVersion IS NULL OR (itn.majorVersion=etn.majorVersion " - " AND (itn.minorVersion IS NULL OR itn.minorVersion>=etn.minorVersion))) " - "ORDER BY itn.kind, etn.majorVersion DESC NULLS FIRST, etn.minorVersion DESC NULLS FIRST " - "LIMIT 1", - database}; - WriteStatement<0> deleteAllSourcesStatement{"DELETE FROM sources", database}; - WriteStatement<0> deleteAllSourceContextsStatement{"DELETE FROM sourceContexts", database}; - mutable ReadStatement<6, 1> selectExportedTypesForSourceIdsStatement{ - "SELECT moduleId, name, ifnull(majorVersion, -1), ifnull(minorVersion, -1), typeId, " - "exportedTypeNameId FROM exportedTypeNames WHERE typeId in carray(?1) ORDER BY moduleId, " - "name, majorVersion, minorVersion", - database}; - WriteStatement<5> insertExportedTypeNamesWithVersionStatement{ - "INSERT INTO exportedTypeNames(moduleId, name, majorVersion, minorVersion, typeId) " - "VALUES(?1, ?2, ?3, ?4, ?5)", - database}; - WriteStatement<4> insertExportedTypeNamesWithMajorVersionStatement{ - "INSERT INTO exportedTypeNames(moduleId, name, majorVersion, typeId) " - "VALUES(?1, ?2, ?3, ?4)", - database}; - WriteStatement<3> insertExportedTypeNamesWithoutVersionStatement{ - "INSERT INTO exportedTypeNames(moduleId, name, typeId) VALUES(?1, ?2, ?3)", database}; - WriteStatement<1> deleteExportedTypeNameStatement{ - "DELETE FROM exportedTypeNames WHERE exportedTypeNameId=?", database}; - WriteStatement<2> updateExportedTypeNameTypeIdStatement{ - "UPDATE exportedTypeNames SET typeId=?2 WHERE exportedTypeNameId=?1", database}; - mutable ReadStatement<4, 1> selectProjectDatasForSourceIdsStatement{ - "SELECT projectSourceId, sourceId, moduleId, fileType FROM projectDatas WHERE " - "projectSourceId IN carray(?1) ORDER BY projectSourceId, sourceId", - database}; - WriteStatement<4> insertProjectDataStatement{ - "INSERT INTO projectDatas(projectSourceId, sourceId, " - "moduleId, fileType) VALUES(?1, ?2, ?3, ?4)", - database}; - WriteStatement<2> deleteProjectDataStatement{ - "DELETE FROM projectDatas WHERE projectSourceId=?1 AND sourceId=?2", database}; - WriteStatement<4> updateProjectDataStatement{ - "UPDATE projectDatas SET moduleId=?3, fileType=?4 WHERE projectSourceId=?1 AND sourceId=?2", - database}; - 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{ - "SELECT moduleExportedImportId, moduleId, exportedModuleId, ifnull(majorVersion, -1), " - "ifnull(minorVersion, -1), isAutoVersion FROM moduleExportedImports WHERE moduleId IN " - "carray(?1) ORDER BY moduleId, exportedModuleId", - database}; - WriteStatement<3> insertModuleExportedImportWithoutVersionStatement{ - "INSERT INTO moduleExportedImports(moduleId, exportedModuleId, isAutoVersion) " - "VALUES (?1, ?2, ?3)", - database}; - WriteStatement<4> insertModuleExportedImportWithMajorVersionStatement{ - "INSERT INTO moduleExportedImports(moduleId, exportedModuleId, isAutoVersion, " - "majorVersion) VALUES (?1, ?2, ?3, ?4)", - database}; - WriteStatement<5> insertModuleExportedImportWithVersionStatement{ - "INSERT INTO moduleExportedImports(moduleId, exportedModuleId, isAutoVersion, " - "majorVersion, minorVersion) VALUES (?1, ?2, ?3, ?4, ?5)", - database}; - WriteStatement<1> deleteModuleExportedImportStatement{ - "DELETE FROM moduleExportedImports WHERE moduleExportedImportId=?1", database}; - mutable ReadStatement<3, 3> selectModuleExportedImportsForModuleIdStatement{ - "WITH RECURSIVE " - " imports(moduleId, majorVersion, minorVersion, moduleExportedImportId) AS ( " - " SELECT exportedModuleId, " - " iif(isAutoVersion=1, ?2, majorVersion), " - " iif(isAutoVersion=1, ?3, minorVersion), " - " moduleExportedImportId " - " FROM moduleExportedImports WHERE moduleId=?1 " - " UNION ALL " - " SELECT exportedModuleId, " - " iif(mei.isAutoVersion=1, i.majorVersion, mei.majorVersion), " - " iif(mei.isAutoVersion=1, i.minorVersion, mei.minorVersion), " - " mei.moduleExportedImportId " - " FROM moduleExportedImports AS mei JOIN imports AS i USING(moduleId)) " - "SELECT DISTINCT moduleId, ifnull(majorVersion, -1), ifnull(minorVersion, -1) " - "FROM imports", - database}; - mutable ReadStatement<1, 1> selectLocalPropertyDeclarationIdsForTypeStatement{ - "SELECT propertyDeclarationId " - "FROM propertyDeclarations " - "WHERE typeId=? " - "ORDER BY propertyDeclarationId", - database}; - mutable ReadStatement<1, 2> selectLocalPropertyDeclarationIdForTypeAndPropertyNameStatement{ - "SELECT propertyDeclarationId " - "FROM propertyDeclarations " - "WHERE typeId=?1 AND name=?2 LIMIT 1", - database}; - mutable ReadStatement<4, 1> selectPropertyDeclarationForPropertyDeclarationIdStatement{ - "SELECT typeId, name, propertyTraits, propertyTypeId " - "FROM propertyDeclarations " - "WHERE propertyDeclarationId=?1 LIMIT 1", - database}; - mutable ReadStatement<1, 1> selectSignalDeclarationNamesForTypeStatement{ - "WITH RECURSIVE " - " all_prototype_and_extension(typeId, prototypeId) AS (" - " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL" - " UNION ALL " - " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL)," - " typeChain(typeId) AS (" - " VALUES(?1)" - " UNION ALL " - " SELECT prototypeId FROM all_prototype_and_extension JOIN typeChain " - " USING(typeId)) " - "SELECT name FROM typeChain JOIN signalDeclarations " - " USING(typeId) ORDER BY name", - database}; - mutable ReadStatement<1, 1> selectFuncionDeclarationNamesForTypeStatement{ - "WITH RECURSIVE " - " all_prototype_and_extension(typeId, prototypeId) AS (" - " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL" - " UNION ALL " - " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL)," - " typeChain(typeId) AS (" - " VALUES(?1)" - " UNION ALL " - " SELECT prototypeId FROM all_prototype_and_extension JOIN typeChain " - " USING(typeId))" - "SELECT name FROM typeChain JOIN functionDeclarations " - " USING(typeId) ORDER BY name", - database}; - mutable ReadStatement<2> selectTypesWithDefaultPropertyStatement{ - "SELECT typeId, defaultPropertyId FROM types ORDER BY typeId", database}; - WriteStatement<2> updateDefaultPropertyIdStatement{ - "UPDATE types SET defaultPropertyId=?2 WHERE typeId=?1", database}; - WriteStatement<1> updateDefaultPropertyIdToNullStatement{ - "UPDATE types SET defaultPropertyId=NULL WHERE defaultPropertyId=?1", database}; - mutable ReadStatement<3, 1> selectInfoTypeByTypeIdStatement{ - "SELECT sourceId, traits, annotationTraits FROM types WHERE typeId=?", database}; - mutable ReadStatement<1, 1> selectDefaultPropertyDeclarationIdStatement{ - "SELECT defaultPropertyId FROM types WHERE typeId=?", database}; - mutable ReadStatement<1, 1> selectPrototypeIdsForTypeIdInOrderStatement{ - "WITH RECURSIVE " - " all_prototype_and_extension(typeId, prototypeId) AS (" - " SELECT typeId, prototypeId FROM types WHERE prototypeId IS NOT NULL" - " UNION ALL " - " SELECT typeId, extensionId FROM types WHERE extensionId IS NOT NULL)," - " prototypes(typeId, level) AS (" - " SELECT prototypeId, 0 FROM all_prototype_and_extension WHERE typeId=?" - " UNION ALL " - " SELECT prototypeId, p.level+1 FROM all_prototype_and_extension JOIN " - " prototypes AS p USING(typeId)) " - "SELECT typeId FROM prototypes ORDER BY level", - database}; - WriteStatement<2> upsertPropertyEditorPathIdStatement{ - "INSERT INTO propertyEditorPaths(typeId, pathSourceId) VALUES(?1, ?2) ON CONFLICT DO " - "UPDATE SET pathSourceId=excluded.pathSourceId WHERE pathSourceId IS NOT " - "excluded.pathSourceId", - database}; - mutable ReadStatement<1, 1> selectPropertyEditorPathIdStatement{ - "SELECT pathSourceId FROM propertyEditorPaths WHERE typeId=?", database}; - mutable ReadStatement<3, 1> selectPropertyEditorPathsForForSourceIdsStatement{ - "SELECT typeId, pathSourceId, directoryId " - "FROM propertyEditorPaths " - "WHERE directoryId IN carray(?1) " - "ORDER BY typeId", - database}; - WriteStatement<3> insertPropertyEditorPathStatement{ - "INSERT INTO propertyEditorPaths(typeId, pathSourceId, directoryId) VALUES (?1, ?2, ?3)", - database}; - WriteStatement<3> updatePropertyEditorPathsStatement{"UPDATE propertyEditorPaths " - "SET pathSourceId=?2, directoryId=?3 " - "WHERE typeId=?1", - database}; - WriteStatement<1> deletePropertyEditorPathStatement{ - "DELETE FROM propertyEditorPaths WHERE typeId=?1", database}; - mutable ReadStatement<4, 1> selectTypeAnnotationsForSourceIdsStatement{ - "SELECT typeId, iconPath, itemLibrary, hints FROM typeAnnotations WHERE " - "sourceId IN carray(?1) ORDER BY typeId", - database}; - WriteStatement<6> insertTypeAnnotationStatement{ - "INSERT INTO " - " typeAnnotations(typeId, sourceId, directorySourceId, iconPath, itemLibrary, hints) " - "VALUES(?1, ?2, ?3, ?4, ?5, ?6)", - database}; - WriteStatement<4> updateTypeAnnotationStatement{ - "UPDATE typeAnnotations SET iconPath=?2, itemLibrary=?3, hints=?4 WHERE typeId=?1", database}; - WriteStatement<1> deleteTypeAnnotationStatement{"DELETE FROM typeAnnotations WHERE typeId=?1", - database}; - mutable ReadStatement<1, 1> selectTypeIconPathStatement{ - "SELECT iconPath FROM typeAnnotations WHERE typeId=?1", database}; - mutable ReadStatement<2, 1> selectTypeHintsStatement{ - "SELECT hints.key, hints.value " - "FROM typeAnnotations, json_each(typeAnnotations.hints) AS hints " - "WHERE typeId=?1 AND hints IS NOT NULL", - database}; - mutable ReadStatement<1, 1> selectTypeAnnotationSourceIdsStatement{ - "SELECT sourceId FROM typeAnnotations WHERE directorySourceId=?1 ORDER BY sourceId", database}; - mutable ReadStatement<1, 0> selectTypeAnnotationDirectorySourceIdsStatement{ - "SELECT DISTINCT directorySourceId FROM typeAnnotations ORDER BY directorySourceId", database}; - mutable ReadStatement<9> selectItemLibraryEntriesStatement{ - "SELECT typeId, i.value->>'$.name', i.value->>'$.iconPath', i.value->>'$.category', " - " i.value->>'$.import', i.value->>'$.toolTip', i.value->>'$.properties', " - " i.value->>'$.extraFilePaths', i.value->>'$.templatePath' " - "FROM typeAnnotations AS ta , json_each(ta.itemLibrary) AS i " - "WHERE ta.itemLibrary IS NOT NULL", - database}; - mutable ReadStatement<9, 1> selectItemLibraryEntriesByTypeIdStatement{ - "SELECT typeId, i.value->>'$.name', i.value->>'$.iconPath', i.value->>'$.category', " - " i.value->>'$.import', i.value->>'$.toolTip', i.value->>'$.properties', " - " i.value->>'$.extraFilePaths', i.value->>'$.templatePath' " - "FROM typeAnnotations AS ta, json_each(ta.itemLibrary) AS i " - "WHERE typeId=?1 AND ta.itemLibrary IS NOT NULL", - database}; - mutable ReadStatement<9, 1> selectItemLibraryEntriesBySourceIdStatement{ - "SELECT typeId, i.value->>'$.name', i.value->>'$.iconPath', " - "i.value->>'$.category', " - " i.value->>'$.import', i.value->>'$.toolTip', i.value->>'$.properties', " - " i.value->>'$.extraFilePaths', i.value->>'$.templatePath' " - "FROM typeAnnotations, json_each(typeAnnotations.itemLibrary) AS i " - "WHERE typeId IN (SELECT DISTINCT typeId " - " FROM documentImports AS di JOIN exportedTypeNames " - " USING(moduleId) " - " WHERE di.sourceId=?)", - database}; - mutable ReadStatement<3, 1> selectItemLibraryPropertiesStatement{ - "SELECT p.value->>0, p.value->>1, p.value->>2 FROM json_each(?1) AS p", database}; - mutable ReadStatement<1, 1> selectItemLibraryExtraFilePathsStatement{ - "SELECT p.value FROM json_each(?1) AS p", database}; - mutable ReadStatement<1, 1> selectTypeIdsByModuleIdStatement{ - "SELECT DISTINCT typeId FROM exportedTypeNames WHERE moduleId=?", database}; - mutable ReadStatement<1, 1> selectHeirTypeIdsStatement{ - "WITH RECURSIVE " - " typeSelection(typeId) AS (" - " SELECT typeId FROM types WHERE prototypeId=?1 OR extensionId=?1" - " UNION ALL " - " SELECT t.typeId " - " FROM types AS t JOIN typeSelection AS ts " - " WHERE prototypeId=ts.typeId OR extensionId=ts.typeId)" - "SELECT typeId FROM typeSelection", - database}; + std::unique_ptr s; }; From 592d7f0c942cc5a804dd07613dc2c5822eee4306 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Mon, 22 Apr 2024 14:19:18 +0200 Subject: [PATCH 194/202] TextEditor: Fix calculating block highlight background color Fixes: QTCREATORBUG-30649 Change-Id: I9a213ecdf4d58ed6531014c99bbdedac8ac9ef00 Reviewed-by: Christian Stenger --- src/plugins/texteditor/texteditor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index 280ca8e433f..1d90c7481f2 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -4381,7 +4381,7 @@ static QColor calcBlendColor(const QColor &baseColor, int level, int count) if (level == count - 1) return color90; - const int blendFactor = level * (256 / (count - 2)); + const int blendFactor = level * (256 / (count - 1)); return blendColors(color80, color90, blendFactor); } From 59ebb8a6a70c6d0864915da05850b35e090b6ad4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20L=C3=B6hning?= Date: Thu, 18 Apr 2024 21:15:00 +0200 Subject: [PATCH 195/202] SquishTests: Autodetect cdb Creator automatically detects the debuggers anyway and then can't handle the fixed ID. Change-Id: I732f86649b02016be2ca513c896a80ce254d721f Reviewed-by: Christian Stenger Reviewed-by: --- .../windows/QtProject/qtcreator/debuggers.xml | 19 +------------- .../windows/QtProject/qtcreator/profiles.xml | 3 --- tests/system/shared/qtcreator.py | 26 ------------------- 3 files changed, 1 insertion(+), 47 deletions(-) diff --git a/tests/system/settings/windows/QtProject/qtcreator/debuggers.xml b/tests/system/settings/windows/QtProject/qtcreator/debuggers.xml index b8d5b705e11..64016440ae7 100644 --- a/tests/system/settings/windows/QtProject/qtcreator/debuggers.xml +++ b/tests/system/settings/windows/QtProject/qtcreator/debuggers.xml @@ -4,23 +4,6 @@ DebuggerItem.0 - - - x86-windows-msvc2015-pe-SQUISH_DEBUGGER_BITNESSbit - - true - - C:/Program Files (x86)/Windows Kits/10/Debuggers/SQUISH_DEBUGGER_ARCHITECTURE/cdb.exe - Auto-detected CDB at C:\Program Files (x86)\Windows Kits\10\Debuggers\SQUISH_DEBUGGER_ARCHITECTURE\cdb.exe - 4 - {1b25f20a-d584-4fb7-85b3-74dd15b82f6f} - - - - - - - DebuggerItem.1 x86-windows-msys-pe-unknown @@ -36,7 +19,7 @@ DebuggerItem.Count - 2 + 1 Version diff --git a/tests/system/settings/windows/QtProject/qtcreator/profiles.xml b/tests/system/settings/windows/QtProject/qtcreator/profiles.xml index 1a46caa099c..ecb22a0459b 100644 --- a/tests/system/settings/windows/QtProject/qtcreator/profiles.xml +++ b/tests/system/settings/windows/QtProject/qtcreator/profiles.xml @@ -37,7 +37,6 @@ - {1b25f20a-d584-4fb7-85b3-74dd15b82f6f} Desktop Device Desktop @@ -65,7 +64,6 @@ - {1b25f20a-d584-4fb7-85b3-74dd15b82f6f} Desktop Device Desktop @@ -94,7 +92,6 @@ false - {1b25f20a-d584-4fb7-85b3-74dd15b82f6f} Desktop Device Desktop diff --git a/tests/system/shared/qtcreator.py b/tests/system/shared/qtcreator.py index f1d4078fb52..12d6fc364b8 100644 --- a/tests/system/shared/qtcreator.py +++ b/tests/system/shared/qtcreator.py @@ -201,31 +201,6 @@ def substituteDefaultCompiler(settingsDir): __substitute__(qtversion, "SQUISH_DEFAULT_COMPILER", compiler) test.log("Injected default compiler '%s' to qtversion.xml..." % compiler) -def substituteCdb(settingsDir): - def canUse64bitCdb(): - try: - serverIni = readFile(os.path.join(os.getenv("APPDATA"), "froglogic", - "Squish", "ver1", "server.ini")) - autLine = next(iter(filter(lambda line: "AUT/qtcreator" in line, - serverIni.splitlines()))) - autPath = autLine.split("\"")[1] - return os.path.exists(os.path.join(autPath, "..", "lib", "qtcreatorcdbext64")) - except: - test.fatal("Something went wrong when determining debugger bitness", - "Did Squish's file structure change? Guessing 32-bit cdb can be used...") - return True - - if canUse64bitCdb(): - architecture = "x64" - bitness = "64" - else: - architecture = "x86" - bitness = "32" - debuggers = os.path.join(settingsDir, "QtProject", 'qtcreator', 'debuggers.xml') - __substitute__(debuggers, "SQUISH_DEBUGGER_ARCHITECTURE", architecture) - __substitute__(debuggers, "SQUISH_DEBUGGER_BITNESS", bitness) - test.log("Injected architecture '%s' and bitness '%s' in cdb path..." % (architecture, bitness)) - def substituteMsvcPaths(settingsDir, version, targetBitness=64): if not version in ['2017', '2019']: @@ -298,7 +273,6 @@ def copySettingsToTmpDir(destination=None, omitFiles=[]): substituteTildeWithinQtVersion(tmpSettingsDir) substituteDefaultCompiler(tmpSettingsDir) elif platform.system() in ('Windows', 'Microsoft'): - substituteCdb(tmpSettingsDir) substituteMsvcPaths(tmpSettingsDir, '2017', 64) substituteMsvcPaths(tmpSettingsDir, '2017', 32) substituteMsvcPaths(tmpSettingsDir, '2019', 64) From 93d008551756abd34821f1d58cb9ee40949f5c10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20L=C3=B6hning?= Date: Fri, 19 Apr 2024 20:53:09 +0200 Subject: [PATCH 196/202] SquishTests: Avoid using occurrence values for OutputPaneToggleButtons The occurrence count can't be understood without looking at the running application and can even change at runtime. Change-Id: I34677fd2fe2a0a8a7f2cdcd9f37ec938efe1a5ea Reviewed-by: Reviewed-by: Christian Stenger --- tests/system/objects.map | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/system/objects.map b/tests/system/objects.map index 66681c6b056..d9379812ab3 100644 --- a/tests/system/objects.map +++ b/tests/system/objects.map @@ -141,11 +141,11 @@ :Qt Creator.WelcomeScreenStackedWidget {name='WelcomeScreenStackedWidget' type='QStackedWidget' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator.replaceEdit_Utils::FilterLineEdit {name='replaceEdit' type='Utils::FancyLineEdit' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator.splitter_QSplitter {name='splitter' type='QSplitter' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} -:Qt Creator_AppOutput_Core::Internal::OutputPaneToggleButton {occurrence='3' type='Core::Internal::OutputPaneToggleButton' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} +:Qt Creator_AppOutput_Core::Internal::OutputPaneToggleButton {toolTip?='*Application Output*' type='Core::Internal::OutputPaneToggleButton' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator_BinEditor::BinEditorWidget {type='BinEditor::Internal::BinEditorWidget' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator_Bookmarks_TreeView {type='TreeView' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator_CloseButton {type='CloseButton' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} -:Qt Creator_CompileOutput_Core::Internal::OutputPaneToggleButton {occurrence='4' type='Core::Internal::OutputPaneToggleButton' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} +:Qt Creator_CompileOutput_Core::Internal::OutputPaneToggleButton {toolTip?='*Compile Output*' type='Core::Internal::OutputPaneToggleButton' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator_Core::Internal::CommandComboBox {type='Core::Internal::CommandComboBox' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator_Core::Internal::MainWindow {type='Utils::AppMainWindow' visible='1' windowTitle?='*Qt Creator'} :Qt Creator_Core::Internal::NavComboBox {type='Core::Internal::NavComboBox' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} @@ -159,11 +159,11 @@ :Qt Creator_Find::Internal::SearchResultTreeView {type='Core::Internal::SearchResultTreeView' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator_Git::Internal::GitEditor {type='Git::Internal::GitEditorWidget' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator_HelpSelector_QComboBox {occurrence='3' type='QComboBox' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} -:Qt Creator_Issues_Core::Internal::OutputPaneToggleButton {occurrence='1' type='Core::Internal::OutputPaneToggleButton' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} +:Qt Creator_Issues_Core::Internal::OutputPaneToggleButton {toolTip?='*Issues*' type='Core::Internal::OutputPaneToggleButton' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator_QHelpContentWidget {name='helpContentWidget' type='QTreeView' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator_QmlJSEditor::Internal::QmlJSOutlineTreeView {type='QmlJSEditor::Internal::QmlJSOutlineTreeView' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator_QmlJSEditor::QmlJSTextEditorWidget {type='QmlJSEditor::QmlJSEditorWidget' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} -:Qt Creator_SearchResult_Core::Internal::OutputPaneToggleButton {occurrence='2' type='Core::Internal::OutputPaneToggleButton' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} +:Qt Creator_SearchResult_Core::Internal::OutputPaneToggleButton {toolTip?='*Search Results*' type='Core::Internal::OutputPaneToggleButton' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator_TextEditor::TextEditorWidget {type='TextEditor::TextEditorWidget' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator_Utils::BuildDirectoryLineEdit {name='LineEdit' type='Utils::FancyLineEdit' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator_Utils::NavigationTreeView {name='projectTreeView' type='QTreeView' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} From 457488a1065889c238e3f3d747de9eede67dbe93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20L=C3=B6hning?= Date: Mon, 22 Apr 2024 12:15:33 +0200 Subject: [PATCH 197/202] SquishTests: Avoid using occurrence value for IconButton Change-Id: I0a4d9497801dbd39c2c735cac760c5dc6a0298fd Reviewed-by: Reviewed-by: Christian Stenger --- tests/system/objects.map | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/system/objects.map b/tests/system/objects.map index d9379812ab3..dd5db34925c 100644 --- a/tests/system/objects.map +++ b/tests/system/objects.map @@ -12,7 +12,7 @@ :*Qt Creator.findEdit_Utils::FilterLineEdit {name='findEdit' type='Utils::FancyLineEdit' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :*Qt Creator_Core::Internal::FancyToolButton {name='KitSelector.Button' type='Core::Internal::FancyToolButton' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :*Qt Creator_Utils::FilterLineEdit {type='Utils::FancyLineEdit' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} -:*Qt Creator_Utils::IconButton {occurrence='5' type='Utils::FancyIconButton' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} +:*Qt Creator_Utils::IconButton {toolTip='Clear text' type='Utils::FancyIconButton' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :About Qt Creator_Core::Internal::VersionDialog {type='Core::Internal::VersionDialog' unnamed='1' visible='1' windowTitle='About Qt Creator'} :Activate completion:_QComboBox {buddy=':Behavior.Activate completion:_QLabel' type='QComboBox' unnamed='1' visible='1'} :Add Bookmark.ExpandBookmarksList_QToolButton {text='+' type='QToolButton' unnamed='1' visible='1' window=':Add Bookmark_BookmarkDialog'} From ec33fc0476413e7f79fae24834755374b6b7bacf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20L=C3=B6hning?= Date: Mon, 22 Apr 2024 13:00:52 +0200 Subject: [PATCH 198/202] SquishTests: Avoid using occurrence value for SideDiffEditorWidgets Change-Id: I7c519347a25b0c541b019dd1cae65df4a74c8826 Reviewed-by: Christian Stenger Reviewed-by: --- tests/system/objects.map | 4 ++-- tests/system/suite_tools/tst_git_first_commit/test.py | 2 +- tests/system/suite_tools/tst_git_local/test.py | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/system/objects.map b/tests/system/objects.map index dd5db34925c..bd8c30bce2d 100644 --- a/tests/system/objects.map +++ b/tests/system/objects.map @@ -153,8 +153,8 @@ :Qt Creator_Core::OutputWindow {type='Core::OutputWindow' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator_CppEditor::Internal::CPPEditorWidget {type='CppEditor::CppEditorWidget' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator_DiffEditor::Internal::DescriptionEditorWidget {type='DiffEditor::Internal::DescriptionEditorWidget' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} -:Qt Creator_DiffEditor::SideDiffEditorWidget {type='DiffEditor::Internal::SideDiffEditorWidget' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} -:Qt Creator_DiffEditor::SideDiffEditorWidget2 {occurrence='2' type='DiffEditor::Internal::SideDiffEditorWidget' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} +:Qt Creator_DiffEditor::SideDiffEditorWidgetChanged {type='DiffEditor::Internal::SideDiffEditorWidget' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow' x~='[1-9][0-9]*'} +:Qt Creator_DiffEditor::SideDiffEditorWidgetOriginal {type='DiffEditor::Internal::SideDiffEditorWidget' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow' x='0'} :Qt Creator_FilenameQComboBox {leftWidget=':Qt Creator.DragDoc_QToolButton' type='QComboBox' unnamed='1' visible='1'} :Qt Creator_Find::Internal::SearchResultTreeView {type='Core::Internal::SearchResultTreeView' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator_Git::Internal::GitEditor {type='Git::Internal::GitEditorWidget' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} diff --git a/tests/system/suite_tools/tst_git_first_commit/test.py b/tests/system/suite_tools/tst_git_first_commit/test.py index 1ef7090602b..0ee8f2505bf 100644 --- a/tests/system/suite_tools/tst_git_first_commit/test.py +++ b/tests/system/suite_tools/tst_git_first_commit/test.py @@ -35,7 +35,7 @@ def main(): test.verify(" files changed, 229938 insertions(+)" in commitDetails, "Summary in details view?") clickButton(waitForObject(":Select a Git Commit.Show_QPushButton")) - changedEdit = waitForObject(":Qt Creator_DiffEditor::SideDiffEditorWidget") + changedEdit = waitForObject(":Qt Creator_DiffEditor::SideDiffEditorWidgetChanged") waitFor("len(str(changedEdit.plainText)) > 0 and " "str(changedEdit.plainText) != 'Waiting for data...'", 40000) diffPlainText = str(changedEdit.plainText) diff --git a/tests/system/suite_tools/tst_git_local/test.py b/tests/system/suite_tools/tst_git_local/test.py index c74ad6e20cf..1c9cd01712a 100644 --- a/tests/system/suite_tools/tst_git_local/test.py +++ b/tests/system/suite_tools/tst_git_local/test.py @@ -105,8 +105,8 @@ def verifyClickCommit(): for i in range(1, 3): if not __clickCommit__(i): continue - changed = waitForObject(":Qt Creator_DiffEditor::SideDiffEditorWidget") - original = waitForObject(":Qt Creator_DiffEditor::SideDiffEditorWidget2") + changed = waitForObject(":Qt Creator_DiffEditor::SideDiffEditorWidgetChanged") + original = waitForObject(":Qt Creator_DiffEditor::SideDiffEditorWidgetOriginal") waitFor('str(changed.plainText) != "Waiting for data..." ' 'and str(original.plainText) != "Waiting for data..."', 5000) # content of diff editors is merge of modified files @@ -202,7 +202,7 @@ def main(): type(gitEditor, "") rect = gitEditor.cursorRect(gitEditor.textCursor()) mouseClick(gitEditor, rect.x+rect.width/2, rect.y+rect.height/2, 0, Qt.LeftButton) - changed = waitForObject(":Qt Creator_DiffEditor::SideDiffEditorWidget") + changed = waitForObject(":Qt Creator_DiffEditor::SideDiffEditorWidgetChanged") waitFor('str(changed.plainText) != "Waiting for data..."', 5000) test.compare(str(changed.plainText), "Retrieving data failed.", "Showing an invalid commit can't succeed but Creator survived.") From a369d075ad6f042823e5a3742162cd3858cc055a Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Sat, 20 Apr 2024 00:25:43 +0300 Subject: [PATCH 199/202] QmlDesigner: Implement adding an image to the content library Also some cleanups in same files. Fixes: QDS-12506 Change-Id: I0c211206b6b7c29857a30f18d6077c2ddd76849c Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- .../assetsLibraryQmlSources/Assets.qml | 6 ++-- .../AssetsContextMenu.qml | 31 +++++++++------- .../assetsLibraryQmlSources/AssetsView.qml | 8 ++--- .../assetslibraryiconprovider.cpp | 3 +- .../assetslibrary/assetslibraryiconprovider.h | 3 +- .../assetslibrary/assetslibrarymodel.cpp | 36 +++++++++---------- .../assetslibrary/assetslibrarymodel.h | 25 ++++++------- .../assetslibrary/assetslibrarywidget.cpp | 33 +++++++++++------ .../assetslibrary/assetslibrarywidget.h | 2 ++ .../contentlibraryusermodel.cpp | 29 +++++++++++++-- .../contentlibrary/contentlibraryusermodel.h | 1 + .../contentlibrary/contentlibraryview.cpp | 29 +++++++++++++++ .../contentlibrary/contentlibraryview.h | 1 + src/plugins/qmldesigner/utils/asset.cpp | 16 +++++++++ src/plugins/qmldesigner/utils/asset.h | 4 +++ 15 files changed, 160 insertions(+), 67 deletions(-) diff --git a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/Assets.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/Assets.qml index cfa40209950..26bef6f8e91 100644 --- a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/Assets.qml +++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/Assets.qml @@ -94,7 +94,7 @@ Item { anchors.fill: parent acceptedButtons: Qt.RightButton onClicked: { - if (assetsModel.haveFiles) { + if (assetsModel.hasFiles) { function onFolderCreated(path) { assetsView.addCreatedFolder(path) } @@ -182,13 +182,13 @@ Item { leftPadding: 10 color: StudioTheme.Values.themeTextColor font.pixelSize: StudioTheme.Values.baseFont - visible: !assetsModel.haveFiles && !root.__searchBoxEmpty + visible: !assetsModel.hasFiles && !root.__searchBoxEmpty } Item { // placeholder when the assets library is empty width: parent.width height: parent.height - toolbar.height - column.spacing - visible: !assetsModel.haveFiles && root.__searchBoxEmpty + visible: !assetsModel.hasFiles && root.__searchBoxEmpty clip: true MouseArea { // right clicking the empty area of the view diff --git a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsContextMenu.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsContextMenu.qml index 391c6220481..a8eb5285f57 100644 --- a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsContextMenu.qml +++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsContextMenu.qml @@ -83,7 +83,7 @@ StudioControls.Menu { root.__selectedAssetPathsList = selectedAssetPathsList root.__fileIndex = fileIndex root.__dirIndex = dirModelIndex - root.__dirPath = AssetsLibraryBackend.assetsModel.filePath(dirModelIndex) + root.__dirPath = root.assetsModel.filePath(dirModelIndex) root.__isDirectory = false root.popup() } @@ -124,9 +124,9 @@ StudioControls.Menu { id: addTexturesItem text: qsTr("Add Texture") enabled: rootView.hasMaterialLibrary - visible: root.__fileIndex && AssetsLibraryBackend.assetsModel.allFilePathsAreTextures(root.__selectedAssetPathsList) + visible: root.__fileIndex && root.assetsModel.allFilePathsAreTextures(root.__selectedAssetPathsList) height: addTexturesItem.visible ? addTexturesItem.implicitHeight : 0 - onTriggered: AssetsLibraryBackend.rootView.addTextures(root.__selectedAssetPathsList) + onTriggered: root.rootView.addTextures(root.__selectedAssetPathsList) } StudioControls.MenuItem { @@ -134,7 +134,7 @@ StudioControls.Menu { text: qsTr("Add Light Probe") enabled: rootView.hasMaterialLibrary && rootView.hasSceneEnv visible: root.__fileIndex && root.__selectedAssetPathsList.length === 1 - && AssetsLibraryBackend.assetsModel.allFilePathsAreTextures(root.__selectedAssetPathsList) + && root.assetsModel.allFilePathsAreTextures(root.__selectedAssetPathsList) height: addLightProbes.visible ? addLightProbes.implicitHeight : 0 onTriggered: rootView.addLightProbe(root.__selectedAssetPathsList[0]) } @@ -145,7 +145,7 @@ StudioControls.Menu { visible: root.__fileIndex height: deleteFileItem.visible ? deleteFileItem.implicitHeight : 0 onTriggered: { - let deleted = AssetsLibraryBackend.assetsModel.requestDeleteFiles(root.__selectedAssetPathsList) + let deleted = root.assetsModel.requestDeleteFiles(root.__selectedAssetPathsList) if (!deleted) confirmDeleteFiles.open() } @@ -182,7 +182,7 @@ StudioControls.Menu { StudioControls.MenuItem { text: qsTr("New Folder") - visible: AssetsLibraryBackend.assetsModel.haveFiles + visible: root.assetsModel.hasFiles height: visible ? implicitHeight : 0 NewFolderDialog { @@ -209,11 +209,11 @@ StudioControls.Menu { } onTriggered: { - if (!AssetsLibraryBackend.assetsModel.hasChildren(root.__dirIndex)) { + if (!root.assetsModel.hasChildren(root.__dirIndex)) { // NOTE: the folder may still not be empty -- it doesn't have files visible to the // user, but that doesn't mean that there are no other files (e.g. files of unknown // types) on disk in this directory. - AssetsLibraryBackend.assetsModel.deleteFolderRecursively(root.__dirIndex) + root.assetsModel.deleteFolderRecursively(root.__dirIndex) } else { confirmDeleteFolderDialog.open() } @@ -222,7 +222,7 @@ StudioControls.Menu { StudioControls.MenuItem { text: qsTr("New Effect") - visible: rootView.canCreateEffects() + visible: root.rootView.canCreateEffects() height: visible ? implicitHeight : 0 NewEffectDialog { @@ -235,15 +235,22 @@ StudioControls.Menu { } StudioControls.MenuItem { - text: rootView.showInGraphicalShellMsg() + text: root.rootView.showInGraphicalShellMsg() enabled: root.__showInGraphicalShellEnabled onTriggered: { if (!root.__fileIndex || root.__selectedAssetPathsList.length > 1) - rootView.showInGraphicalShell(root.__dirPath) + root.rootView.showInGraphicalShell(root.__dirPath) else - rootView.showInGraphicalShell(root.__selectedAssetPathsList[0]) + root.rootView.showInGraphicalShell(root.__selectedAssetPathsList[0]) } } + + StudioControls.MenuItem { + text: qsTr("Add to Content Library") + visible: root.rootView.userBundleEnabled() && root.__fileIndex && root.assetsModel.allFilePathsAreTextures(root.__selectedAssetPathsList) + height: visible ? implicitHeight : 0 + onTriggered: root.rootView.addAssetsToContentLibrary(root.__selectedAssetPathsList) + } } diff --git a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsView.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsView.qml index 9326e6a5e35..aeabc92c6d4 100644 --- a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsView.qml +++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsView.qml @@ -70,9 +70,9 @@ TreeView { model: assetsModel onRowsChanged: { - if (root.rows > root.rootPathRow + 1 && !assetsModel.haveFiles || - root.rows <= root.rootPathRow + 1 && assetsModel.haveFiles) { - assetsModel.syncHaveFiles() + if (root.rows > root.rootPathRow + 1 && !assetsModel.hasFiles || + root.rows <= root.rootPathRow + 1 && assetsModel.hasFiles) { + assetsModel.syncHasFiles() } root.updateRows() @@ -366,7 +366,7 @@ TreeView { function moveSelection(amount) { - if (!assetsModel.haveFiles || !amount) + if (!assetsModel.hasFiles || !amount) return let index = root.currentFilePath ? assetsModel.indexForPath(root.currentFilePath) diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp index dc5a1c97411..b821cc65957 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp @@ -2,9 +2,8 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "assetslibraryiconprovider.h" -#include "asset.h" -#include "modelnodeoperations.h" +#include #include #include #include diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.h index fb38605ea61..d52779232f0 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.h +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.h @@ -3,12 +3,11 @@ #pragma once +#include #include #include -#include "asset.h" - namespace QmlDesigner { struct Thumbnail diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp index c2359409eb5..9d09f52d8ff 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp @@ -1,21 +1,19 @@ // Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include -#include -#include -#include -#include - -#include "asset.h" #include "assetslibrarymodel.h" +#include #include #include #include #include -#include +#include + +#include +#include +#include namespace QmlDesigner { @@ -38,7 +36,7 @@ void AssetsLibraryModel::createBackendModel() QObject::connect(m_sourceFsModel, &QFileSystemModel::directoryLoaded, this, [this]([[maybe_unused]] const QString &dir) { - syncHaveFiles(); + syncHasFiles(); }); m_fileWatcher = new Utils::FileSystemWatcher(parent()); @@ -207,7 +205,7 @@ bool AssetsLibraryModel::filterAcceptsRow(int sourceRow, const QModelIndex &sour } } -bool AssetsLibraryModel::checkHaveFiles(const QModelIndex &parentIdx) const +bool AssetsLibraryModel::checkHasFiles(const QModelIndex &parentIdx) const { if (!parentIdx.isValid()) return false; @@ -218,30 +216,30 @@ bool AssetsLibraryModel::checkHaveFiles(const QModelIndex &parentIdx) const if (!isDirectory(newIdx)) return true; - if (checkHaveFiles(newIdx)) + if (checkHasFiles(newIdx)) return true; } return false; } -void AssetsLibraryModel::setHaveFiles(bool value) +void AssetsLibraryModel::setHasFiles(bool value) { - if (m_haveFiles != value) { - m_haveFiles = value; - emit haveFilesChanged(); + if (m_hasFiles != value) { + m_hasFiles = value; + emit hasFilesChanged(); } } -bool AssetsLibraryModel::checkHaveFiles() const +bool AssetsLibraryModel::checkHasFiles() const { auto rootIdx = indexForPath(m_rootPath); - return checkHaveFiles(rootIdx); + return checkHasFiles(rootIdx); } -void AssetsLibraryModel::syncHaveFiles() +void AssetsLibraryModel::syncHasFiles() { - setHaveFiles(checkHaveFiles()); + setHasFiles(checkHasFiles()); } QString AssetsLibraryModel::getUniqueName(const QString &oldName) { diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h index 9334e86e9b2..2516be787fc 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h @@ -3,12 +3,13 @@ #pragma once -#include -#include #include -#include -#include +namespace Utils { +class FileSystemWatcher; +} + +QT_FORWARD_DECLARE_CLASS(QFileSystemModel) namespace QmlDesigner { @@ -22,7 +23,7 @@ public: void setRootPath(const QString &newPath); void setSearchText(const QString &searchText); - Q_PROPERTY(bool haveFiles READ haveFiles NOTIFY haveFilesChanged); + Q_PROPERTY(bool hasFiles READ hasFiles NOTIFY hasFilesChanged) Q_INVOKABLE QString rootPath() const; Q_INVOKABLE QString filePath(const QModelIndex &index) const; @@ -35,7 +36,7 @@ public: Q_INVOKABLE QModelIndex parentDirIndex(const QString &path) const; Q_INVOKABLE QModelIndex parentDirIndex(const QModelIndex &index) const; Q_INVOKABLE QString parentDirPath(const QString &path) const; - Q_INVOKABLE void syncHaveFiles(); + Q_INVOKABLE void syncHasFiles(); Q_INVOKABLE QList parentIndices(const QModelIndex &index) const; Q_INVOKABLE bool indexIsValid(const QModelIndex &index) const; @@ -55,30 +56,30 @@ public: return std::min(result, 1); } - bool haveFiles() const { return m_haveFiles; } + bool hasFiles() const { return m_hasFiles; } QString getUniqueName(const QString &oldName); signals: void directoryLoaded(const QString &path); void rootPathChanged(); - void haveFilesChanged(); + void hasFilesChanged(); void fileChanged(const QString &path); void effectsDeleted(const QStringList &effectNames); private: - void setHaveFiles(bool value); + void setHasFiles(bool value); bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; void resetModel(); void createBackendModel(); void destroyBackendModel(); - bool checkHaveFiles(const QModelIndex &parentIdx) const; - bool checkHaveFiles() const; + bool checkHasFiles(const QModelIndex &parentIdx) const; + bool checkHasFiles() const; QString m_searchText; QString m_rootPath; QFileSystemModel *m_sourceFsModel = nullptr; - bool m_haveFiles = false; + bool m_hasFiles = false; Utils::FileSystemWatcher *m_fileWatcher = nullptr; }; diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp index bcaa5351fd0..4b270c8902d 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp @@ -3,20 +3,22 @@ #include "assetslibrarywidget.h" -#include "asset.h" #include "assetslibraryiconprovider.h" #include "assetslibrarymodel.h" #include "assetslibraryview.h" -#include "designeractionmanager.h" -#include "import.h" -#include "modelnodeoperations.h" -#include "nodemetainfo.h" -#include "qmldesignerconstants.h" -#include "qmldesignerplugin.h" -#include "theme.h" -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include #include #include @@ -376,7 +378,7 @@ QList AssetsLibraryWidget::createToolBarWidgets() void AssetsLibraryWidget::handleSearchFilterChanged(const QString &filterText) { - if (filterText == m_filterText || (!m_assetsModel->haveFiles() + if (filterText == m_filterText || (!m_assetsModel->hasFiles() && filterText.contains(m_filterText, Qt::CaseInsensitive))) return; @@ -645,4 +647,15 @@ void AssetsLibraryWidget::addResources(const QStringList &files, bool showDialog } } +bool AssetsLibraryWidget::userBundleEnabled() const +{ + // TODO: this method is to be removed after user bundle implementation is complete + return Core::ICore::settings()->value("QML/Designer/UseExperimentalFeatures45", false).toBool(); +} + +void AssetsLibraryWidget::addAssetsToContentLibrary(const QStringList &assetPaths) +{ + m_assetsView->emitCustomNotification("add_assets_to_content_lib", {}, {assetPaths}); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h index ed987d14deb..8b59ae07859 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h @@ -98,6 +98,8 @@ public: Q_INVOKABLE void showInGraphicalShell(const QString &path); Q_INVOKABLE QString showInGraphicalShellMsg() const; + Q_INVOKABLE bool userBundleEnabled() const; + Q_INVOKABLE void addAssetsToContentLibrary(const QStringList &assetPaths); signals: void itemActivated(const QString &itemName); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp index 243e0d96622..18d6e45fa8b 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp @@ -94,7 +94,30 @@ void ContentLibraryUserModel::addMaterial(const QString &name, const QString &qm m_userMaterials.append(libMat); int matSectionIdx = 0; - emit dataChanged(index(matSectionIdx, 0), index(matSectionIdx, 0)); + emit dataChanged(index(matSectionIdx), index(matSectionIdx)); +} + +void ContentLibraryUserModel::addTextures(const QStringList &paths) +{ + QDir bundleDir{Paths::bundlesPathSetting() + "/User/textures"}; + bundleDir.mkpath("."); + bundleDir.mkdir("icons"); + + for (const QString &path : paths) { + QFileInfo fileInfo(path); + QString suffix = '.' + fileInfo.suffix(); + auto iconFileInfo = QFileInfo(fileInfo.path().append("/icons/").append(fileInfo.baseName() + ".png")); + QPair info = ImageUtils::imageInfo(path); + QString dirPath = fileInfo.path(); + QSize imgDims = info.first; + qint64 imgFileSize = info.second; + + auto tex = new ContentLibraryTexture(this, iconFileInfo, dirPath, suffix, imgDims, imgFileSize); + m_userTextures.append(tex); + } + + int texSectionIdx = 1; + emit dataChanged(index(texSectionIdx), index(texSectionIdx)); } // returns unique library material's name and qml component @@ -273,10 +296,10 @@ void ContentLibraryUserModel::loadTextureBundle() const QFileInfoList fileInfos = bundleDir.entryInfoList(QDir::Files); for (const QFileInfo &fileInfo : fileInfos) { - auto iconFileInfo = QFileInfo(fileInfo.path().append("/icons/").append(fileInfo.fileName())); + QString suffix = '.' + fileInfo.suffix(); + auto iconFileInfo = QFileInfo(fileInfo.path().append("/icons/").append(fileInfo.baseName() + ".png")); QPair info = ImageUtils::imageInfo(fileInfo.path()); QString dirPath = fileInfo.path(); - QString suffix = '.' + fileInfo.suffix(); QSize imgDims = info.first; qint64 imgFileSize = info.second; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h index 26edbf5ec33..3e9a96fd9d7 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h @@ -62,6 +62,7 @@ public: void updateIsEmpty(); void addMaterial(const QString &name, const QString &qml, const QUrl &icon, const QStringList &files); + void addTextures(const QStringList &paths); void setBundleObj(const QJsonObject &newBundleObj); QJsonObject &bundleJsonObjectRef(); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp index 91e402116a4..dd8a4d9919e 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp @@ -397,6 +397,8 @@ void ContentLibraryView::customNotification(const AbstractView *view, QTC_ASSERT(nodeList.size() == 1 && data.size() == 1, return); addLibMaterial(nodeList.first(), data.first().value()); + } else if (identifier == "add_assets_to_content_lib") { + addLibAssets(data.first().toStringList()); } } @@ -655,6 +657,33 @@ QPair> ContentLibraryView::modelNodeToQmlString(const Mod return {qml, assets}; } +void ContentLibraryView::addLibAssets(const QStringList &paths) +{ + auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/textures"); + QStringList pathsInBundle; + + for (const QString &path : paths) { + Asset asset(path); + auto assetPath = Utils::FilePath::fromString(path); + + // save icon + QString iconSavePath = bundlePath.pathAppended("icons/" + assetPath.baseName() + ".png").toString(); + QPixmap icon = asset.pixmap({120, 120}); + bool iconSaved = icon.save(iconSavePath); + if (!iconSaved) + qWarning() << __FUNCTION__ << "icon save failed"; + + // save asset + auto result = assetPath.copyFile(bundlePath.pathAppended(asset.fileName())); + if (!result) + qWarning() << __FUNCTION__ << result.error(); + + pathsInBundle.append(bundlePath.pathAppended(asset.fileName()).toString()); + } + + m_widget->userModel()->addTextures(pathsInBundle); +} + ModelNode ContentLibraryView::getBundleMaterialDefaultInstance(const TypeName &type) { ModelNode matLib = Utils3D::materialLibraryNode(this); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h index d6ce50ed0eb..03d42fa8bcd 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h @@ -55,6 +55,7 @@ private: void updateBundleEffectsImportedState(); void updateBundlesQuick3DVersion(); void addLibMaterial(const ModelNode &mat, const QPixmap &icon); + void addLibAssets(const QStringList &paths); QStringList writeLibMaterialQml(const ModelNode &mat, const QString &qml); QPair> modelNodeToQmlString(const ModelNode &node, QStringList &depListIds, int depth = 0); diff --git a/src/plugins/qmldesigner/utils/asset.cpp b/src/plugins/qmldesigner/utils/asset.cpp index f6753dc0042..ec1e4312e41 100644 --- a/src/plugins/qmldesigner/utils/asset.cpp +++ b/src/plugins/qmldesigner/utils/asset.cpp @@ -3,7 +3,10 @@ #include "asset.h" +#include "hdrimage.h" + #include +#include namespace QmlDesigner { @@ -106,6 +109,19 @@ bool Asset::isSupported(const QString &path) return supportedSuffixes().contains(path); } +QPixmap Asset::pixmap(const QSize &size) const +{ + if (!isImage() && !isHdrFile()) + return {}; + + QPixmap icon = isHdrFile() ? HdrImage{m_filePath}.toPixmap() : QPixmap{m_filePath}; + + if (size.isValid()) + icon = icon.scaled(size, Qt::KeepAspectRatio); + + return icon; +} + Asset::Type Asset::type() const { return m_type; diff --git a/src/plugins/qmldesigner/utils/asset.h b/src/plugins/qmldesigner/utils/asset.h index 1b548a1c738..a5c5899f34f 100644 --- a/src/plugins/qmldesigner/utils/asset.h +++ b/src/plugins/qmldesigner/utils/asset.h @@ -3,8 +3,11 @@ #pragma once +#include #include +QT_FORWARD_DECLARE_CLASS(QPixmap) + namespace QmlDesigner { class Asset @@ -39,6 +42,7 @@ public: const QString id() const; const QString fileName() const; bool hasSuffix() const; + QPixmap pixmap(const QSize &size = {}) const; Type type() const; bool isImage() const; From facd32b2e07b3d2a13f26b99ed08ccf1eb174397 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Tue, 23 Apr 2024 08:54:37 +0200 Subject: [PATCH 200/202] SquishTests: Remove duplicate object MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I7ceed8ef40684323b8404c3fe7c529fbd5b7be74 Reviewed-by: Robert Löhning --- tests/system/objects.map | 4 +--- tests/system/shared/project.py | 2 +- tests/system/suite_debugger/tst_debug_empty_main/test.py | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/tests/system/objects.map b/tests/system/objects.map index bd8c30bce2d..6ad38f58318 100644 --- a/tests/system/objects.map +++ b/tests/system/objects.map @@ -92,7 +92,6 @@ :JsonWizard_ProjectExplorer::JsonFieldPage {type='ProjectExplorer::JsonFieldPage' unnamed='1' visible='1' window=':New_ProjectExplorer::JsonWizard'} :Kits_QtVersion_QComboBox {container=':qt_tabwidget_stackedwidget_QWidget' leftWidget=':QtVersionLabel_KitPage' type='QComboBox' unnamed='1' visible='1'} :Locals and Expressions_Debugger::Internal::WatchTreeView {container=':Debugger.Docks.LocalsAndWatchersDockWidget.Inspector_QFrame' name='WatchWindow' type='Debugger::Internal::WatchTreeView' visible='1'} -:New Text File.Add to project:_QLabel {name='projectLabel' text='Add to project:' type='QLabel' visible='1' window=':New_ProjectExplorer::JsonWizard'} :New Text File.nameLineEdit_Utils::FileNameValidatingLineEdit {name='nameLineEdit' type='Utils::FileNameValidatingLineEdit' visible='1' window=':New_ProjectExplorer::JsonWizard'} :New.comboBox_QComboBox {type='QComboBox' unnamed='1' visible='1' window=':New_Core::Internal::NewDialog'} :New.templateCategoryView_QTreeView {name='templateCategoryView' type='QTreeView' visible='1' window=':New_Core::Internal::NewDialog'} @@ -205,8 +204,7 @@ :headerFileLineEdit_Utils::FileNameValidatingLineEdit {name='HdrFileName' type='Utils::FancyLineEdit' visible='1' window=':New_ProjectExplorer::JsonWizard'} :popupFrame_Proposal_QListView {container=':popupFrame_TextEditor::GenericProposalWidget' type='QListView' unnamed='1' visible='1'} :popupFrame_TextEditor::GenericProposalWidget {name='m_popupFrame' type='TextEditor::GenericProposalWidget' visible='1'} -:projectComboBox_QComboBox {buddy=':New Text File.Add to project:_QLabel' name='projectComboBox' type='QComboBox' visible='1'} -:projectComboBox_Utils::TreeViewComboBox {buddy=':New Text File.Add to project:_QLabel' name='projectComboBox' type='QComboBox' visible='1'} +:projectComboBox_QComboBox {name='projectComboBox' type='QComboBox' visible='1'} :qdesigner_internal::WidgetBoxCategoryListView {container=':Widget Box_qdesigner_internal::WidgetBoxTreeWidget' occurrence='3' type='qdesigner_internal::WidgetBoxCategoryListView' unnamed='1' visible='1'} :qt_tabwidget_stackedwidget.QtSupport__Internal__QtVersionManager_QtSupport::Internal::QtOptionsPageWidget {container=':Options.qt_tabwidget_stackedwidget_QStackedWidget' type='QScrollArea' unnamed='1' visible='1'} :qt_tabwidget_stackedwidget_QScrollArea {container=':Options.qt_tabwidget_stackedwidget_QStackedWidget' type='QScrollArea' unnamed='1' visible='1'} diff --git a/tests/system/shared/project.py b/tests/system/shared/project.py index 2ff44dc3737..cdf5290d895 100644 --- a/tests/system/shared/project.py +++ b/tests/system/shared/project.py @@ -662,7 +662,7 @@ def addCPlusPlusFile(name, template, projectName, forceOverwrite=False, addToVCS test.compare(str(waitForObject("{name='HdrFileName' type='QLineEdit' visible='1'}").text), expectedHeaderName) clickButton(waitForObject(":Next_QPushButton")) - projectComboBox = waitForObjectExists(":projectComboBox_Utils::TreeViewComboBox") + projectComboBox = waitForObjectExists(":projectComboBox_QComboBox") test.compare(projectComboBox.enabled, projectName != None, "Project combo box must be enabled when a project is open") projectNameToDisplay = "" diff --git a/tests/system/suite_debugger/tst_debug_empty_main/test.py b/tests/system/suite_debugger/tst_debug_empty_main/test.py index 019b55e5f00..d62f76cdebf 100644 --- a/tests/system/suite_debugger/tst_debug_empty_main/test.py +++ b/tests/system/suite_debugger/tst_debug_empty_main/test.py @@ -14,8 +14,7 @@ def addFileToProject(projectPath, category, fileTemplate, fileName): projectPath, "Verifying whether path is correct."): replaceEditorContent(pathLineEdit, projectPath) clickButton(waitForObject(":Next_QPushButton")) - projCombo = findObject("{buddy={name='projectLabel' text='Add to project:' type='QLabel' " - "visible='1'} name='projectComboBox' type='QComboBox' visible='1'}") + projCombo = waitForObjectExists(":projectComboBox_QComboBox", 1000) proFileName = os.path.basename(projectPath) + ".pro" test.verify(not selectFromCombo(projCombo, proFileName), "Verifying project is selected.") __createProjectHandleLastPage__() From b07d2f0c1d8dd032145161d84b9aa3ec99103238 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 22 Apr 2024 17:59:59 +0200 Subject: [PATCH 201/202] Include Qt and GoogleTest as system include That is silencing warnings in Qt and GoogleTest headers . Change-Id: Ia6fda019c271788fbb341a8de0cf6d7b757f38bf Reviewed-by: Cristian Adam Reviewed-by: Qt CI Patch Build Bot --- cmake/QtCreatorAPIInternal.cmake | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/cmake/QtCreatorAPIInternal.cmake b/cmake/QtCreatorAPIInternal.cmake index 08167535e32..0cd3602d543 100644 --- a/cmake/QtCreatorAPIInternal.cmake +++ b/cmake/QtCreatorAPIInternal.cmake @@ -338,12 +338,18 @@ function(add_qtc_depends target_name) foreach(obj_lib IN LISTS object_lib_depends) target_compile_options(${target_name} PRIVATE $) target_compile_definitions(${target_name} PRIVATE $) - target_include_directories(${target_name} PRIVATE $) + if (obj_lib MATCHES "Qt::.*|GoogleTest") + set(system_include "SYSTEM") + endif() + target_include_directories(${target_name} ${system_include} PRIVATE $) endforeach() foreach(obj_lib IN LISTS object_public_depends) target_compile_options(${target_name} PUBLIC $) target_compile_definitions(${target_name} PUBLIC $) - target_include_directories(${target_name} PUBLIC $) + if (obj_lib MATCHES "Qt::.*|GoogleTest") + set(system_include "SYSTEM") + endif() + target_include_directories(${target_name} ${system_include} PUBLIC $) endforeach() endfunction() From 2c38f9ae74816fbe193f83766071ffd2fac184c5 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 23 Apr 2024 11:14:02 +0200 Subject: [PATCH 202/202] Fix max component version Change-Id: Ib7328c00a461f22e5c924a441844260f15fb0dd3 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- src/libs/languageutils/componentversion.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libs/languageutils/componentversion.h b/src/libs/languageutils/componentversion.h index 0ba332103ac..2b8311a6cb6 100644 --- a/src/libs/languageutils/componentversion.h +++ b/src/libs/languageutils/componentversion.h @@ -11,6 +11,8 @@ QT_END_NAMESPACE #include +#include + namespace LanguageUtils { class LANGUAGEUTILS_EXPORT ComponentVersion @@ -19,8 +21,8 @@ class LANGUAGEUTILS_EXPORT ComponentVersion int _minor; public: - static const int NoVersion = -1; - static const int MaxVersion = -1; + static constexpr int NoVersion = -1; + static constexpr int MaxVersion = std::numeric_limits::max(); ComponentVersion() : _major(NoVersion)
  • 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