diff options
Diffstat (limited to 'src/plugins/qmldesigner')
265 files changed, 14271 insertions, 11267 deletions
diff --git a/src/plugins/qmldesigner/.clang-format b/src/plugins/qmldesigner/.clang-format index d3695ac298..366f82f76f 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/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index b5b64bebbc..c29017523d 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("$<$<COMPILE_LANG_AND_ID:CXX,Clang,GNU>:-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}) @@ -28,6 +27,10 @@ env_with_default("QTC_ENABLE_MODEL_TRACING" ENV_QTC_ENABLE_MODEL_TRACING OFF) option(ENABLE_MODEL_TRACING "Enable model tracing" ${ENV_QTC_ENABLE_MODEL_TRACING}) add_feature_info("Model tracing" ${ENABLE_MODEL_TRACING} "") +env_with_default("QTC_ENABLE_METAINFO_TRACING" ENV_QTC_ENABLE_METAINFO_TRACING OFF) +option(ENABLE_METAINFO_TRACING "Enable meta info tracing" ${ENV_QTC_ENABLE_METAINFO_TRACING}) +add_feature_info("Meta info tracing" ${ENABLE_METAINFO_TRACING} "") + add_qtc_library(QmlDesignerUtils STATIC DEPENDS Qt::Gui Utils Qt::QmlPrivate @@ -46,6 +49,11 @@ add_qtc_library(QmlDesignerUtils STATIC qmldesignerutils_global.h ) +if (TARGET QmlDesignerUtils) + target_compile_options(QmlDesignerUtils PUBLIC $<$<COMPILE_LANG_AND_ID:CXX,Clang,GNU>:-Wno-error=maybe-uninitialized>) + target_compile_options(QmlDesignerUtils PUBLIC $<$<COMPILE_LANG_AND_ID:CXX,Clang>:-Wno-unneeded-internal-declaration>) +endif() + extend_qtc_library(QmlDesignerUtils CONDITION ENABLE_COMPILE_WARNING_AS_ERROR PROPERTIES COMPILE_WARNING_AS_ERROR ON @@ -89,12 +97,13 @@ add_qtc_library(QmlDesignerCore STATIC ${CMAKE_CURRENT_LIST_DIR}/designercore/include SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/designercore SOURCES - rewritertransaction.cpp - rewritertransaction.h + rewritertransaction.cpp rewritertransaction.h + generatedcomponentutils.cpp generatedcomponentutils.h + uniquename.cpp uniquename.h ) extend_qtc_library(QmlDesignerCore - CONDITION ENABLE_PROJECT_STORAGE_TRACING OR ENABLE_IMAGE_CACHE_TRACING OR ENABLE_MODEL_TRACING + CONDITION ENABLE_PROJECT_STORAGE_TRACING OR ENABLE_IMAGE_CACHE_TRACING OR ENABLE_MODEL_TRACING OR ENABLE_METAINFO_TRACING PUBLIC_DEPENDS Nanotrace PUBLIC_DEFINES ENABLE_QMLDESIGNER_TRACING @@ -102,6 +111,7 @@ extend_qtc_library(QmlDesignerCore $<$<BOOL:${ENABLE_PROJECT_STORAGE_TRACING}>:ENABLE_PROJECT_STORAGE_TRACING> $<$<BOOL:${ENABLE_IMAGE_CACHE_TRACING}>:ENABLE_IMAGE_CACHE_TRACING> $<$<BOOL:${ENABLE_MODEL_TRACING}>:ENABLE_MODEL_TRACING> + $<$<BOOL:${ENABLE_METAINFO_TRACING}>:ENABLE_METAINFO_TRACING> ) extend_qtc_library(QmlDesignerCore @@ -457,6 +467,8 @@ extend_qtc_library(QmlDesignerCore projectstoragetypes.h projectstorageupdater.cpp projectstorageupdater.h projectstorage.cpp projectstorage.h + projectstorageerrornotifierinterface.h + projectstorageerrornotifier.cpp projectstorageerrornotifier.h sourcepath.h sourcepathcache.h sourcepathcacheinterface.h @@ -492,7 +504,6 @@ add_qtc_plugin(QmlDesigner INCLUDES ${CMAKE_CURRENT_LIST_DIR}/components ${CMAKE_CURRENT_LIST_DIR}/components/assetslibrary - ${CMAKE_CURRENT_LIST_DIR}/components/collectioneditor ${CMAKE_CURRENT_LIST_DIR}/components/debugview ${CMAKE_CURRENT_LIST_DIR}/components/edit3d ${CMAKE_CURRENT_LIST_DIR}/components/formeditor @@ -631,6 +642,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 @@ -732,6 +744,8 @@ extend_qtc_plugin(QmlDesigner assetimportupdatetreeitemdelegate.cpp assetimportupdatetreeitemdelegate.h assetimportupdatetreemodel.cpp assetimportupdatetreemodel.h assetimportupdatetreeview.cpp assetimportupdatetreeview.h + import3dcanvas.cpp import3dcanvas.h + import3dconnectionmanager.cpp import3dconnectionmanager.h itemlibrary.qrc itemlibraryconstants.h itemlibraryimageprovider.cpp itemlibraryimageprovider.h @@ -819,9 +833,10 @@ extend_qtc_plugin(QmlDesigner contentlibrarymaterialscategory.cpp contentlibrarymaterialscategory.h contentlibrarymaterial.cpp contentlibrarymaterial.h contentlibraryiconprovider.cpp contentlibraryiconprovider.h - contentlibraryeffect.cpp contentlibraryeffect.h + contentlibraryitem.cpp contentlibraryitem.h contentlibraryeffectscategory.cpp contentlibraryeffectscategory.h contentlibraryeffectsmodel.cpp contentlibraryeffectsmodel.h + contentlibraryusermodel.cpp contentlibraryusermodel.h ) extend_qtc_plugin(QmlDesigner @@ -836,21 +851,6 @@ extend_qtc_plugin(QmlDesigner ) extend_qtc_plugin(QmlDesigner - SOURCES_PREFIX components/collectioneditor - SOURCES - collectiondatatypemodel.cpp collectiondatatypemodel.h - collectiondetails.cpp collectiondetails.h - collectiondetailsmodel.cpp collectiondetailsmodel.h - collectiondetailssortfiltermodel.cpp collectiondetailssortfiltermodel.h - collectioneditorconstants.h - collectioneditorutils.cpp collectioneditorutils.h - collectionlistmodel.cpp collectionlistmodel.h - collectionview.cpp collectionview.h - collectionwidget.cpp collectionwidget.h - datastoremodelnode.cpp datastoremodelnode.h -) - -extend_qtc_plugin(QmlDesigner SOURCES_PREFIX components/textureeditor SOURCES textureeditorcontextobject.cpp textureeditorcontextobject.h diff --git a/src/plugins/qmldesigner/QmlDesigner.json.in b/src/plugins/qmldesigner/QmlDesigner.json.in index 4b5682f485..e96f2f8513 100644 --- a/src/plugins/qmldesigner/QmlDesigner.json.in +++ b/src/plugins/qmldesigner/QmlDesigner.json.in @@ -15,7 +15,7 @@ "Category" : "Qt Quick", "Description" : "Visual Designer for QML files.", "Deprecated" : true, - "Url" : "http://www.qt.io", + "Url" : "https://www.qt.io", "Arguments" : [ { "Name" : "-capture-puppet-stream", diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp index a4509c38a6..cee25240cf 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp @@ -143,7 +143,7 @@ AssetExportDialog::AssetExportDialog(const FilePath &exportPath, m_exportAssetsCheck, m_perComponentExportCheck, st, - noMargin(), + noMargin, }.attachTo(optionsWidget); Column { diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp index dc5a1c9741..b821cc6595 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 <modelnodeoperations.h> #include <theme.h> #include <utils/hdrimage.h> #include <utils/ktximage.h> diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.h index fb38605ea6..d52779232f 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.h +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.h @@ -3,12 +3,11 @@ #pragma once +#include <asset.h> #include <synchronousimagecache.h> #include <QQuickImageProvider> -#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 c2359409eb..7f488bd615 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp @@ -1,21 +1,22 @@ // 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 <QCheckBox> -#include <QFileInfo> -#include <QFileSystemModel> -#include <QMessageBox> -#include <QSortFilterProxyModel> - -#include "asset.h" #include "assetslibrarymodel.h" #include <modelnodeoperations.h> #include <qmldesignerplugin.h> +#include <uniquename.h> #include <coreplugin/icore.h> + #include <utils/algorithm.h> -#include <utils/qtcassert.h> +#include <utils/asset.h> +#include <utils/filepath.h> +#include <utils/filesystemwatcher.h> + +#include <QFileInfo> +#include <QFileSystemModel> +#include <QMessageBox> namespace QmlDesigner { @@ -38,7 +39,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()); @@ -153,16 +154,15 @@ bool AssetsLibraryModel::renameFolder(const QString &folderPath, const QString & QString AssetsLibraryModel::addNewFolder(const QString &folderPath) { - QString iterPath = folderPath; - QDir dir{folderPath}; - - while (dir.exists()) { - iterPath = getUniqueName(iterPath); + Utils::FilePath uniqueDirPath = Utils::FilePath::fromString(UniqueName::generatePath(folderPath)); - dir.setPath(iterPath); + auto res = uniqueDirPath.ensureWritableDir(); + if (!res.has_value()) { + qWarning() << __FUNCTION__ << res.error(); + return {}; } - return dir.mkpath(iterPath) ? iterPath : ""; + return uniqueDirPath.path(); } bool AssetsLibraryModel::urlPathExistsInModel(const QUrl &url) const @@ -207,7 +207,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,60 +218,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()); -} - -QString AssetsLibraryModel::getUniqueName(const QString &oldName) { - static QRegularExpression rgx("\\d+$"); // matches a number at the end of a string - - QString uniqueName = oldName; - // if the folder name ends with a number, increment it - QRegularExpressionMatch match = rgx.match(uniqueName); - if (match.hasMatch()) { // ends with a number - QString numStr = match.captured(0); - int num = match.captured(0).toInt(); - - // get number of padding zeros, ex: for "005" = 2 - int nPaddingZeros = 0; - for (; nPaddingZeros < numStr.size() && numStr[nPaddingZeros] == '0'; ++nPaddingZeros); - - ++num; - - // if the incremented number's digits increased, decrease the padding zeros - if (std::fmod(std::log10(num), 1.0) == 0) - --nPaddingZeros; - - uniqueName = oldName.mid(0, match.capturedStart()) - + QString('0').repeated(nPaddingZeros) - + QString::number(num); - } else { - uniqueName = oldName + '1'; - } - - return uniqueName; + setHasFiles(checkHasFiles()); } void AssetsLibraryModel::setRootPath(const QString &newPath) diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h index 9334e86e9b..f08578651a 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h @@ -3,12 +3,13 @@ #pragma once -#include <QFileInfo> -#include <QFileSystemModel> #include <QSortFilterProxyModel> -#include <utils/filesystemwatcher.h> -#include <utils/qtcassert.h> +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<QModelIndex> parentIndices(const QModelIndex &index) const; Q_INVOKABLE bool indexIsValid(const QModelIndex &index) const; @@ -55,30 +56,28 @@ public: return std::min(result, 1); } - bool haveFiles() const { return m_haveFiles; } - - QString getUniqueName(const QString &oldName); + bool hasFiles() const { return m_hasFiles; } 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 3b98eb6baf..5e2211ce0d 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp @@ -3,26 +3,29 @@ #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 <utils3d.h> +#include <designeractionmanager.h> +#include <designerpaths.h> +#include <hdrimage.h> +#include <import.h> +#include <modelnodeoperations.h> +#include <nodemetainfo.h> +#include <qmldesignerconstants.h> +#include <qmldesignerplugin.h> #include <studioquickwidget.h> +#include <theme.h> +#include <uniquename.h> +#include <utils3d.h> #include <coreplugin/fileutils.h> #include <coreplugin/icore.h> #include <coreplugin/messagebox.h> #include <utils/algorithm.h> +#include <utils/asset.h> #include <utils/environment.h> #include <utils/filepath.h> #include <utils/qtcassert.h> @@ -92,7 +95,7 @@ AssetsLibraryWidget::AssetsLibraryWidget(AsynchronousImageCache &asynchronousFon , m_assetsModel{new AssetsLibraryModel(this)} , m_assetsView{view} , m_createTextures{view} - , m_assetsWidget{new StudioQuickWidget(this)} + , m_assetsWidget{Utils::makeUniqueObjectPtr<StudioQuickWidget>(this)} { setWindowTitle(tr("Assets Library", "Title of assets library widget")); setMinimumWidth(250); @@ -128,7 +131,7 @@ AssetsLibraryWidget::AssetsLibraryWidget(AsynchronousImageCache &asynchronousFon auto layout = new QVBoxLayout(this); layout->setContentsMargins({}); layout->setSpacing(0); - layout->addWidget(m_assetsWidget.data()); + layout->addWidget(m_assetsWidget.get()); updateSearch(); @@ -172,23 +175,10 @@ void AssetsLibraryWidget::deleteSelectedAssets() QString AssetsLibraryWidget::getUniqueEffectPath(const QString &parentFolder, const QString &effectName) { - auto genEffectPath = [&parentFolder](const QString &name) { - QString effectsDir = ModelNodeOperations::getEffectsDefaultDirectory(parentFolder); - return QLatin1String("%1/%2.qep").arg(effectsDir, name); - }; - - QString uniqueName = effectName; - QString path = genEffectPath(uniqueName); - QFileInfo file{path}; + QString effectsDir = ModelNodeOperations::getEffectsDefaultDirectory(parentFolder); + QString effectPath = QLatin1String("%1/%2.qep").arg(effectsDir, effectName); - while (file.exists()) { - uniqueName = m_assetsModel->getUniqueName(uniqueName); - - path = genEffectPath(uniqueName); - file.setFile(path); - } - - return path; + return UniqueName::generatePath(effectPath); } bool AssetsLibraryWidget::createNewEffect(const QString &effectPath, bool openInEffectComposer) @@ -287,14 +277,16 @@ void AssetsLibraryWidget::handleDeleteEffects([[maybe_unused]] const QStringList // Remove usages of deleted effects from the current document m_assetsView->executeInTransaction(__FUNCTION__, [&]() { QList<ModelNode> 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 +294,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; }); @@ -374,7 +366,7 @@ QList<QToolButton *> 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; @@ -643,4 +635,9 @@ void AssetsLibraryWidget::addResources(const QStringList &files, bool showDialog } } +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 ed987d14de..f2d476c842 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h @@ -8,6 +8,8 @@ #include <coreplugin/icontext.h> +#include <utils/uniqueobjectptr.h> + #include <QFrame> #include <QQmlPropertyMap> #include <QQuickWidget> @@ -98,6 +100,7 @@ public: Q_INVOKABLE void showInGraphicalShell(const QString &path); Q_INVOKABLE QString showInGraphicalShellMsg() const; + Q_INVOKABLE void addAssetsToContentLibrary(const QStringList &assetPaths); signals: void itemActivated(const QString &itemName); @@ -135,7 +138,7 @@ private: AssetsLibraryView *m_assetsView = nullptr; CreateTextures m_createTextures = nullptr; - QScopedPointer<StudioQuickWidget> m_assetsWidget; + Utils::UniqueObjectPtr<StudioQuickWidget> m_assetsWidget; std::unique_ptr<PreviewTooltipBackend> m_fontPreviewTooltipBackend; QShortcut *m_qmlSourceUpdateShortcut = nullptr; diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp b/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp index ff2361aa36..b067dc8d46 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp @@ -147,7 +147,6 @@ BindingEditorFactory::BindingEditorFactory() { setId(BINDINGEDITOR_CONTEXT_ID); setDisplayName(::Core::Tr::tr("Binding Editor")); - setEditorActionHandlers(0); addMimeType(BINDINGEDITOR_CONTEXT_ID); addMimeType(Utils::Constants::QML_MIMETYPE); addMimeType(Utils::Constants::QMLTYPES_MIMETYPE); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.cpp deleted file mode 100644 index cb306c58cb..0000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.cpp +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (C) 2024 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "collectiondatatypemodel.h" - -#include <QHash> -#include <QtQml/QmlTypeAndRevisionsRegistration> - -namespace QmlDesigner { - -struct CollectionDataTypeModel::Details -{ - CollectionDetails::DataType type; - QString name; - QString description; -}; - -const QList<CollectionDataTypeModel::Details> CollectionDataTypeModel::m_orderedDetails{ - {DataType::String, "String", "Text"}, - {DataType::Integer, "Integer", "Whole number that can be positive, negative, or zero"}, - {DataType::Real, "Real", "Number with a decimal"}, - {DataType::Image, "Image", "Image resource"}, - {DataType::Color, "Color", "HEX value"}, - {DataType::Url, "Url", "Resource locator"}, - {DataType::Boolean, "Boolean", "True/false"}, - {DataType::Unknown, "Unknown", "Unknown data type"}, -}; - -CollectionDataTypeModel::CollectionDataTypeModel(QObject *parent) - : QAbstractListModel(parent) -{ -} - -int CollectionDataTypeModel::rowCount([[maybe_unused]] const QModelIndex &parent) const -{ - return m_orderedDetails.size(); -} - -QVariant CollectionDataTypeModel::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return {}; - - if (role == Qt::DisplayRole) - return m_orderedDetails.at(index.row()).name; - if (role == Qt::ToolTipRole) - return m_orderedDetails.at(index.row()).description; - - return {}; -} - -QString CollectionDataTypeModel::dataTypeToString(DataType dataType) -{ - static const QHash<DataType, QString> dataTypeHash = []() -> QHash<DataType, QString> { - QHash<DataType, QString> result; - for (const Details &details : m_orderedDetails) - result.insert(details.type, details.name); - return result; - }(); - - if (dataTypeHash.contains(dataType)) - return dataTypeHash.value(dataType); - - return "Unknown"; -} - -CollectionDetails::DataType CollectionDataTypeModel::dataTypeFromString(const QString &dataType) -{ - static const QHash<QString, DataType> stringTypeHash = []() -> QHash<QString, DataType> { - QHash<QString, DataType> result; - for (const Details &details : m_orderedDetails) - result.insert(details.name, details.type); - return result; - }(); - - if (stringTypeHash.contains(dataType)) - return stringTypeHash.value(dataType); - - return DataType::Unknown; -} - -void CollectionDataTypeModel::registerDeclarativeType() -{ - qmlRegisterType<CollectionDataTypeModel>("CollectionDetails", 1, 0, "CollectionDataTypeModel"); -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.h deleted file mode 100644 index 1f91aecbff..0000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (C) 2024 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "collectiondetails.h" - -#include <QAbstractListModel> -#include <QList> - -namespace QmlDesigner { - -class CollectionDataTypeModel : public QAbstractListModel -{ - Q_OBJECT - -public: - using DataType = CollectionDetails::DataType; - CollectionDataTypeModel(QObject *parent = nullptr); - - int rowCount(const QModelIndex &parent = QModelIndex()) const override; - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - - static Q_INVOKABLE QString dataTypeToString(DataType dataType); - static Q_INVOKABLE DataType dataTypeFromString(const QString &dataType); - - static void registerDeclarativeType(); - -private: - struct Details; - static const QList<Details> m_orderedDetails; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp deleted file mode 100644 index ddfb82746c..0000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp +++ /dev/null @@ -1,1011 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "collectiondetails.h" - -#include "collectiondatatypemodel.h" -#include "collectioneditorutils.h" - -#include <utils/span.h> -#include <qmljs/parser/qmljsast_p.h> -#include <qmljs/parser/qmljsastvisitor_p.h> -#include <qmljs/qmljsdocument.h> -#include <qqml.h> - -#include <QJsonArray> -#include <QJsonDocument> -#include <QJsonObject> -#include <QRegularExpression> -#include <QTextStream> -#include <QUrl> -#include <QVariant> - -namespace QmlDesigner { -#define COLLERR_OK QT_TRANSLATE_NOOP("CollectioParseError", "no error occurred") -#define COLLERR_MAINOBJECT QT_TRANSLATE_NOOP("CollectioParseError", "Document object not found") -#define COLLERR_COLLECTIONNAME QT_TRANSLATE_NOOP("CollectioParseError", "Model name not found") -#define COLLERR_COLLECTIONOBJ QT_TRANSLATE_NOOP("CollectioParseError", "Model is not an object") -#define COLLERR_COLUMNARRAY QT_TRANSLATE_NOOP("CollectioParseError", "Column is not an array") -#define COLLERR_UNKNOWN QT_TRANSLATE_NOOP("CollectioParseError", "Unknown error") - -struct CollectionProperty -{ - using DataType = CollectionDetails::DataType; - - QString name; - DataType type; -}; - -const QMap<DataTypeWarning::Warning, QString> DataTypeWarning::dataTypeWarnings = { - {DataTypeWarning::CellDataTypeMismatch, "Cell and column data types do not match."} -}; - -class CollectionDetails::Private -{ -public: - QList<CollectionProperty> properties; - QList<QJsonArray> dataRecords; - CollectionReference reference; - bool isChanged = false; - - bool isValidColumnId(int column) const { return column > -1 && column < properties.size(); } - - bool isValidRowId(int row) const { return row > -1 && row < dataRecords.size(); } -}; - -inline static bool isValidColorName(const QString &colorName) -{ - return QColor::isValidColorName(colorName); -} - -/** - * @brief getCustomUrl - * MimeType = <MainType/SubType> - * Address = <Url|LocalFile> - * - * @param value The input value to be evaluated - * @param dataType if the value is a valid url or image, 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 - * 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 - */ -static bool getCustomUrl(const QString &value, - CollectionDetails::DataType &dataType, - QUrl *urlResult = nullptr, - QString *subType = nullptr) -{ - static const QRegularExpression urlRegex{ - "^(?<MimeType>" - "(?<MainType>image)\\/" - "(?<SubType>apng|avif|gif|jpeg|png|(?:svg\\+xml)|webp|xyz)\\:)?" // end of MimeType - "(?<Address>" - "(?<Url>https?:\\/\\/" - "(?:www\\.|(?!www))[A-z0-9][A-z0-9-]+[A-z0-9]\\.[^\\s]{2,}|www\\.[A-z0-9][A-z0-9-]+" - "[A-z0-9]\\.[^\\s]{2,}|https?:\\/\\/" - "(?:www\\.|(?!www))[A-z0-9]+\\.[^\\s]{2,}|www\\.[A-z0-9]+\\.[^\\s]{2,})|" // end of Url - "(?<LocalFile>(" - "?:(?:[A-z]:)|(?:(?:\\\\|\\/){1,2}\\w+)\\$?)(?:(?:\\\\|\\/)(?:\\w[\\w ]*.*))+)" // end of LocalFile - "){1}$" // end of Address - }; - - 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 (urlResult) - urlResult->setUrl(match.captured("Address")); - - if (subType) - *subType = match.captured("SubType"); - - return true; - } - } - - if (urlResult) - urlResult->clear(); - - if (subType) - subType->clear(); - - dataType = CollectionDetails::DataType::Unknown; - return false; -} - -/** - * @brief dataTypeFromString - * @param value The string value to be evaluated - * @return Unknown if the string is empty, But returns Bool, Color, Integer, - * Real, Url, Image if these types are detected within the non-empty string, - * Otherwise it returns String. - * If the value is integer, but it's out of the int range, it will be - * considered as a Real. - */ -static CollectionDetails::DataType dataTypeFromString(const QString &value) -{ - using DataType = CollectionDetails::DataType; - static const QRegularExpression validator{ - "(?<boolean>^(?:true|false)$)|" - "(?<color>^(?:#(?:(?:[0-9a-fA-F]{2}){3,4}|(?:[0-9a-fA-F]){3,4}))$)|" - "(?<integer>^\\d+$)|" - "(?<real>^(?:-?(?:0|[1-9]\\d*)?(?:\\.\\d*)?(?<=\\d|\\.)" - "(?:e-?(?:0|[1-9]\\d*))?|0x[0-9a-f]+)$)"}; - static const int boolIndex = validator.namedCaptureGroups().indexOf("boolean"); - static const int colorIndex = validator.namedCaptureGroups().indexOf("color"); - static const int integerIndex = validator.namedCaptureGroups().indexOf("integer"); - static const int realIndex = validator.namedCaptureGroups().indexOf("real"); - - [[maybe_unused]] static const bool allIndexesFound = - [](const std::initializer_list<int> &captureIndexes) { - QTC_ASSERT(Utils::allOf(captureIndexes, [](int val) { return val > -1; }), return false); - return true; - }({boolIndex, colorIndex, integerIndex, realIndex}); - - if (value.isEmpty()) - return DataType::Unknown; - - const QString trimmedValue = value.trimmed(); - QRegularExpressionMatch match = validator.match(trimmedValue); - - if (match.hasCaptured(boolIndex)) - return DataType::Boolean; - if (match.hasCaptured(colorIndex)) - return DataType::Color; - if (match.hasCaptured(integerIndex)) { - bool isInt = false; - trimmedValue.toInt(&isInt); - return isInt ? DataType::Integer : DataType::Real; - } - if (match.hasCaptured(realIndex)) - return DataType::Real; - - DataType urlType; - if (getCustomUrl(trimmedValue, urlType)) - return urlType; - - return DataType::String; -} - -static CollectionProperty::DataType dataTypeFromJsonValue(const QJsonValue &value) -{ - using DataType = CollectionDetails::DataType; - using JsonType = QJsonValue::Type; - - switch (value.type()) { - case JsonType::Null: - case JsonType::Undefined: - return DataType::Unknown; - case JsonType::Bool: - return DataType::Boolean; - case JsonType::Double: { - if (qFuzzyIsNull(std::remainder(value.toDouble(), 1))) - return DataType::Integer; - return DataType::Real; - } - case JsonType::String: - return dataTypeFromString(value.toString()); - default: - return DataType::Unknown; - } -} - -static QList<CollectionProperty> getColumnsFromImportedJsonArray(const QJsonArray &importedArray) -{ - using DataType = CollectionDetails::DataType; - - QHash<QString, int> resultSet; - QList<CollectionProperty> result; - - for (const QJsonValue &value : importedArray) { - if (value.isObject()) { - const QJsonObject object = value.toObject(); - QJsonObject::ConstIterator element = object.constBegin(); - const QJsonObject::ConstIterator stopItem = object.constEnd(); - - while (element != stopItem) { - const QString propertyName = element.key(); - if (resultSet.contains(propertyName)) { - CollectionProperty &property = result[resultSet.value(propertyName)]; - if (property.type == DataType::Unknown) { - property.type = dataTypeFromJsonValue(element.value()); - } else if (property.type == DataType::Integer) { - const DataType currentCellDataType = dataTypeFromJsonValue(element.value()); - if (currentCellDataType == DataType::Real) - property.type = currentCellDataType; - } - } else { - result.append({propertyName, dataTypeFromJsonValue(element.value())}); - resultSet.insert(propertyName, resultSet.size()); - } - ++element; - } - } - } - - return result; -} - -static QVariant valueToVariant(const QJsonValue &value, CollectionDetails::DataType type) -{ - using DataType = CollectionDetails::DataType; - QVariant variantValue = value.toVariant(); - - switch (type) { - case DataType::String: - return variantValue.toString(); - case DataType::Integer: - return variantValue.toInt(); - case DataType::Real: - return variantValue.toDouble(); - case DataType::Boolean: - return variantValue.toBool(); - case DataType::Color: - return variantValue.value<QColor>(); - case DataType::Image: { - DataType type; - QUrl url; - if (getCustomUrl(variantValue.toString(), type, &url)) - return url; - return variantValue.toString(); - } - case DataType::Url: - return variantValue.value<QUrl>(); - default: - return variantValue; - } -} - -static QJsonValue variantToJsonValue( - const QVariant &variant, CollectionDetails::DataType type = CollectionDetails::DataType::Unknown) -{ - using VariantType = QVariant::Type; - using DataType = CollectionDetails::DataType; - - if (type == CollectionDetails::DataType::Unknown) { - static const QHash<VariantType, DataType> typeMap = {{VariantType::Bool, DataType::Boolean}, - {VariantType::Double, DataType::Real}, - {VariantType::Int, DataType::Integer}, - {VariantType::String, DataType::String}, - {VariantType::Color, DataType::Color}, - {VariantType::Url, DataType::Url}}; - type = typeMap.value(variant.type(), DataType::Unknown); - } - - switch (type) { - case DataType::Boolean: - return variant.toBool(); - case DataType::Real: - 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::String: - case DataType::Color: - case DataType::Url: - default: - return variant.toString(); - } -} - -inline static bool isEmptyJsonValue(const QJsonValue &value) -{ - return value.isNull() || value.isUndefined() || (value.isString() && value.toString().isEmpty()); -} - -QStringList csvReadLine(const QString &line) -{ - static const QRegularExpression lineRegex{ - "(?:,\\\"|^\\\")(?<value>\\\"\\\"|[\\w\\W]*?)(?=\\\",|\\\"$)" - "|(?:,(?!\\\")|^(?!\\\"))(?<quote>[^,]*?)(?=$|,)|(\\\\r\\\\n|\\\\n)"}; - static const int valueIndex = lineRegex.namedCaptureGroups().indexOf("value"); - static const int quoteIndex = lineRegex.namedCaptureGroups().indexOf("quote"); - Q_ASSERT(valueIndex > 0 && quoteIndex > 0); - - QStringList result; - QRegularExpressionMatchIterator iterator = lineRegex.globalMatch(line, 0); - while (iterator.hasNext()) { - const QRegularExpressionMatch match = iterator.next(); - - if (match.hasCaptured(valueIndex)) - result.append(match.captured(valueIndex)); - else if (match.hasCaptured(quoteIndex)) - result.append(match.captured(quoteIndex)); - } - return result; -} - -class PropertyOrderFinder : public QmlJS::AST::Visitor -{ -public: - static QStringList parse(const QString &jsonContent) - { - PropertyOrderFinder finder; - QmlJS::Document::MutablePtr jsonDoc = QmlJS::Document::create(Utils::FilePath::fromString( - "<expression>"), - QmlJS::Dialect::Json); - - jsonDoc->setSource(jsonContent); - jsonDoc->parseJavaScript(); - - if (!jsonDoc->isParsedCorrectly()) - return {}; - - jsonDoc->ast()->accept(&finder); - return finder.m_orderedList; - } - -protected: - bool visit(QmlJS::AST::PatternProperty *patternProperty) override - { - const QString propertyName = patternProperty->name->asString(); - if (!m_propertySet.contains(propertyName)) { - m_propertySet.insert(propertyName); - m_orderedList.append(propertyName); - } - return true; - } - - void throwRecursionDepthError() override - { - qWarning() << Q_FUNC_INFO << __LINE__ << "Recursion depth error"; - }; - -private: - QSet<QString> m_propertySet; - QStringList m_orderedList; -}; - -QString CollectionParseError::errorString() const -{ - switch (errorNo) { - case NoError: - return COLLERR_OK; - case MainObjectMissing: - return COLLERR_MAINOBJECT; - case CollectionNameNotFound: - return COLLERR_COLLECTIONNAME; - case CollectionIsNotObject: - return COLLERR_COLLECTIONOBJ; - case ColumnsBlockIsNotArray: - return COLLERR_COLUMNARRAY; - case UnknownError: - default: - return COLLERR_UNKNOWN; - } -} - -CollectionDetails::CollectionDetails() - : d(new Private()) -{} - -CollectionDetails::CollectionDetails(const CollectionReference &reference) - : CollectionDetails() -{ - d->reference = reference; -} - -void CollectionDetails::resetData(const QJsonDocument &localDocument, - const QString &collectionToImport, - CollectionParseError *error) -{ - CollectionDetails importedCollection = fromLocalJson(localDocument, collectionToImport, error); - d->properties.swap(importedCollection.d->properties); - d->dataRecords.swap(importedCollection.d->dataRecords); -} - -CollectionDetails::CollectionDetails(const CollectionDetails &other) = default; - -CollectionDetails::~CollectionDetails() = default; - -void CollectionDetails::insertColumn(const QString &propertyName, - int colIdx, - const QVariant &defaultValue, - DataType type) -{ - if (containsPropertyName(propertyName)) - return; - - CollectionProperty property = {propertyName, type}; - if (d->isValidColumnId(colIdx)) { - d->properties.insert(colIdx, property); - } else { - colIdx = d->properties.size(); - d->properties.append(property); - } - - const QJsonValue defaultJsonValue = QJsonValue::fromVariant(defaultValue); - for (QJsonArray &record : d->dataRecords) - record.insert(colIdx, defaultJsonValue); - - markChanged(); -} - -bool CollectionDetails::removeColumns(int colIdx, int count) -{ - if (!d->isValidColumnId(colIdx)) - return false; - - int maxCount = d->properties.count() - colIdx; - count = std::min(maxCount, count); - - if (count < 1) - return false; - - d->properties.remove(colIdx, count); - - for (QJsonArray &record : d->dataRecords) { - QJsonArray newElement; - - auto elementItr = record.constBegin(); - auto elementEnd = elementItr + colIdx; - while (elementItr != elementEnd) - newElement.append(*(elementItr++)); - - elementItr += count; - elementEnd = record.constEnd(); - - while (elementItr != elementEnd) - newElement.append(*(elementItr++)); - - record = newElement; - } - - markChanged(); - - return true; -} - -void CollectionDetails::insertEmptyRows(int row, int count) -{ - if (count < 1) - return; - - row = qBound(0, row, rows()); - - insertRecords({}, row, count); - - markChanged(); -} - -bool CollectionDetails::removeRows(int row, int count) -{ - if (!d->isValidRowId(row)) - return false; - - int maxCount = d->dataRecords.count() - row; - count = std::min(maxCount, count); - - if (count < 1) - return false; - - d->dataRecords.remove(row, count); - markChanged(); - return true; -} - -bool CollectionDetails::setPropertyValue(int row, int column, const QVariant &value) -{ - if (!d->isValidRowId(row) || !d->isValidColumnId(column)) - return false; - - QVariant currentValue = data(row, column); - if (value == currentValue) - return false; - - QJsonArray &record = d->dataRecords[row]; - record.replace(column, variantToJsonValue(value, typeAt(column))); - markChanged(); - return true; -} - -bool CollectionDetails::setPropertyName(int column, const QString &value) -{ - if (!d->isValidColumnId(column)) - return false; - - const CollectionProperty &oldProperty = d->properties.at(column); - if (oldProperty.name == value) - return false; - - d->properties.replace(column, {value, oldProperty.type}); - - markChanged(); - return true; -} - -bool CollectionDetails::setPropertyType(int column, DataType type) -{ - if (!d->isValidColumnId(column)) - return false; - - bool changed = false; - CollectionProperty &property = d->properties[column]; - if (property.type != type) - changed = true; - - const DataType formerType = property.type; - property.type = type; - - for (QJsonArray &rowData : d->dataRecords) { - if (column < rowData.size()) { - const QJsonValue value = rowData.at(column); - const QVariant properTypedValue = valueToVariant(value, formerType); - const QJsonValue properTypedJsonValue = variantToJsonValue(properTypedValue, type); - rowData.replace(column, properTypedJsonValue); - changed = true; - } - } - - if (changed) - markChanged(); - - return changed; -} - -CollectionReference CollectionDetails::reference() const -{ - return d->reference; -} - -QVariant CollectionDetails::data(int row, int column) const -{ - if (!d->isValidRowId(row)) - return {}; - - if (!d->isValidColumnId(column)) - return {}; - - const QJsonValue cellValue = d->dataRecords.at(row).at(column); - - if (typeAt(column) == DataType::Image) { - const QUrl imageUrl = valueToVariant(cellValue, DataType::Image).toUrl(); - - if (imageUrl.isValid()) - return imageUrl; - } - - return cellValue.toVariant(); -} - -QString CollectionDetails::propertyAt(int column) const -{ - if (!d->isValidColumnId(column)) - return {}; - - return d->properties.at(column).name; -} - -CollectionDetails::DataType CollectionDetails::typeAt(int column) const -{ - if (!d->isValidColumnId(column)) - return {}; - - return d->properties.at(column).type; -} - -CollectionDetails::DataType CollectionDetails::typeAt(int row, int column) const -{ - if (!d->isValidRowId(row) || !d->isValidColumnId(column)) - return {}; - - const QJsonValue cellData = d->dataRecords.at(row).at(column); - return dataTypeFromJsonValue(cellData); -} - -DataTypeWarning::Warning CollectionDetails::cellWarningCheck(int row, int column) const -{ - const QJsonValue cellValue = d->dataRecords.at(row).at(column); - - const DataType columnType = typeAt(column); - const DataType cellType = typeAt(row, column); - - if (columnType == DataType::Unknown || isEmptyJsonValue(cellValue)) - return DataTypeWarning::Warning::None; - - if (columnType == DataType::Real && cellType == DataType::Integer) - return DataTypeWarning::Warning::None; - - if (columnType != cellType) - return DataTypeWarning::Warning::CellDataTypeMismatch; - - return DataTypeWarning::Warning::None; -} - -bool CollectionDetails::containsPropertyName(const QString &propertyName) const -{ - return Utils::anyOf(d->properties, [&propertyName](const CollectionProperty &property) { - return property.name == propertyName; - }); -} - -bool CollectionDetails::hasValidReference() const -{ - return d->reference.node.isValid() && d->reference.name.size(); -} - -bool CollectionDetails::isChanged() const -{ - return d->isChanged; -} - -int CollectionDetails::columns() const -{ - return d->properties.size(); -} - -int CollectionDetails::rows() const -{ - return d->dataRecords.size(); -} - -bool CollectionDetails::markSaved() -{ - if (d->isChanged) { - d->isChanged = false; - return true; - } - return false; -} - -void CollectionDetails::swap(CollectionDetails &other) -{ - d.swap(other.d); -} - -void CollectionDetails::resetReference(const CollectionReference &reference) -{ - if (d->reference != reference) { - d->reference = reference; - markChanged(); - } -} - -QString CollectionDetails::toJson() const -{ - QJsonArray exportedArray; - const int propertyCount = d->properties.count(); - - for (const QJsonArray &record : std::as_const(d->dataRecords)) { - const int valueCount = std::min(int(record.count()), propertyCount); - - QJsonObject exportedElement; - for (int i = 0; i < valueCount; ++i) { - const QJsonValue &value = record.at(i); - if (isEmptyJsonValue(value)) - exportedElement.insert(d->properties.at(i).name, QJsonValue::Null); - else - exportedElement.insert(d->properties.at(i).name, value); - } - - exportedArray.append(exportedElement); - } - - return QString::fromUtf8(QJsonDocument(exportedArray).toJson()); -} - -QString CollectionDetails::toCsv() const -{ - QString content; - - auto gotoNextLine = [&content]() { - if (content.size() && content.back() == ',') - content.back() = '\n'; - else - content += "\n"; - }; - - const int propertyCount = d->properties.count(); - if (propertyCount <= 0) - return ""; - - for (const CollectionProperty &property : std::as_const(d->properties)) - content += property.name + ','; - - gotoNextLine(); - - for (const QJsonArray &record : std::as_const(d->dataRecords)) { - const int valueCount = std::min(int(record.count()), propertyCount); - int i = 0; - for (; i < valueCount; ++i) { - const QJsonValue &value = record.at(i); - - if (value.isDouble()) - content += QString::number(value.toDouble()) + ','; - else if (value.isBool()) - content += value.toBool() ? QString("true,") : QString("false,"); - else - content += value.toString() + ','; - } - - for (; i < propertyCount; ++i) - content += ','; - - gotoNextLine(); - } - - return content; -} - -QJsonObject CollectionDetails::toLocalJson() const -{ - QJsonObject collectionObject; - QJsonArray columnsArray; - QJsonArray dataArray; - - for (const CollectionProperty &property : std::as_const(d->properties)) { - QJsonObject columnObject; - columnObject.insert("name", property.name); - columnObject.insert("type", CollectionDataTypeModel::dataTypeToString(property.type)); - columnsArray.append(columnObject); - } - - for (const QJsonArray &record : std::as_const(d->dataRecords)) - dataArray.append(record); - - collectionObject.insert("columns", columnsArray); - collectionObject.insert("data", dataArray); - - return collectionObject; -} - -void CollectionDetails::registerDeclarativeType() -{ - typedef CollectionDetails::DataType DataType; - qRegisterMetaType<DataType>("DataType"); - qmlRegisterUncreatableType<CollectionDetails>("CollectionDetails", 1, 0, "DataType", "Enum type"); - - qRegisterMetaType<DataTypeWarning::Warning>("Warning"); - qmlRegisterUncreatableType<DataTypeWarning>("CollectionDetails", 1, 0, "Warning", "Enum type"); -} - -CollectionDetails CollectionDetails::fromImportedCsv(const QByteArray &document, - const bool &firstRowIsHeader) -{ - QStringList headers; - QJsonArray importedArray; - - QTextStream stream(document); - stream.setEncoding(QStringConverter::Latin1); - - if (firstRowIsHeader && !stream.atEnd()) { - headers = Utils::transform(csvReadLine(stream.readLine()), - [](const QString &value) -> QString { return value.trimmed(); }); - } - - while (!stream.atEnd()) { - const QStringList recordDataList = csvReadLine(stream.readLine()); - int column = -1; - QJsonObject recordData; - for (const QString &cellData : recordDataList) { - if (++column == headers.size()) { - QString proposalName; - int proposalId = column; - do - proposalName = QString("Column %1").arg(++proposalId); - while (headers.contains(proposalName)); - headers.append(proposalName); - } - recordData.insert(headers.at(column), cellData); - } - importedArray.append(recordData); - } - - return fromImportedJson(importedArray, headers); -} - -CollectionDetails CollectionDetails::fromImportedJson(const QByteArray &json, QJsonParseError *error) -{ - QJsonArray importedCollection; - auto refineJsonArray = [](const QJsonArray &array) -> QJsonArray { - QJsonArray resultArray; - for (const QJsonValue &collectionData : array) { - if (collectionData.isObject()) { - QJsonObject rowObject = collectionData.toObject(); - const QStringList rowKeys = rowObject.keys(); - for (const QString &key : rowKeys) { - const QJsonValue cellValue = rowObject.value(key); - if (cellValue.isArray()) - rowObject.remove(key); - } - resultArray.push_back(rowObject); - } - } - return resultArray; - }; - - QJsonParseError parseError; - QJsonDocument document = QJsonDocument::fromJson(json, &parseError); - if (error) - *error = parseError; - - if (parseError.error != QJsonParseError::NoError) - return CollectionDetails{}; - - if (document.isArray()) { - importedCollection = refineJsonArray(document.array()); - } else if (document.isObject()) { - QJsonObject documentObject = document.object(); - const QStringList mainKeys = documentObject.keys(); - - bool arrayFound = false; - for (const QString &key : mainKeys) { - const QJsonValue value = documentObject.value(key); - if (value.isArray()) { - arrayFound = true; - importedCollection = refineJsonArray(value.toArray()); - break; - } - } - - if (!arrayFound) { - QJsonObject singleObject; - for (const QString &key : mainKeys) { - const QJsonValue value = documentObject.value(key); - - if (!value.isObject()) - singleObject.insert(key, value); - } - importedCollection.push_back(singleObject); - } - } - - return fromImportedJson(importedCollection, PropertyOrderFinder::parse(QLatin1String(json))); -} - -CollectionDetails CollectionDetails::fromLocalJson(const QJsonDocument &document, - const QString &collectionName, - CollectionParseError *error) -{ - auto setError = [&error](CollectionParseError::ParseError parseError) { - if (error) - error->errorNo = parseError; - }; - - setError(CollectionParseError::NoError); - - if (document.isObject()) { - QJsonObject collectionMap = document.object(); - if (collectionMap.contains(collectionName)) { - QJsonValue collectionValue = collectionMap.value(collectionName); - if (collectionValue.isObject()) - return fromLocalCollection(collectionValue.toObject()); - else - setError(CollectionParseError::CollectionIsNotObject); - } else { - setError(CollectionParseError::CollectionNameNotFound); - } - } else { - setError(CollectionParseError::MainObjectMissing); - } - - return CollectionDetails{}; -} - -CollectionDetails &CollectionDetails::operator=(const CollectionDetails &other) -{ - CollectionDetails value(other); - swap(value); - return *this; -} - -void CollectionDetails::markChanged() -{ - d->isChanged = true; -} - -void CollectionDetails::insertRecords(const QJsonArray &record, int idx, int count) -{ - if (count < 1) - return; - - QJsonArray localRecord; - const int columnsCount = columns(); - for (int i = 0; i < columnsCount; i++) { - const QJsonValue originalCellData = record.at(i); - if (originalCellData.isArray()) - localRecord.append({}); - else - localRecord.append(originalCellData); - } - - if (idx > d->dataRecords.size() || idx < 0) - idx = d->dataRecords.size(); - - d->dataRecords.insert(idx, count, localRecord); -} - -CollectionDetails CollectionDetails::fromImportedJson(const QJsonArray &importedArray, - const QStringList &propertyPriority) -{ - QList<CollectionProperty> columnData = getColumnsFromImportedJsonArray(importedArray); - if (!propertyPriority.isEmpty()) { - QMap<QString, int> priorityMap; - for (const QString &propertyName : propertyPriority) { - if (!priorityMap.contains(propertyName)) - priorityMap.insert(propertyName, priorityMap.size()); - } - const int lowestPriority = priorityMap.size(); - - Utils::sort(columnData, [&](const CollectionProperty &a, const CollectionProperty &b) { - return priorityMap.value(a.name, lowestPriority) - < priorityMap.value(b.name, lowestPriority); - }); - } - - QList<QJsonArray> localJsonArray; - for (const QJsonValue &importedRowValue : importedArray) { - QJsonObject importedRowObject = importedRowValue.toObject(); - QJsonArray localRow; - for (const CollectionProperty &property : columnData) - localRow.append(importedRowObject.value(property.name)); - localJsonArray.append(localRow); - } - CollectionDetails result; - result.d->properties = columnData; - result.d->dataRecords = localJsonArray; - result.markSaved(); - - return result; -} - -CollectionDetails CollectionDetails::fromLocalCollection(const QJsonObject &localCollection, - CollectionParseError *error) -{ - auto setError = [&error](CollectionParseError::ParseError parseError) { - if (error) - error->errorNo = parseError; - }; - - CollectionDetails result; - setError(CollectionParseError::NoError); - - if (localCollection.contains("columns")) { - const QJsonValue columnsValue = localCollection.value("columns"); - if (columnsValue.isArray()) { - const QJsonArray columns = columnsValue.toArray(); - for (const QJsonValue &columnValue : columns) { - if (columnValue.isObject()) { - const QJsonObject column = columnValue.toObject(); - const QString columnName = column.value("name").toString(); - if (!columnName.isEmpty()) { - result.insertColumn(columnName, - -1, - {}, - CollectionDataTypeModel::dataTypeFromString( - column.value("type").toString())); - } - } - } - - if (int columnsCount = result.columns()) { - const QJsonArray dataRecords = localCollection.value("data").toArray(); - for (const QJsonValue &dataRecordValue : dataRecords) { - QJsonArray dataRecord = dataRecordValue.toArray(); - while (dataRecord.count() > columnsCount) - dataRecord.removeLast(); - - result.insertRecords(dataRecord); - } - } - } else { - setError(CollectionParseError::ColumnsBlockIsNotArray); - return result; - } - } - - return result; -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h deleted file mode 100644 index b84c214570..0000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "modelnode.h" - -#include <QSharedPointer> - -QT_BEGIN_NAMESPACE -class QJsonObject; -struct QJsonParseError; -class QVariant; -QT_END_NAMESPACE - -namespace QmlDesigner { - -struct CollectionReference -{ - ModelNode node; - QString name; - - friend auto qHash(const CollectionReference &collection) - { - return qHash(collection.node) ^ ::qHash(collection.name); - } - - bool operator==(const CollectionReference &other) const - { - return node == other.node && name == other.name; - } - - bool operator!=(const CollectionReference &other) const { return !(*this == other); } -}; - -struct CollectionProperty; - -struct DataTypeWarning { -public: - enum Warning { None, CellDataTypeMismatch }; - Q_ENUM(Warning) - - Warning warning = None; - DataTypeWarning(Warning warning) - : warning(warning) - {} - - static QString getDataTypeWarningString(Warning warning) - { - return dataTypeWarnings.value(warning); - } - -private: - Q_GADGET - static const QMap<Warning, QString> dataTypeWarnings; -}; - -struct CollectionParseError -{ - enum ParseError { - NoError, - MainObjectMissing, - CollectionNameNotFound, - CollectionIsNotObject, - ColumnsBlockIsNotArray, - UnknownError - }; - - ParseError errorNo = ParseError::NoError; - QString errorString() const; -}; - -class CollectionDetails -{ - Q_GADGET - -public: - enum class DataType { Unknown, String, Url, Integer, Real, Boolean, Image, Color }; - Q_ENUM(DataType) - - explicit CollectionDetails(); - CollectionDetails(const CollectionReference &reference); - CollectionDetails(const CollectionDetails &other); - ~CollectionDetails(); - - void resetData(const QJsonDocument &localDocument, - const QString &collectionToImport, - CollectionParseError *error = nullptr); - - void insertColumn(const QString &propertyName, - int colIdx = -1, - const QVariant &defaultValue = {}, - DataType type = DataType::Unknown); - bool removeColumns(int colIdx, int count = 1); - - void insertEmptyRows(int row = 0, int count = 1); - bool removeRows(int row, int count = 1); - bool setPropertyValue(int row, int column, const QVariant &value); - - bool setPropertyName(int column, const QString &value); - bool setPropertyType(int column, DataType type); - - CollectionReference reference() const; - QVariant data(int row, int column) const; - QString propertyAt(int column) const; - DataType typeAt(int column) const; - DataType typeAt(int row, int column) const; - DataTypeWarning::Warning cellWarningCheck(int row, int column) const; - bool containsPropertyName(const QString &propertyName) const; - - bool hasValidReference() const; - bool isChanged() const; - - int columns() const; - int rows() const; - - bool markSaved(); - - void swap(CollectionDetails &other); - void resetReference(const CollectionReference &reference); - - QString toJson() const; - QString toCsv() const; - QJsonObject toLocalJson() const; - - static void registerDeclarativeType(); - - static CollectionDetails fromImportedCsv(const QByteArray &document, - const bool &firstRowIsHeader = true); - static CollectionDetails fromImportedJson(const QByteArray &json, - QJsonParseError *error = nullptr); - static CollectionDetails fromLocalJson(const QJsonDocument &document, - const QString &collectionName, - CollectionParseError *error = nullptr); - - CollectionDetails &operator=(const CollectionDetails &other); - -private: - void markChanged(); - void insertRecords(const QJsonArray &record, int idx = -1, int count = 1); - - static CollectionDetails fromImportedJson(const QJsonArray &importedArray, - const QStringList &propertyPriority = {}); - static CollectionDetails fromLocalCollection(const QJsonObject &localCollection, - CollectionParseError *error = nullptr); - - // The private data is supposed to be shared between the copies - class Private; - QSharedPointer<Private> d; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp deleted file mode 100644 index b26b1a845e..0000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp +++ /dev/null @@ -1,621 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "collectiondetailsmodel.h" - -#include "collectiondatatypemodel.h" -#include "collectioneditorutils.h" -#include "modelnode.h" - -#include <coreplugin/editormanager/editormanager.h> - -#include <utils/fileutils.h> -#include <utils/qtcassert.h> -#include <utils/textfileformat.h> - -#include <QJsonArray> -#include <QJsonDocument> -#include <QJsonObject> -#include <QJsonParseError> - -namespace QmlDesigner { - -CollectionDetailsModel::CollectionDetailsModel(QObject *parent) - : QAbstractTableModel(parent) -{ - connect(this, &CollectionDetailsModel::modelReset, this, &CollectionDetailsModel::updateEmpty); - connect(this, &CollectionDetailsModel::rowsInserted, this, &CollectionDetailsModel::updateEmpty); - connect(this, &CollectionDetailsModel::rowsRemoved, this, &CollectionDetailsModel::updateEmpty); -} - -QHash<int, QByteArray> CollectionDetailsModel::roleNames() const -{ - static QHash<int, QByteArray> roles; - if (roles.isEmpty()) { - roles.insert(QAbstractTableModel::roleNames()); - roles.insert(SelectedRole, "itemSelected"); - roles.insert(DataTypeRole, "dataType"); - roles.insert(ColumnDataTypeRole, "columnType"); - roles.insert(DataTypeWarningRole, "dataTypeWarning"); - } - return roles; -} - -int CollectionDetailsModel::rowCount([[maybe_unused]] const QModelIndex &parent) const -{ - return m_currentCollection.rows(); -} - -int CollectionDetailsModel::columnCount([[maybe_unused]] const QModelIndex &parent) const -{ - return m_currentCollection.columns(); -} - -QVariant CollectionDetailsModel::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return {}; - - QTC_ASSERT(m_currentCollection.hasValidReference(), return {}); - - if (role == SelectedRole) - return (index.column() == m_selectedColumn || index.row() == m_selectedRow); - - if (role == DataTypeRole) - return QVariant::fromValue(m_currentCollection.typeAt(index.row(), index.column())); - - if (role == ColumnDataTypeRole) - return QVariant::fromValue(m_currentCollection.typeAt(index.column())); - - if (role == Qt::EditRole) - return m_currentCollection.data(index.row(), index.column()); - - if (role == DataTypeWarningRole ) - return QVariant::fromValue(m_currentCollection.cellWarningCheck(index.row(), index.column())); - - return m_currentCollection.data(index.row(), index.column()).toString(); -} - -bool CollectionDetailsModel::setData(const QModelIndex &index, const QVariant &value, int role) -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return false); - - if (!index.isValid()) - return {}; - - if (role == Qt::EditRole) { - DataTypeWarning::Warning prevWarning = m_currentCollection.cellWarningCheck(index.row(), index.column()); - bool changed = m_currentCollection.setPropertyValue(index.row(), index.column(), value); - - if (changed) { - QList<int> roles = {Qt::DisplayRole, Qt::EditRole}; - - if (prevWarning != m_currentCollection.cellWarningCheck(index.row(), index.column())) - roles << DataTypeWarningRole; - - emit dataChanged(index, index, roles); - } - - return true; - } - - return false; -} - -bool CollectionDetailsModel::setHeaderData(int section, - Qt::Orientation orientation, - const QVariant &value, - [[maybe_unused]] int role) -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return false); - - if (orientation == Qt::Vertical) - return false; - - bool headerChanged = m_currentCollection.setPropertyName(section, value.toString()); - if (headerChanged) - emit this->headerDataChanged(orientation, section, section); - - return headerChanged; -} - -bool CollectionDetailsModel::insertRows(int row, int count, [[maybe_unused]] const QModelIndex &parent) -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return false); - - if (count < 1) - return false; - - row = qBound(0, row, rowCount()); - - beginResetModel(); - m_currentCollection.insertEmptyRows(row, count); - endResetModel(); - - selectRow(row); - return true; -} - -bool CollectionDetailsModel::removeColumns(int column, int count, const QModelIndex &parent) -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return false); - - if (column < 0 || column >= columnCount(parent) || count < 1) - return false; - - count = std::min(count, columnCount(parent) - column); - beginRemoveColumns(parent, column, column + count - 1); - bool columnsRemoved = m_currentCollection.removeColumns(column, count); - endRemoveColumns(); - - if (!columnCount(parent)) - removeRows(0, rowCount(parent), parent); - - int nextColumn = column - 1; - if (nextColumn < 0 && columnCount(parent) > 0) - nextColumn = 0; - - selectColumn(nextColumn); - - ensureSingleCell(); - return columnsRemoved; -} - -bool CollectionDetailsModel::removeRows(int row, int count, const QModelIndex &parent) -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return false); - - if (row < 0 || row >= rowCount(parent) || count < 1) - return false; - - count = std::min(count, rowCount(parent) - row); - beginRemoveRows(parent, row, row + count - 1); - bool rowsRemoved = m_currentCollection.removeRows(row, count); - endRemoveRows(); - - ensureSingleCell(); - return rowsRemoved; -} - -Qt::ItemFlags CollectionDetailsModel::flags(const QModelIndex &index) const -{ - if (!index.isValid()) - return {}; - - return {Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable}; -} - -QVariant CollectionDetailsModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (orientation == Qt::Horizontal) { - if (role == DataTypeRole) - return CollectionDataTypeModel::dataTypeToString(m_currentCollection.typeAt(section)); - else - return m_currentCollection.propertyAt(section); - } - - if (orientation == Qt::Vertical) - return section + 1; - - return {}; -} - -CollectionDetails::DataType CollectionDetailsModel::propertyDataType(int column) const -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return CollectionDetails::DataType::Unknown); - - return m_currentCollection.typeAt(column); -} - -int CollectionDetailsModel::selectedColumn() const -{ - return m_selectedColumn; -} - -int CollectionDetailsModel::selectedRow() const -{ - return m_selectedRow; -} - -QString CollectionDetailsModel::propertyName(int column) const -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return {}); - - return m_currentCollection.propertyAt(column); -} - -QString CollectionDetailsModel::propertyType(int column) const -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return {}); - - return CollectionDataTypeModel::dataTypeToString(m_currentCollection.typeAt(column)); -} - -bool CollectionDetailsModel::isPropertyAvailable(const QString &name) -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return false); - - return m_currentCollection.containsPropertyName(name); -} - -bool CollectionDetailsModel::addColumn(int column, const QString &name, const QString &propertyType) -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return false); - - if (m_currentCollection.containsPropertyName(name)) - return false; - - if (column < 0 || column > columnCount()) - column = columnCount(); - - beginInsertColumns({}, column, column); - m_currentCollection.insertColumn(name, - column, - {}, - CollectionDataTypeModel::dataTypeFromString(propertyType)); - endInsertColumns(); - return m_currentCollection.containsPropertyName(name); -} - -bool CollectionDetailsModel::selectColumn(int section) -{ - if (m_selectedColumn == section) - return false; - - const int columns = columnCount(); - - if (section >= columns) - section = columns - 1; - - selectRow(-1); - - const int rows = rowCount(); - const int previousColumn = m_selectedColumn; - - m_selectedColumn = section; - emit this->selectedColumnChanged(m_selectedColumn); - - auto notifySelectedDataChanged = [this, columns, rows](int notifyingColumn) { - if (notifyingColumn > -1 && notifyingColumn < columns && rows) { - emit dataChanged(index(0, notifyingColumn), - index(rows - 1, notifyingColumn), - {SelectedRole}); - } - }; - - notifySelectedDataChanged(previousColumn); - notifySelectedDataChanged(m_selectedColumn); - - return true; -} - -bool CollectionDetailsModel::renameColumn(int section, const QString &newValue) -{ - return setHeaderData(section, Qt::Horizontal, newValue); -} - -bool CollectionDetailsModel::setPropertyType(int column, const QString &newValue) -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return false); - - bool changed = m_currentCollection.setPropertyType(column, - CollectionDataTypeModel::dataTypeFromString( - newValue)); - if (changed) { - emit headerDataChanged(Qt::Horizontal, column, column); - emit dataChanged( - index(0, column), - index(rowCount() - 1, column), - {Qt::DisplayRole, Qt::EditRole, DataTypeRole, DataTypeWarningRole, ColumnDataTypeRole}); - } - - return changed; -} - -bool CollectionDetailsModel::selectRow(int row) -{ - if (m_selectedRow == row) - return false; - - const int rows = rowCount(); - - if (row >= rows) - row = rows - 1; - - selectColumn(-1); - - const int columns = columnCount(); - const int previousRow = m_selectedRow; - - m_selectedRow = row; - emit this->selectedRowChanged(m_selectedRow); - - auto notifySelectedDataChanged = [this, rows, columns](int notifyingRow) { - if (notifyingRow > -1 && notifyingRow < rows && columns) - emit dataChanged(index(notifyingRow, 0), index(notifyingRow, columns - 1), {SelectedRole}); - }; - - notifySelectedDataChanged(previousRow); - notifySelectedDataChanged(m_selectedRow); - - return true; -} - -void CollectionDetailsModel::deselectAll() -{ - selectColumn(-1); - selectRow(-1); -} - -void CollectionDetailsModel::loadCollection(const ModelNode &sourceNode, const QString &collection) -{ - QString fileName = CollectionEditorUtils::getSourceCollectionPath(sourceNode); - - CollectionReference newReference{sourceNode, collection}; - bool alreadyOpen = m_openedCollections.contains(newReference); - - if (alreadyOpen) { - if (m_currentCollection.reference() != newReference) { - deselectAll(); - beginResetModel(); - switchToCollection(newReference); - ensureSingleCell(); - endResetModel(); - } - } else { - deselectAll(); - switchToCollection(newReference); - loadJsonCollection(fileName, collection); - } -} - -void CollectionDetailsModel::removeCollection(const ModelNode &sourceNode, const QString &collection) -{ - CollectionReference collectionRef{sourceNode, collection}; - if (!m_openedCollections.contains(collectionRef)) - return; - - if (m_currentCollection.reference() == collectionRef) - loadCollection({}, {}); - - m_openedCollections.remove(collectionRef); -} - -void CollectionDetailsModel::removeAllCollections() -{ - loadCollection({}, {}); - m_openedCollections.clear(); -} - -void CollectionDetailsModel::renameCollection(const ModelNode &sourceNode, - const QString &oldName, - const QString &newName) -{ - CollectionReference oldRef{sourceNode, oldName}; - if (!m_openedCollections.contains(oldRef)) - return; - - CollectionReference newReference{sourceNode, newName}; - bool collectionIsSelected = m_currentCollection.reference() == oldRef; - CollectionDetails collection = m_openedCollections.take(oldRef); - collection.resetReference(newReference); - m_openedCollections.insert(newReference, collection); - - if (collectionIsSelected) - setCollectionName(newName); -} - -bool CollectionDetailsModel::saveDataStoreCollections() -{ - const ModelNode node = m_currentCollection.reference().node; - const Utils::FilePath path = CollectionEditorUtils::dataStoreJsonFilePath(); - Utils::FileReader fileData; - - if (!fileData.fetch(path)) { - qWarning() << Q_FUNC_INFO << "Cannot read the json file:" << fileData.errorString(); - return false; - } - - QJsonParseError jpe; - QJsonDocument document = QJsonDocument::fromJson(fileData.data(), &jpe); - - if (jpe.error == QJsonParseError::NoError) { - QJsonObject obj = document.object(); - - QList<CollectionDetails> collectionsToBeSaved; - for (CollectionDetails &openedCollection : m_openedCollections) { - const CollectionReference reference = openedCollection.reference(); - if (reference.node == node) { - obj.insert(reference.name, openedCollection.toLocalJson()); - collectionsToBeSaved << openedCollection; - } - } - - document.setObject(obj); - - if (CollectionEditorUtils::writeToJsonDocument(path, document)) { - const CollectionReference currentReference = m_currentCollection.reference(); - for (CollectionDetails &collection : collectionsToBeSaved) { - collection.markSaved(); - const CollectionReference reference = collection.reference(); - if (reference != currentReference) - closeCollectionIfSaved(reference); - } - return true; - } - } - return false; -} - -bool CollectionDetailsModel::exportCollection(const QUrl &url) -{ - using Core::EditorManager; - using Utils::FilePath; - using Utils::TextFileFormat; - - QTC_ASSERT(m_currentCollection.hasValidReference(), return false); - - bool saved = false; - const FilePath filePath = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile() - : url.toString()); - const QString saveFormat = filePath.toFileInfo().suffix().toLower(); - const QString content = saveFormat == "csv" ? m_currentCollection.toCsv() - : m_currentCollection.toJson(); - - TextFileFormat textFileFormat; - textFileFormat.codec = EditorManager::defaultTextCodec(); - textFileFormat.lineTerminationMode = EditorManager::defaultLineEnding(); - QString errorMessage; - saved = textFileFormat.writeFile(filePath, content, &errorMessage); - - if (!saved) - qWarning() << Q_FUNC_INFO << "Unable to write file" << errorMessage; - - return saved; -} - -const CollectionDetails CollectionDetailsModel::upToDateConstCollection( - const CollectionReference &reference) const -{ - using Utils::FilePath; - using Utils::FileReader; - CollectionDetails collection; - - if (m_openedCollections.contains(reference)) { - collection = m_openedCollections.value(reference); - } else { - QUrl url = CollectionEditorUtils::getSourceCollectionPath(reference.node); - FilePath path = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile() - : url.toString()); - FileReader file; - - if (!file.fetch(path)) - return collection; - - QJsonParseError jpe; - QJsonDocument document = QJsonDocument::fromJson(file.data(), &jpe); - - if (jpe.error != QJsonParseError::NoError) - return collection; - - collection = CollectionDetails::fromLocalJson(document, reference.name); - collection.resetReference(reference); - } - return collection; -} - -bool CollectionDetailsModel::collectionHasColumn(const CollectionReference &reference, - const QString &columnName) const -{ - const CollectionDetails collection = upToDateConstCollection(reference); - return collection.containsPropertyName(columnName); -} - -QString CollectionDetailsModel::getFirstColumnName(const CollectionReference &reference) const -{ - const CollectionDetails collection = upToDateConstCollection(reference); - return collection.propertyAt(0); -} - -void CollectionDetailsModel::updateEmpty() -{ - bool isEmptyNow = rowCount() == 0; - if (m_isEmpty != isEmptyNow) { - m_isEmpty = isEmptyNow; - emit isEmptyChanged(m_isEmpty); - } -} - -void CollectionDetailsModel::switchToCollection(const CollectionReference &collection) -{ - if (m_currentCollection.reference() == collection) - return; - - closeCurrentCollectionIfSaved(); - - if (!m_openedCollections.contains(collection)) - m_openedCollections.insert(collection, CollectionDetails(collection)); - - m_currentCollection = m_openedCollections.value(collection); - - setCollectionName(collection.name); -} - -void CollectionDetailsModel::closeCollectionIfSaved(const CollectionReference &collection) -{ - if (!m_openedCollections.contains(collection)) - return; - - const CollectionDetails &collectionDetails = m_openedCollections.value(collection); - - if (!collectionDetails.isChanged()) - m_openedCollections.remove(collection); -} - -void CollectionDetailsModel::closeCurrentCollectionIfSaved() -{ - if (m_currentCollection.hasValidReference()) { - closeCollectionIfSaved(m_currentCollection.reference()); - m_currentCollection = CollectionDetails{}; - } -} - -void CollectionDetailsModel::loadJsonCollection(const QString &filePath, const QString &collection) -{ - QJsonDocument document = readJsonFile(filePath); - - beginResetModel(); - m_currentCollection.resetData(document, collection); - ensureSingleCell(); - endResetModel(); -} - -void CollectionDetailsModel::ensureSingleCell() -{ - if (!m_currentCollection.hasValidReference()) - return; - - if (!columnCount()) - addColumn(0, "Column 1", "String"); - - if (!rowCount()) - insertRow(0); - - updateEmpty(); -} - -QJsonDocument CollectionDetailsModel::readJsonFile(const QUrl &url) -{ - using Utils::FilePath; - using Utils::FileReader; - FilePath path = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile() : url.toString()); - FileReader file; - - if (!file.fetch(path)) { - emit warning(tr("File reading problem"), file.errorString()); - return {}; - } - - QJsonParseError jpe; - QJsonDocument document = QJsonDocument::fromJson(file.data(), &jpe); - - if (jpe.error != QJsonParseError::NoError) - emit warning(tr("Json parse error"), jpe.errorString()); - - return document; -} - -void CollectionDetailsModel::setCollectionName(const QString &newCollectionName) -{ - if (m_collectionName != newCollectionName) { - m_collectionName = newCollectionName; - emit this->collectionNameChanged(m_collectionName); - } -} - -QString CollectionDetailsModel::warningToString(DataTypeWarning::Warning warning) const -{ - return DataTypeWarning::getDataTypeWarningString(warning); -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h deleted file mode 100644 index 24a040cce6..0000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "collectiondetails.h" - -#include <QAbstractTableModel> -#include <QHash> - -namespace QmlDesigner { - -class ModelNode; - -class CollectionDetailsModel : public QAbstractTableModel -{ - Q_OBJECT - - Q_PROPERTY(QString collectionName MEMBER m_collectionName NOTIFY collectionNameChanged) - Q_PROPERTY(int selectedColumn READ selectedColumn WRITE selectColumn NOTIFY selectedColumnChanged) - Q_PROPERTY(int selectedRow READ selectedRow WRITE selectRow NOTIFY selectedRowChanged) - Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) - -public: - enum DataRoles { SelectedRole = Qt::UserRole + 1, DataTypeRole, ColumnDataTypeRole, DataTypeWarningRole }; - explicit CollectionDetailsModel(QObject *parent = nullptr); - - QHash<int, QByteArray> roleNames() const override; - int rowCount(const QModelIndex &parent = {}) const override; - int columnCount(const QModelIndex &parent = {}) const override; - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; - bool setHeaderData(int section, - Qt::Orientation orientation, - const QVariant &value, - int role = Qt::EditRole) override; - bool insertRows(int row, int count, const QModelIndex &parent = {}) override; - bool removeColumns(int column, int count, const QModelIndex &parent = {}) override; - bool removeRows(int row, int count, const QModelIndex &parent = {}) override; - - Qt::ItemFlags flags(const QModelIndex &index) const override; - QVariant headerData(int section, - Qt::Orientation orientation, - int role = Qt::DisplayRole) const override; - - CollectionDetails::DataType propertyDataType(int column) const; - - int selectedColumn() const; - int selectedRow() const; - Q_INVOKABLE QString propertyName(int column) const; - Q_INVOKABLE QString propertyType(int column) const; - - Q_INVOKABLE bool isPropertyAvailable(const QString &name); - Q_INVOKABLE bool addColumn(int column, const QString &name, const QString &propertyType = {}); - Q_INVOKABLE bool selectColumn(int section); - Q_INVOKABLE bool renameColumn(int section, const QString &newValue); - Q_INVOKABLE bool setPropertyType(int column, const QString &newValue); - Q_INVOKABLE bool selectRow(int row); - Q_INVOKABLE void deselectAll(); - Q_INVOKABLE QString warningToString(DataTypeWarning::Warning warning) const; - - void loadCollection(const ModelNode &sourceNode, const QString &collection); - void removeCollection(const ModelNode &sourceNode, const QString &collection); - void removeAllCollections(); - void renameCollection(const ModelNode &sourceNode, const QString &oldName, const QString &newName); - - Q_INVOKABLE bool saveDataStoreCollections(); - Q_INVOKABLE bool exportCollection(const QUrl &url); - - const CollectionDetails upToDateConstCollection(const CollectionReference &reference) const; - bool collectionHasColumn(const CollectionReference &reference, const QString &columnName) const; - QString getFirstColumnName(const CollectionReference &reference) const; - -signals: - void collectionNameChanged(const QString &collectionName); - void selectedColumnChanged(int); - void selectedRowChanged(int); - void isEmptyChanged(bool); - void warning(const QString &title, const QString &body); - -private slots: - void updateEmpty(); - -private: - void switchToCollection(const CollectionReference &collection); - void closeCollectionIfSaved(const CollectionReference &collection); - void closeCurrentCollectionIfSaved(); - void setCollectionName(const QString &newCollectionName); - void loadJsonCollection(const QString &filePath, const QString &collection); - void ensureSingleCell(); - QJsonDocument readJsonFile(const QUrl &url); - - QHash<CollectionReference, CollectionDetails> m_openedCollections; - CollectionDetails m_currentCollection; - bool m_isEmpty = true; - int m_selectedColumn = -1; - int m_selectedRow = -1; - - QString m_collectionName; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp deleted file mode 100644 index f56bb36e88..0000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "collectiondetailssortfiltermodel.h" - -#include "collectiondetailsmodel.h" -#include "collectioneditorutils.h" - -#include <utils/qtcassert.h> - -namespace QmlDesigner { - -CollectionDetailsSortFilterModel::CollectionDetailsSortFilterModel(QObject *parent) - : QSortFilterProxyModel(parent) -{ - connect(this, &CollectionDetailsSortFilterModel::rowsInserted, - this, &CollectionDetailsSortFilterModel::updateRowCountChanges); - connect(this, &CollectionDetailsSortFilterModel::rowsRemoved, - this, &CollectionDetailsSortFilterModel::updateRowCountChanges); - connect(this, &CollectionDetailsSortFilterModel::modelReset, - this, &CollectionDetailsSortFilterModel::updateRowCountChanges); - - setDynamicSortFilter(true); -} - -void CollectionDetailsSortFilterModel::setSourceModel(CollectionDetailsModel *model) -{ - m_source = model; - Super::setSourceModel(model); - connect(m_source, &CollectionDetailsModel::selectedColumnChanged, - this, &CollectionDetailsSortFilterModel::updateSelectedColumn); - - connect(m_source, &CollectionDetailsModel::selectedRowChanged, - this, &CollectionDetailsSortFilterModel::updateSelectedRow); -} - -int CollectionDetailsSortFilterModel::selectedRow() const -{ - QTC_ASSERT(m_source, return -1); - - return mapFromSource(m_source->index(m_source->selectedRow(), 0)).row(); -} - -int CollectionDetailsSortFilterModel::selectedColumn() const -{ - QTC_ASSERT(m_source, return -1); - - return mapFromSource(m_source->index(0, m_source->selectedColumn())).column(); -} - -bool CollectionDetailsSortFilterModel::selectRow(int row) -{ - QTC_ASSERT(m_source, return false); - - return m_source->selectRow(mapToSource(index(row, 0)).row()); -} - -bool CollectionDetailsSortFilterModel::selectColumn(int column) -{ - QTC_ASSERT(m_source, return false); - - return m_source->selectColumn(mapToSource(index(0, column)).column()); -} - -CollectionDetailsSortFilterModel::~CollectionDetailsSortFilterModel() = default; - -bool CollectionDetailsSortFilterModel::filterAcceptsRow(int sourceRow, - const QModelIndex &sourceParent) const -{ - QTC_ASSERT(m_source, return false); - QModelIndex sourceIndex(m_source->index(sourceRow, 0, sourceParent)); - return sourceIndex.isValid(); -} - -bool CollectionDetailsSortFilterModel::lessThan(const QModelIndex &sourceleft, - const QModelIndex &sourceRight) const -{ - QTC_ASSERT(m_source, return false); - - if (sourceleft.column() == sourceRight.column()) { - int column = sourceleft.column(); - CollectionDetails::DataType columnType = m_source->propertyDataType(column); - return CollectionEditorUtils::variantIslessThan(sourceleft.data(), - sourceRight.data(), - columnType); - } - - return false; -} - -void CollectionDetailsSortFilterModel::updateEmpty() -{ - bool newValue = rowCount() == 0; - if (m_isEmpty != newValue) { - m_isEmpty = newValue; - emit isEmptyChanged(m_isEmpty); - } -} - -void CollectionDetailsSortFilterModel::updateSelectedRow() -{ - const int upToDateSelectedRow = selectedRow(); - if (m_selectedRow == upToDateSelectedRow) - return; - - const int rows = rowCount(); - const int columns = columnCount(); - const int previousRow = m_selectedRow; - - m_selectedRow = upToDateSelectedRow; - emit this->selectedRowChanged(m_selectedRow); - - auto notifySelectedDataChanged = [this, rows, columns](int notifyingRow) { - if (notifyingRow > -1 && notifyingRow < rows && columns) { - emit dataChanged(index(notifyingRow, 0), - index(notifyingRow, columns - 1), - {CollectionDetailsModel::SelectedRole}); - } - }; - - notifySelectedDataChanged(previousRow); - notifySelectedDataChanged(m_selectedRow); -} - -void CollectionDetailsSortFilterModel::updateSelectedColumn() -{ - const int upToDateSelectedColumn = selectedColumn(); - if (m_selectedColumn == upToDateSelectedColumn) - return; - - const int rows = rowCount(); - const int columns = columnCount(); - const int previousColumn = m_selectedColumn; - - m_selectedColumn = upToDateSelectedColumn; - emit this->selectedColumnChanged(m_selectedColumn); - - auto notifySelectedDataChanged = [this, rows, columns](int notifyingCol) { - if (notifyingCol > -1 && notifyingCol < columns && rows) { - emit dataChanged(index(0, notifyingCol), - index(rows - 1, notifyingCol), - {CollectionDetailsModel::SelectedRole}); - } - }; - - notifySelectedDataChanged(previousColumn); - notifySelectedDataChanged(m_selectedColumn); -} - -void CollectionDetailsSortFilterModel::updateRowCountChanges() -{ - updateEmpty(); - updateSelectedRow(); - invalidate(); -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h deleted file mode 100644 index 93305f3ca2..0000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <QPointer> -#include <QSortFilterProxyModel> - -namespace QmlDesigner { - -class CollectionDetailsModel; - -class CollectionDetailsSortFilterModel : public QSortFilterProxyModel -{ - Q_OBJECT - - Q_PROPERTY(int selectedColumn READ selectedColumn WRITE selectColumn NOTIFY selectedColumnChanged) - Q_PROPERTY(int selectedRow READ selectedRow WRITE selectRow NOTIFY selectedRowChanged) - Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) - - using Super = QSortFilterProxyModel; - -public: - explicit CollectionDetailsSortFilterModel(QObject *parent = nullptr); - virtual ~CollectionDetailsSortFilterModel(); - - void setSourceModel(CollectionDetailsModel *model); - - int selectedRow() const; - int selectedColumn() const; - - Q_INVOKABLE bool selectRow(int row); - Q_INVOKABLE bool selectColumn(int column); - -signals: - void selectedColumnChanged(int); - void selectedRowChanged(int); - void isEmptyChanged(bool); - -protected: - using Super::setSourceModel; - bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; - bool lessThan(const QModelIndex &sourceleft, const QModelIndex &sourceRight) const override; - -private: - void updateEmpty(); - void updateSelectedRow(); - void updateSelectedColumn(); - void updateRowCountChanges(); - - QPointer<CollectionDetailsModel> m_source; - int m_selectedColumn = -1; - int m_selectedRow = -1; - bool m_isEmpty = true; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h deleted file mode 100644 index 76524762ed..0000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -namespace QmlDesigner::CollectionEditorConstants { - -enum class SourceFormat { Unknown, Json }; - -inline constexpr char SOURCEFILE_PROPERTY[] = "source"; -inline constexpr char ALLMODELS_PROPERTY[] = "allModels"; -inline constexpr char JSONCHILDMODELNAME_PROPERTY[] = "modelName"; - -inline constexpr char COLLECTIONMODEL_IMPORT[] = "QtQuick.Studio.Utils"; -inline constexpr char JSONCOLLECTIONMODEL_TYPENAME[] = "QtQuick.Studio.Utils.JsonListModel"; -inline constexpr char JSONCOLLECTIONCHILDMODEL_TYPENAME[] = "QtQuick.Studio.Utils.ChildListModel"; -inline constexpr char JSONBACKEND_TYPENAME[] = "JsonData"; - -} // namespace QmlDesigner::CollectionEditorConstants diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp deleted file mode 100644 index 4725987f12..0000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp +++ /dev/null @@ -1,340 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "collectioneditorutils.h" - -#include "model.h" -#include "nodemetainfo.h" -#include "propertymetainfo.h" - -#include <coreplugin/documentmanager.h> -#include <coreplugin/icore.h> -#include <projectexplorer/project.h> -#include <projectexplorer/projectexplorer.h> -#include <projectexplorer/projectmanager.h> -#include <qmljs/qmljsmodelmanagerinterface.h> -#include <utils/qtcassert.h> - -#include <variant> - -#include <QColor> -#include <QJsonArray> -#include <QJsonDocument> -#include <QJsonObject> -#include <QJsonParseError> -#include <QJsonValue> -#include <QUrl> - -using DataType = QmlDesigner::CollectionDetails::DataType; - -namespace { - -using CollectionDataVariant = std::variant<QString, bool, double, int, QUrl, QColor>; - -inline bool operator<(const QColor &a, const QColor &b) -{ - return a.name(QColor::HexArgb) < b.name(QColor::HexArgb); -} - -inline CollectionDataVariant valueToVariant(const QVariant &value, DataType type) -{ - switch (type) { - case DataType::String: - return value.toString(); - case DataType::Real: - return value.toDouble(); - case DataType::Integer: - return value.toInt(); - case DataType::Boolean: - return value.toBool(); - case DataType::Color: - return value.value<QColor>(); - case DataType::Image: - case DataType::Url: - return value.value<QUrl>(); - default: - return false; - } -} - -struct LessThanVisitor -{ - template<typename T1, typename T2> - bool operator()(const T1 &a, const T2 &b) const - { - return CollectionDataVariant(a).index() < CollectionDataVariant(b).index(); - } - - template<typename T> - bool operator()(const T &a, const T &b) const - { - return a < b; - } -}; - -Utils::FilePath findFile(const Utils::FilePath &path, const QString &fileName) -{ - QDirIterator it(path.toString(), QDirIterator::Subdirectories); - - while (it.hasNext()) { - QFileInfo file(it.next()); - if (file.isDir()) - continue; - - if (file.fileName() == fileName) - return Utils::FilePath::fromFileInfo(file); - } - return {}; -} - -Utils::FilePath dataStoreDir() -{ - using Utils::FilePath; - ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectManager::startupProject(); - - if (!currentProject) - return {}; - - return currentProject->projectDirectory().pathAppended("/imports/" - + currentProject->displayName()); -} - -inline Utils::FilePath collectionPath(const QString &filePath) -{ - return dataStoreDir().pathAppended("/" + filePath); -} - -inline Utils::FilePath qmlDirFilePath() -{ - return collectionPath("qmldir"); -} - -} // namespace - -namespace QmlDesigner::CollectionEditorUtils { - -bool variantIslessThan(const QVariant &a, const QVariant &b, DataType type) -{ - return std::visit(LessThanVisitor{}, valueToVariant(a, type), valueToVariant(b, type)); -} - -QString getSourceCollectionType(const ModelNode &node) -{ - using namespace QmlDesigner; - if (node.type() == CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME) - return "json"; - - return {}; -} - -Utils::FilePath dataStoreJsonFilePath() -{ - return collectionPath("models.json"); -} - -Utils::FilePath dataStoreQmlFilePath() -{ - return collectionPath("DataStore.qml"); -} - -bool canAcceptCollectionAsModel(const ModelNode &node) -{ - const NodeMetaInfo nodeMetaInfo = node.metaInfo(); - if (!nodeMetaInfo.isValid()) - return false; - - const PropertyMetaInfo modelProperty = nodeMetaInfo.property("model"); - if (!modelProperty.isValid()) - return false; - - return modelProperty.isWritable() && !modelProperty.isPrivate() - && modelProperty.propertyType().isVariant(); -} - -bool hasTextRoleProperty(const ModelNode &node) -{ - const NodeMetaInfo nodeMetaInfo = node.metaInfo(); - if (!nodeMetaInfo.isValid()) - return false; - - const PropertyMetaInfo textRoleProperty = nodeMetaInfo.property("textRole"); - if (!textRoleProperty.isValid()) - return false; - - return textRoleProperty.isWritable() && !textRoleProperty.isPrivate() - && textRoleProperty.propertyType().isString(); -} - -QString getSourceCollectionPath(const ModelNode &dataStoreNode) -{ - using Utils::FilePath; - if (!dataStoreNode.isValid()) - return {}; - - const FilePath expectedFile = dataStoreJsonFilePath(); - - if (expectedFile.exists()) - return expectedFile.toFSPathString(); - - return {}; -} - -bool isDataStoreNode(const ModelNode &dataStoreNode) -{ - using Utils::FilePath; - - if (!dataStoreNode.isValid()) - return false; - - const FilePath expectedFile = dataStoreQmlFilePath(); - - if (!expectedFile.exists()) - return false; - - FilePath modelPath = FilePath::fromUserInput(dataStoreNode.model()->fileUrl().toLocalFile()); - - return modelPath.isSameFile(expectedFile); -} - -bool ensureDataStoreExists(bool &justCreated) -{ - using Utils::FilePath; - using Utils::FileReader; - using Utils::FileSaver; - - FilePath qmlDestinationPath = dataStoreQmlFilePath(); - justCreated = false; - - auto extractDependency = [&justCreated](const FilePath &filePath) -> bool { - if (filePath.exists()) - return true; - - const QString templateFileName = filePath.fileName() + u".tpl"; - const FilePath templatePath = findFile(Core::ICore::resourcePath(), templateFileName); - if (!templatePath.exists()) { - qWarning() << Q_FUNC_INFO << __LINE__ << templateFileName << "does not exist"; - return false; - } - - if (!filePath.parentDir().ensureWritableDir()) { - qWarning() << Q_FUNC_INFO << __LINE__ << "Cannot create directory" - << filePath.parentDir(); - return false; - } - - if (templatePath.copyFile(filePath)) { - justCreated = true; - return true; - } - - qWarning() << Q_FUNC_INFO << __LINE__ << "Cannot copy" << templateFileName << "to" << filePath; - return false; - }; - - if (!extractDependency(dataStoreJsonFilePath())) - return false; - - if (!extractDependency(collectionPath("data.json"))) - return false; - - if (!extractDependency(collectionPath("JsonData.qml"))) - return false; - - if (!qmlDestinationPath.exists()) { - if (qmlDestinationPath.ensureExistingFile()) { - justCreated = true; - } else { - qWarning() << Q_FUNC_INFO << __LINE__ << "Can't create DataStore Qml File"; - return false; - } - } - - FilePath qmlDirPath = qmlDirFilePath(); - qmlDirPath.ensureExistingFile(); - - FileReader qmlDirReader; - if (!qmlDirReader.fetch(qmlDirPath)) { - qWarning() << Q_FUNC_INFO << __LINE__ << "Can't read the content of the qmldir"; - return false; - } - - QByteArray qmlDirContent = qmlDirReader.data(); - const QList<QByteArray> qmlDirLines = qmlDirContent.split('\n'); - for (const QByteArray &line : qmlDirLines) { - if (line.startsWith("singleton DataStore ")) - return true; - } - - if (!qmlDirContent.isEmpty() && qmlDirContent.back() != '\n') - qmlDirContent.append("\n"); - qmlDirContent.append("singleton DataStore 1.0 DataStore.qml\n"); - - FileSaver qmlDirSaver(qmlDirPath); - qmlDirSaver.write(qmlDirContent); - - if (qmlDirSaver.finalize()) { - justCreated = true; - return true; - } - - qWarning() << Q_FUNC_INFO << __LINE__ << "Can't write to the qmldir file"; - return false; -} - -QJsonObject defaultCollection() -{ - QJsonObject collectionObject; - - QJsonArray columns; - QJsonObject defaultColumn; - defaultColumn.insert("name", "Column 1"); - defaultColumn.insert("type", "string"); - columns.append(defaultColumn); - - QJsonArray collectionData; - QJsonArray cellData; - cellData.append(QString{}); - collectionData.append(cellData); - - collectionObject.insert("columns", columns); - collectionObject.insert("data", collectionData); - - return collectionObject; -} - -QJsonObject defaultColorCollection() -{ - using Utils::FilePath; - using Utils::FileReader; - const FilePath templatePath = findFile(Core::ICore::resourcePath(), "Colors.json.tpl"); - - FileReader fileReader; - if (!fileReader.fetch(templatePath)) { - qWarning() << Q_FUNC_INFO << __LINE__ << "Can't read the content of the file" << templatePath; - return {}; - } - - QJsonParseError parseError; - const CollectionDetails collection = CollectionDetails::fromImportedJson(fileReader.data(), - &parseError); - if (parseError.error != QJsonParseError::NoError) { - qWarning() << Q_FUNC_INFO << __LINE__ << "Error in template file" << parseError.errorString(); - return {}; - } - - return collection.toLocalJson(); -} - -bool writeToJsonDocument(const Utils::FilePath &path, const QJsonDocument &document, QString *errorString) -{ - Core::FileChangeBlocker fileBlocker(path); - Utils::FileSaver jsonFile(path); - if (jsonFile.write(document.toJson())) - jsonFile.finalize(); - if (errorString) - *errorString = jsonFile.errorString(); - - return !jsonFile.hasError(); -} - -} // namespace QmlDesigner::CollectionEditorUtils diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h deleted file mode 100644 index 355addf59b..0000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "collectiondetails.h" -#include "collectioneditorconstants.h" - -QT_BEGIN_NAMESPACE -class QJsonArray; -class QJsonObject; -QT_END_NAMESPACE - -namespace Utils { -class FilePath; -} - -namespace QmlDesigner::CollectionEditorUtils { - -bool variantIslessThan(const QVariant &a, const QVariant &b, CollectionDetails::DataType type); - -QString getSourceCollectionType(const QmlDesigner::ModelNode &node); - -QString getSourceCollectionPath(const QmlDesigner::ModelNode &dataStoreNode); - -Utils::FilePath dataStoreJsonFilePath(); - -Utils::FilePath dataStoreQmlFilePath(); - -bool writeToJsonDocument(const Utils::FilePath &path, - const QJsonDocument &document, - QString *errorString = nullptr); - -bool isDataStoreNode(const ModelNode &dataStoreNode); - -bool ensureDataStoreExists(bool &justCreated); - -bool canAcceptCollectionAsModel(const ModelNode &node); - -bool hasTextRoleProperty(const ModelNode &node); - -QJsonObject defaultCollection(); - -QJsonObject defaultColorCollection(); - -} // namespace QmlDesigner::CollectionEditorUtils diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp deleted file mode 100644 index d27a077d2a..0000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp +++ /dev/null @@ -1,521 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "collectionlistmodel.h" - -#include "collectioneditorutils.h" - -#include <utils/algorithm.h> -#include <utils/fileutils.h> -#include <utils/qtcassert.h> - -#include <QJsonDocument> -#include <QJsonObject> -#include <QJsonParseError> - -namespace { - -template<typename ValueType> -bool containsItem(const std::initializer_list<ValueType> &container, const ValueType &value) -{ - auto begin = std::cbegin(container); - auto end = std::cend(container); - - auto it = std::find(begin, end, value); - return it != end; -} - -bool sameCollectionNames(QStringList a, QStringList b) -{ - if (a.size() != b.size()) - return false; - - a.sort(Qt::CaseSensitive); - b.sort(Qt::CaseSensitive); - - return a == b; -} - -} // namespace - -namespace QmlDesigner { - -CollectionListModel::CollectionListModel() - : QAbstractListModel() -{ - connect(this, &CollectionListModel::modelReset, this, &CollectionListModel::updateEmpty); - connect(this, &CollectionListModel::rowsRemoved, this, &CollectionListModel::updateEmpty); - connect(this, &CollectionListModel::rowsInserted, this, &CollectionListModel::updateEmpty); -} - -QHash<int, QByteArray> CollectionListModel::roleNames() const -{ - static QHash<int, QByteArray> roles; - if (roles.isEmpty()) { - roles.insert(Super::roleNames()); - roles.insert({ - {IdRole, "collectionId"}, - {NameRole, "collectionName"}, - {SelectedRole, "collectionIsSelected"}, - }); - } - return roles; -} - -int CollectionListModel::rowCount([[maybe_unused]] const QModelIndex &parent) const -{ - return m_data.count(); -} - -bool CollectionListModel::setData(const QModelIndex &index, const QVariant &value, int role) -{ - if (!index.isValid()) - return false; - - if (containsItem<int>({Qt::EditRole, Qt::DisplayRole, NameRole}, role)) { - if (collectionExists(value.toString())) - return false; - - QString oldName = collectionNameAt(index.row()); - bool nameChanged = value != data(index); - if (nameChanged) { - QString newName = value.toString(); - QString errorString; - if (renameCollectionInDataStore(oldName, newName, errorString)) { - m_data.replace(index.row(), newName); - emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole, NameRole}); - emit this->collectionNameChanged(oldName, newName); - if (m_selectedCollectionName == oldName) - updateSelectedCollectionName(); - return true; - } else { - emit warning("Rename Model", errorString); - return false; - } - } - } else if (role == SelectedRole) { - if (value.toBool() != index.data(SelectedRole).toBool()) { - setSelectedIndex(value.toBool() ? index.row() : -1); - return true; - } - } - return false; -} - -bool CollectionListModel::removeRows(int row, int count, const QModelIndex &parent) -{ - const int rows = rowCount(parent); - if (row >= rows) - return false; - - row = qBound(0, row, rows - 1); - count = qBound(0, count, rows - row); - - if (count < 1) - return false; - - QString errorString; - QStringList removedCollections = m_data.mid(row, count); - if (removeCollectionsFromDataStore(removedCollections, errorString)) { - beginRemoveRows(parent, row, row + count - 1); - m_data.remove(row, count); - endRemoveRows(); - - emit collectionsRemoved(removedCollections); - if (m_selectedIndex >= row) { - int preferredIndex = m_selectedIndex - count; - if (preferredIndex < 0) // If the selected item is deleted, reset selection - selectCollectionIndex(-1); - selectCollectionIndex(preferredIndex, true); - } - - updateSelectedCollectionName(); - return true; - } else { - emit warning("Remove Model", errorString); - return false; - } -} - -QVariant CollectionListModel::data(const QModelIndex &index, int role) const -{ - QTC_ASSERT(index.isValid(), return {}); - - switch (role) { - case IdRole: - return index.row(); - case SelectedRole: - return index.row() == m_selectedIndex; - case NameRole: - default: - return m_data.at(index.row()); - } -} - -void CollectionListModel::setDataStoreNode(const ModelNode &dataStoreNode) -{ - m_dataStoreNode = dataStoreNode; - update(); -} - -int CollectionListModel::selectedIndex() const -{ - return m_selectedIndex; -} - -ModelNode CollectionListModel::sourceNode() const -{ - return m_dataStoreNode; -} - -bool CollectionListModel::collectionExists(const QString &collectionName) const -{ - return m_data.contains(collectionName); -} - -QStringList CollectionListModel::collections() const -{ - return m_data; -} - -QString CollectionListModel::getUniqueCollectionName(const QString &baseName) const -{ - QString name = baseName.isEmpty() ? "Model" : baseName; - QString nameTemplate = name + "%1"; - - int num = 0; - - while (collectionExists(name)) - name = nameTemplate.arg(++num, 2, 10, QChar('0')); - - return name; -} - -void CollectionListModel::selectCollectionIndex(int idx, bool selectAtLeastOne) -{ - int collectionCount = m_data.size(); - int preferredIndex = -1; - if (collectionCount) { - if (selectAtLeastOne) - preferredIndex = std::max(0, std::min(idx, collectionCount - 1)); - else if (idx > -1 && idx < collectionCount) - preferredIndex = idx; - } - - setSelectedIndex(preferredIndex); -} - -void CollectionListModel::selectCollectionName(QString collectionName, bool selectAtLeastOne) -{ - int idx = m_data.indexOf(collectionName); - if (idx > -1) - selectCollectionIndex(idx); - else - selectCollectionIndex(selectedIndex(), selectAtLeastOne); - - collectionName = collectionNameAt(selectedIndex()); - if (m_selectedCollectionName == collectionName) - return; - - m_selectedCollectionName = collectionName; - emit selectedCollectionNameChanged(m_selectedCollectionName); -} - -QString CollectionListModel::collectionNameAt(int idx) const -{ - return index(idx).data(NameRole).toString(); -} - -QString CollectionListModel::selectedCollectionName() const -{ - return m_selectedCollectionName; -} - -void CollectionListModel::update() -{ - using Utils::FilePath; - using Utils::FileReader; - - FileReader sourceFile; - QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(m_dataStoreNode); - FilePath path = FilePath::fromUserInput(sourceFileAddress); - bool fileRead = false; - if (path.exists()) { - fileRead = sourceFile.fetch(path); - if (!fileRead) - emit this->warning(tr("Model Editor"), - tr("Cannot read the dataStore file\n%1").arg(sourceFile.errorString())); - } - - QStringList collectionNames; - if (fileRead) { - QJsonParseError parseError; - QJsonDocument document = QJsonDocument::fromJson(sourceFile.data(), &parseError); - if (parseError.error != QJsonParseError::NoError) { - emit this->warning(tr("Model Editor"), - tr("There is an error in the JSON file.\n%1") - .arg(parseError.errorString())); - } else { - if (document.isObject()) - collectionNames = document.object().toVariantMap().keys(); - else - emit this->warning(tr("Model Editor"), tr("The JSON document be an object.")); - } - } - - if (!sameCollectionNames(m_data, collectionNames)) { - QString prevSelectedCollection = selectedIndex() > -1 ? m_data.at(selectedIndex()) - : QString(); - beginResetModel(); - m_data = collectionNames; - endResetModel(); - emit this->collectionNamesChanged(collections()); - selectCollectionName(prevSelectedCollection, true); - } -} - -bool CollectionListModel::addCollection(const QString &collectionName, - const QJsonObject &localCollection) -{ - if (collectionExists(collectionName)) { - emit warning(tr("Add Model"), tr("Model \"%1\" already exists.").arg(collectionName)); - return false; - } - - QString errorMessage; - if (addCollectionToDataStore(collectionName, localCollection, errorMessage)) { - int row = rowCount(); - beginInsertRows({}, row, row); - m_data.append(collectionName); - endInsertRows(); - - selectCollectionName(collectionName); - emit collectionAdded(collectionName); - return true; - } else { - emit warning(tr("Add Collection"), errorMessage); - } - return false; -} - -void CollectionListModel::setSelectedIndex(int idx) -{ - idx = (idx > -1 && idx < rowCount()) ? idx : -1; - - if (m_selectedIndex != idx) { - QModelIndex previousIndex = index(m_selectedIndex); - QModelIndex newIndex = index(idx); - - m_selectedIndex = idx; - - if (previousIndex.isValid()) - emit dataChanged(previousIndex, previousIndex, {SelectedRole}); - - if (newIndex.isValid()) - emit dataChanged(newIndex, newIndex, {SelectedRole}); - - emit selectedIndexChanged(idx); - updateSelectedCollectionName(); - } -} - -bool CollectionListModel::removeCollectionsFromDataStore(const QStringList &removedCollections, - QString &error) const -{ - using Utils::FilePath; - using Utils::FileReader; - auto setErrorAndReturn = [&error](const QString &msg) -> bool { - error = msg; - return false; - }; - - if (m_dataStoreNode.type() != CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME) - return setErrorAndReturn(tr("Invalid node type")); - - QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(m_dataStoreNode); - - QFileInfo sourceFileInfo(sourceFileAddress); - if (!sourceFileInfo.isFile()) - return setErrorAndReturn(tr("The selected node has an invalid source address")); - - FilePath jsonPath = FilePath::fromUserInput(sourceFileAddress); - FileReader jsonFile; - if (!jsonFile.fetch(jsonPath)) { - return setErrorAndReturn(tr("Can't read file \"%1\".\n%2") - .arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString())); - } - - QJsonParseError parseError; - QJsonDocument document = QJsonDocument::fromJson(jsonFile.data(), &parseError); - if (parseError.error != QJsonParseError::NoError) { - return setErrorAndReturn(tr("\"%1\" is corrupted.\n%2") - .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString())); - } - - if (document.isObject()) { - QJsonObject rootObject = document.object(); - - for (const QString &collectionName : removedCollections) { - bool sourceContainsCollection = rootObject.contains(collectionName); - if (sourceContainsCollection) { - rootObject.remove(collectionName); - } else { - setErrorAndReturn(tr("The model group doesn't contain the model name (%1).") - .arg(sourceContainsCollection)); - } - } - - document.setObject(rootObject); - - if (CollectionEditorUtils::writeToJsonDocument(jsonPath, document)) { - error.clear(); - return true; - } else { - return setErrorAndReturn( - tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath())); - } - } else { - return setErrorAndReturn(tr("Local Json Document should be an object")); - } - - return false; -} - -bool CollectionListModel::renameCollectionInDataStore(const QString &oldName, - const QString &newName, - QString &error) -{ - using Utils::FilePath; - using Utils::FileReader; - using Utils::FileSaver; - - auto setErrorAndReturn = [&error](const QString &msg) -> bool { - error = msg; - return false; - }; - - if (m_dataStoreNode.type() != CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME) - return setErrorAndReturn(tr("Invalid node type")); - - QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(m_dataStoreNode); - - QFileInfo sourceFileInfo(sourceFileAddress); - if (!sourceFileInfo.isFile()) - return setErrorAndReturn(tr("Selected node must have a valid source file address")); - - FilePath jsonPath = FilePath::fromUserInput(sourceFileAddress); - FileReader jsonFile; - if (!jsonFile.fetch(jsonPath)) { - return setErrorAndReturn( - tr("Can't read \"%1\".\n%2").arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString())); - } - - QJsonParseError parseError; - QJsonDocument document = QJsonDocument::fromJson(jsonFile.data(), &parseError); - if (parseError.error != QJsonParseError::NoError) { - return setErrorAndReturn(tr("\"%1\" is corrupted.\n%2") - .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString())); - } - - if (document.isObject()) { - QJsonObject rootObject = document.object(); - - bool collectionContainsOldName = rootObject.contains(oldName); - bool collectionContainsNewName = rootObject.contains(newName); - - if (!collectionContainsOldName) { - return setErrorAndReturn( - tr("The model group doesn't contain the old model name (%1).").arg(oldName)); - } - - if (collectionContainsNewName) { - return setErrorAndReturn( - tr("The model name \"%1\" already exists in the model group.").arg(newName)); - } - - QJsonValue oldValue = rootObject.value(oldName); - rootObject.insert(newName, oldValue); - rootObject.remove(oldName); - - document.setObject(rootObject); - - if (CollectionEditorUtils::writeToJsonDocument(jsonPath, document)) { - error.clear(); - return true; - } else { - return setErrorAndReturn( - tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath())); - } - } else { - return setErrorAndReturn(tr("Local Json Document should be an object")); - } - return false; -} - -bool CollectionListModel::addCollectionToDataStore(const QString &collectionName, - const QJsonObject &localCollection, - QString &errorString) const -{ - using Utils::FilePath; - using Utils::FileReader; - auto returnError = [&errorString](const QString &msg) -> bool { - errorString = msg; - return false; - }; - - if (collectionExists(collectionName)) - return returnError(tr("A model with the identical name already exists.")); - - QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(m_dataStoreNode); - - QFileInfo sourceFileInfo(sourceFileAddress); - if (!sourceFileInfo.isFile()) - return returnError(tr("Selected node must have a valid source file address")); - - FilePath jsonPath = FilePath::fromUserInput(sourceFileAddress); - FileReader jsonFile; - if (!jsonFile.fetch(jsonPath)) { - return returnError( - tr("Can't read \"%1\".\n%2").arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString())); - } - - QJsonParseError parseError; - QJsonDocument document = QJsonDocument::fromJson(jsonFile.data(), &parseError); - if (parseError.error != QJsonParseError::NoError) - return returnError(tr("\"%1\" is corrupted.\n%2") - .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString())); - - if (document.isObject()) { - QJsonObject sourceObject = document.object(); - sourceObject.insert(collectionName, localCollection); - document.setObject(sourceObject); - - if (CollectionEditorUtils::writeToJsonDocument(jsonPath, document)) - return true; - else - return returnError(tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath())); - } else { - return returnError(tr("JSON document type should be an object containing models.")); - } -} - -void CollectionListModel::updateEmpty() -{ - bool isEmptyNow = m_data.isEmpty(); - if (m_isEmpty != isEmptyNow) { - m_isEmpty = isEmptyNow; - emit isEmptyChanged(m_isEmpty); - - if (m_isEmpty) - setSelectedIndex(-1); - } -} - -void CollectionListModel::updateSelectedCollectionName() -{ - QString selectedCollectionByIndex = collectionNameAt(selectedIndex()); - if (selectedCollectionByIndex != selectedCollectionName()) - selectCollectionName(selectedCollectionByIndex); -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h deleted file mode 100644 index 7902fd5909..0000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <QAbstractListModel> -#include <QHash> - -#include "modelnode.h" - -namespace QmlDesigner { - -class CollectionListModel : public QAbstractListModel -{ - Q_OBJECT - - Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged) - Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) - Q_PROPERTY(QString selectedCollectionName - READ selectedCollectionName - WRITE selectCollectionName - NOTIFY selectedCollectionNameChanged) - -public: - enum Roles { IdRole = Qt::UserRole + 1, NameRole, SelectedRole }; - - explicit CollectionListModel(); - QHash<int, QByteArray> roleNames() const override; - - int rowCount(const QModelIndex &parent = QModelIndex()) const override; - bool setData(const QModelIndex &index, const QVariant &value, int role) override; - bool removeRows(int row, int count, const QModelIndex &parent = {}) override; - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - void setDataStoreNode(const ModelNode &dataStoreNode = {}); - - Q_INVOKABLE int selectedIndex() const; - Q_INVOKABLE ModelNode sourceNode() const; - Q_INVOKABLE bool collectionExists(const QString &collectionName) const; - Q_INVOKABLE QStringList collections() const; - Q_INVOKABLE QString getUniqueCollectionName(const QString &baseName = {}) const; - - void selectCollectionIndex(int idx, bool selectAtLeastOne = false); - void selectCollectionName(QString collectionName, bool selectAtLeastOne = false); - QString collectionNameAt(int idx) const; - QString selectedCollectionName() const; - - void update(); - bool addCollection(const QString &collectionName, const QJsonObject &localCollection); - -signals: - void selectedIndexChanged(int idx); - void isEmptyChanged(bool); - void collectionNameChanged(const QString &oldName, const QString &newName); - void collectionNamesChanged(const QStringList &collectionNames); - void collectionsRemoved(const QStringList &names); - void collectionAdded(const QString &name); - void selectedCollectionNameChanged(const QString &selectedCollectionName); - void warning(const QString &title, const QString &body); - -private: - void setSelectedIndex(int idx); - bool removeCollectionsFromDataStore(const QStringList &removedCollections, QString &error) const; - bool renameCollectionInDataStore(const QString &oldName, const QString &newName, QString &error); - bool addCollectionToDataStore(const QString &collectionName, - const QJsonObject &localCollection, - QString &errorString) const; - - void updateEmpty(); - void updateSelectedCollectionName(); - - using Super = QAbstractListModel; - int m_selectedIndex = -1; - bool m_isEmpty = false; - ModelNode m_dataStoreNode; - QString m_selectedCollectionName; - QStringList m_data; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp deleted file mode 100644 index f6ec821fde..0000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ /dev/null @@ -1,469 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "collectionview.h" - -#include "collectiondatatypemodel.h" -#include "collectiondetailsmodel.h" -#include "collectioneditorconstants.h" -#include "collectioneditorutils.h" -#include "collectionlistmodel.h" -#include "collectionwidget.h" -#include "datastoremodelnode.h" -#include "designmodecontext.h" -#include "nodeabstractproperty.h" -#include "nodemetainfo.h" -#include "nodeproperty.h" -#include "qmldesignerplugin.h" -#include "variantproperty.h" - -#include <projectexplorer/project.h> -#include <projectexplorer/projectexplorer.h> -#include <projectexplorer/projectmanager.h> -#include <qmljs/qmljsmodelmanagerinterface.h> - -#include <coreplugin/icore.h> -#include <utils/algorithm.h> -#include <utils/qtcassert.h> - -#include <QTimer> - -namespace { - -bool isStudioCollectionModel(const QmlDesigner::ModelNode &node) -{ - return node.metaInfo().isQtQuickStudioUtilsJsonListModel(); -} - -inline void setVariantPropertyValue(const QmlDesigner::ModelNode &node, - const QmlDesigner::PropertyName &propertyName, - const QVariant &value) -{ - QmlDesigner::VariantProperty property = node.variantProperty(propertyName); - property.setValue(value); -} - -inline void setBindingPropertyExpression(const QmlDesigner::ModelNode &node, - const QmlDesigner::PropertyName &propertyName, - const QString &expression) -{ - QmlDesigner::BindingProperty property = node.bindingProperty(propertyName); - property.setExpression(expression); -} - -} // namespace - -namespace QmlDesigner { - -CollectionView::CollectionView(ExternalDependenciesInterface &externalDependencies) - : AbstractView(externalDependencies) - , m_dataStore(std::make_unique<DataStoreModelNode>()) - -{ - connect(ProjectExplorer::ProjectManager::instance(), - &ProjectExplorer::ProjectManager::startupProjectChanged, this, [this] { - resetDataStoreNode(); - if (m_widget.get()) - m_widget->collectionDetailsModel()->removeAllCollections(); - }); -} - -bool CollectionView::hasWidget() const -{ - return true; -} - -QmlDesigner::WidgetInfo CollectionView::widgetInfo() -{ - if (m_widget.isNull()) { - m_widget = new CollectionWidget(this); - m_widget->setMinimumSize(m_widget->minimumSizeHint()); - - auto collectionEditorContext = new Internal::CollectionEditorContext(m_widget.data()); - Core::ICore::addContextObject(collectionEditorContext); - CollectionListModel *listModel = m_widget->listModel().data(); - - connect(listModel, - &CollectionListModel::selectedCollectionNameChanged, - this, - [this](const QString &collection) { - m_widget->collectionDetailsModel()->loadCollection(dataStoreNode(), collection); - }); - - connect(listModel, &CollectionListModel::isEmptyChanged, this, [this](bool isEmpty) { - if (isEmpty) - m_widget->collectionDetailsModel()->loadCollection({}, {}); - }); - - connect(listModel, &CollectionListModel::modelReset, this, [this] { - CollectionListModel *listModel = m_widget->listModel().data(); - if (listModel->sourceNode() == m_dataStore->modelNode()) - m_dataStore->setCollectionNames(listModel->collections()); - }); - - connect(listModel, - &CollectionListModel::collectionAdded, - this, - [this](const QString &collectionName) { m_dataStore->addCollection(collectionName); }); - - connect(listModel, - &CollectionListModel::collectionNameChanged, - this, - [this](const QString &oldName, const QString &newName) { - m_dataStore->renameCollection(oldName, newName); - m_widget->collectionDetailsModel()->renameCollection(dataStoreNode(), - oldName, - newName); - }); - - connect(listModel, - &CollectionListModel::collectionsRemoved, - this, - [this](const QStringList &collectionNames) { - m_dataStore->removeCollections(collectionNames); - for (const QString &collectionName : collectionNames) { - m_widget->collectionDetailsModel()->removeCollection(dataStoreNode(), - collectionName); - } - }); - } - - return createWidgetInfo(m_widget.data(), - "CollectionEditor", - WidgetInfo::LeftPane, - 0, - tr("Model Editor [beta]"), - tr("Model Editor view")); -} - -void CollectionView::modelAttached(Model *model) -{ - AbstractView::modelAttached(model); - 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()); - m_widget->listModel()->setDataStoreNode(); -} - -void CollectionView::selectedNodesChanged(const QList<ModelNode> &selectedNodeList, - [[maybe_unused]] const QList<ModelNode> &lastSelectedNodeList) -{ - QList<ModelNode> selectedCollectionNodes = Utils::filtered(selectedNodeList, - &isStudioCollectionModel); - - bool singleNonCollectionNodeSelected = selectedNodeList.size() == 1 - && selectedCollectionNodes.isEmpty(); - - bool singleSelectedHasModelProperty = false; - if (singleNonCollectionNodeSelected) { - const ModelNode selectedNode = selectedNodeList.first(); - singleSelectedHasModelProperty = CollectionEditorUtils::canAcceptCollectionAsModel( - selectedNode); - } - - m_widget->setTargetNodeSelected(singleSelectedHasModelProperty); - - // More than one model is selected. So ignore them - if (selectedCollectionNodes.size() > 1) - return; -} - -void CollectionView::customNotification(const AbstractView *, - const QString &identifier, - const QList<ModelNode> &nodeList, - const QList<QVariant> &data) -{ - if (identifier == QLatin1String("item_library_created_by_drop") && !nodeList.isEmpty()) - onItemLibraryNodeCreated(nodeList.first()); - else if (identifier == QLatin1String("open_collection_by_id") && !data.isEmpty()) - m_widget->openCollection(collectionNameFromDataStoreChildren(data.first().toByteArray())); - else if (identifier == "delete_selected_collection") - m_widget->deleteSelectedCollection(); -} - -void CollectionView::addResource(const QUrl &url, const QString &name) -{ - executeInTransaction(Q_FUNC_INFO, [this, &url, &name]() { - ensureStudioModelImport(); - QString sourceAddress; - if (url.isLocalFile()) { - Utils::FilePath fp = QmlDesignerPlugin::instance()->currentDesignDocument()->fileName().parentDir(); - sourceAddress = Utils::FilePath::calcRelativePath(url.toLocalFile(), - fp.absoluteFilePath().toString()); - } else { - sourceAddress = url.toString(); - } -#ifdef QDS_USE_PROJECTSTORAGE - ModelNode resourceNode = createModelNode("JsonListModel"); -#else - const NodeMetaInfo resourceMetaInfo = jsonCollectionMetaInfo(); - ModelNode resourceNode = createModelNode(resourceMetaInfo.typeName(), - resourceMetaInfo.majorVersion(), - resourceMetaInfo.minorVersion()); -#endif - VariantProperty sourceProperty = resourceNode.variantProperty( - CollectionEditorConstants::SOURCEFILE_PROPERTY); - VariantProperty nameProperty = resourceNode.variantProperty("objectName"); - sourceProperty.setValue(sourceAddress); - nameProperty.setValue(name); - resourceNode.setIdWithoutRefactoring(model()->generateIdFromName(name, "model")); - rootModelNode().defaultNodeAbstractProperty().reparentHere(resourceNode); - }); -} - -void CollectionView::assignCollectionToNode(const QString &collectionName, const ModelNode &node) -{ - using DataType = CollectionDetails::DataType; - executeInTransaction("CollectionView::assignCollectionToNode", [&]() { - m_dataStore->assignCollectionToNode( - this, - node, - collectionName, - [&](const QString &collectionName, const QString &columnName) -> bool { - const CollectionReference reference{dataStoreNode(), collectionName}; - return m_widget->collectionDetailsModel()->collectionHasColumn(reference, columnName); - }, - [&](const QString &collectionName) -> QString { - const CollectionReference reference{dataStoreNode(), collectionName}; - return m_widget->collectionDetailsModel()->getFirstColumnName(reference); - }); - - // Create and assign a delegate to the list view item - if (node.metaInfo().isQtQuickListView()) { - CollectionDetails collection = m_widget->collectionDetailsModel()->upToDateConstCollection( - {dataStoreNode(), collectionName}); - - ModelNode rowItem(createModelNode("QtQuick.Row")); - ::setVariantPropertyValue(rowItem, "spacing", 5); - - const int columnsCount = collection.columns(); - for (int column = 0; column < columnsCount; ++column) { - const DataType dataType = collection.typeAt(column); - const QString columnName = collection.propertyAt(column); - ModelNode cellItem; - if (dataType == DataType::Color) { - cellItem = createModelNode("QtQuick.Rectangle"); - ::setBindingPropertyExpression(cellItem, "color", columnName); - ::setVariantPropertyValue(cellItem, "height", 20); - } else { - cellItem = createModelNode("QtQuick.Text"); - ::setBindingPropertyExpression(cellItem, "text", columnName); - } - ::setVariantPropertyValue(cellItem, "width", 100); - rowItem.defaultNodeAbstractProperty().reparentHere(cellItem); - } - - NodeProperty delegateProperty = node.nodeProperty("delegate"); - // Remove the old model node if is available - if (delegateProperty.modelNode()) - delegateProperty.modelNode().destroy(); - - delegateProperty.setModelNode(rowItem); - } - }); -} - -void CollectionView::assignCollectionToSelectedNode(const QString &collectionName) -{ - QTC_ASSERT(dataStoreNode() && hasSingleSelectedModelNode(), return); - assignCollectionToNode(collectionName, singleSelectedModelNode()); -} - -void CollectionView::addNewCollection(const QString &collectionName, const QJsonObject &localCollection) -{ - addTask(QSharedPointer<CollectionTask>( - new AddCollectionTask(this, m_widget->listModel(), localCollection, collectionName))); -} - -void CollectionView::openCollection(const QString &collectionName) -{ - m_widget->openCollection(collectionName); -} - -void CollectionView::registerDeclarativeType() -{ - CollectionDetails::registerDeclarativeType(); - CollectionDataTypeModel::registerDeclarativeType(); -} - -void CollectionView::resetDataStoreNode() -{ - m_dataStore->reloadModel(); - - ModelNode dataStore = m_dataStore->modelNode(); - if (!dataStore || m_widget->listModel()->sourceNode() == dataStore) - return; - - bool dataStoreSingletonFound = m_dataStoreTypeFound; - if (!dataStoreSingletonFound && rewriterView() && rewriterView()->isAttached()) { - const QList<QmlTypeData> types = rewriterView()->getQMLTypes(); - for (const QmlTypeData &cppTypeData : types) { - if (cppTypeData.isSingleton && cppTypeData.typeName == "DataStore") { - dataStoreSingletonFound = true; - break; - } - } - if (!dataStoreSingletonFound && !m_rewriterAmended) { - rewriterView()->forceAmend(); - m_rewriterAmended = true; - } - } - - if (dataStoreSingletonFound) { - m_widget->listModel()->setDataStoreNode(dataStore); - m_dataStoreTypeFound = true; - - while (!m_delayedTasks.isEmpty()) - m_delayedTasks.takeFirst()->process(); - } else if (++m_reloadCounter < 50) { - QTimer::singleShot(200, this, &CollectionView::resetDataStoreNode); - } else { - QTC_ASSERT(false, m_delayedTasks.clear()); - } -} - -ModelNode CollectionView::dataStoreNode() const -{ - return m_dataStore->modelNode(); -} - -void CollectionView::ensureDataStoreExists() -{ - bool filesJustCreated = false; - bool filesExist = 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; - } - } -} - -QString CollectionView::collectionNameFromDataStoreChildren(const PropertyName &childPropertyName) const -{ - return dataStoreNode() - .nodeProperty(childPropertyName) - .modelNode() - .property(CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY) - .toVariantProperty() - .value() - .toString(); -} - -NodeMetaInfo CollectionView::jsonCollectionMetaInfo() const -{ - return model()->metaInfo(CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME); -} - -void CollectionView::ensureStudioModelImport() -{ - executeInTransaction(__FUNCTION__, [&] { - Import import = Import::createLibraryImport(CollectionEditorConstants::COLLECTIONMODEL_IMPORT); - try { - if (!model()->hasImport(import, true, true)) - model()->changeImports({import}, {}); - } catch (const Exception &) { - QTC_ASSERT(false, return); - } - }); -} - -void CollectionView::onItemLibraryNodeCreated(const ModelNode &node) -{ - if (node.metaInfo().isQtQuickListView()) { - addTask(QSharedPointer<CollectionTask>( - new DropListViewTask(this, m_widget->listModel(), node))); - } -} - -void CollectionView::onDocumentUpdated(const QSharedPointer<const QmlJS::Document> &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<CollectionTask> task) -{ - ensureDataStoreExists(); - if (m_dataStoreTypeFound) - task->process(); - else if (m_dataStore->modelNode()) - m_delayedTasks << task; -} - -CollectionTask::CollectionTask(CollectionView *view, CollectionListModel *listModel) - : m_collectionView(view) - , m_listModel(listModel) -{} - -DropListViewTask::DropListViewTask(CollectionView *view, - CollectionListModel *listModel, - const ModelNode &node) - : CollectionTask(view, listModel) - , m_node(node) -{} - -void DropListViewTask::process() -{ - AbstractView *view = m_node.view(); - if (!m_node || !m_collectionView || !m_listModel || !view) - return; - - const QString newCollectionName = m_listModel->getUniqueCollectionName("ListModel"); - m_listModel->addCollection(newCollectionName, CollectionEditorUtils::defaultColorCollection()); - m_collectionView->openCollection(newCollectionName); - m_collectionView->assignCollectionToNode(newCollectionName, m_node); -} - -AddCollectionTask::AddCollectionTask(CollectionView *view, - CollectionListModel *listModel, - const QJsonObject &localJsonObject, - const QString &collectionName) - : CollectionTask(view, listModel) - , m_localJsonObject(localJsonObject) - , m_name(collectionName) -{} - -void AddCollectionTask::process() -{ - if (!m_listModel) - return; - - const QString newCollectionName = m_listModel->collectionExists(m_name) - ? m_listModel->getUniqueCollectionName(m_name) - : m_name; - - m_listModel->addCollection(newCollectionName, m_localJsonObject); -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h deleted file mode 100644 index a4b16c4c27..0000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "abstractview.h" -#include "datastoremodelnode.h" -#include "modelnode.h" - -#include <QJsonObject> - -namespace QmlJS { -class Document; -} - -namespace QmlDesigner { - -class CollectionDetails; -class CollectionListModel; -class CollectionTask; -class CollectionWidget; -class DataStoreModelNode; - -class CollectionView : public AbstractView -{ - Q_OBJECT - -public: - explicit CollectionView(ExternalDependenciesInterface &externalDependencies); - - bool hasWidget() const override; - WidgetInfo widgetInfo() override; - - void modelAttached(Model *model) override; - void modelAboutToBeDetached(Model *model) override; - - void selectedNodesChanged(const QList<ModelNode> &selectedNodeList, - const QList<ModelNode> &lastSelectedNodeList) override; - - void customNotification(const AbstractView *view, - const QString &identifier, - const QList<ModelNode> &nodeList, - const QList<QVariant> &data) override; - - void addResource(const QUrl &url, const QString &name); - - void assignCollectionToNode(const QString &collectionName, const ModelNode &node); - void assignCollectionToSelectedNode(const QString &collectionName); - void addNewCollection(const QString &collectionName, const QJsonObject &localCollection); - - void openCollection(const QString &collectionName); - - static void registerDeclarativeType(); - - void resetDataStoreNode(); - ModelNode dataStoreNode() const; - void ensureDataStoreExists(); - QString collectionNameFromDataStoreChildren(const PropertyName &childPropertyName) const; - -private: - friend class CollectionTask; - - NodeMetaInfo jsonCollectionMetaInfo() const; - void ensureStudioModelImport(); - void onItemLibraryNodeCreated(const ModelNode &node); - void onDocumentUpdated(const QSharedPointer<const QmlJS::Document> &doc); - void addTask(QSharedPointer<CollectionTask> task); - - QPointer<CollectionWidget> m_widget; - std::unique_ptr<DataStoreModelNode> m_dataStore; - QSet<Utils::FilePath> m_expectedDocumentUpdates; - QList<QSharedPointer<CollectionTask>> m_delayedTasks; - QMetaObject::Connection m_documentUpdateConnection; - bool m_libraryInfoIsUpdated = false; - bool m_dataStoreTypeFound = false; - bool m_rewriterAmended = false; - int m_reloadCounter = 0; -}; - -class CollectionTask -{ -public: - CollectionTask(CollectionView *view, CollectionListModel *listModel); - CollectionTask() = delete; - virtual ~CollectionTask() = default; - - virtual void process() = 0; - -protected: - QPointer<CollectionView> m_collectionView; - QPointer<CollectionListModel> m_listModel; -}; - -class DropListViewTask : public CollectionTask -{ -public: - DropListViewTask(CollectionView *view, CollectionListModel *listModel, const ModelNode &node); - - void process() override; - -private: - ModelNode m_node; -}; - -class AddCollectionTask : public CollectionTask -{ -public: - AddCollectionTask(CollectionView *view, - CollectionListModel *listModel, - const QJsonObject &localJsonObject, - const QString &collectionName); - - void process() override; - -private: - QJsonObject m_localJsonObject; - QString m_name; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp deleted file mode 100644 index 093729dc67..0000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp +++ /dev/null @@ -1,296 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "collectionwidget.h" - -#include "collectiondetails.h" -#include "collectiondetailsmodel.h" -#include "collectiondetailssortfiltermodel.h" -#include "collectioneditorutils.h" -#include "collectionlistmodel.h" -#include "collectionview.h" -#include "designmodewidget.h" -#include "qmldesignerconstants.h" -#include "qmldesignerplugin.h" -#include "theme.h" - -#include <coreplugin/icore.h> -#include <coreplugin/messagebox.h> -#include <studioquickwidget.h> - -#include <QFileInfo> -#include <QJsonArray> -#include <QJsonDocument> -#include <QJsonObject> -#include <QJsonParseError> -#include <QMetaObject> -#include <QQmlEngine> -#include <QQuickItem> -#include <QShortcut> -#include <QVBoxLayout> - -namespace { - -QString collectionViewResourcesPath() -{ -#ifdef SHARE_QML_PATH - if (qEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) - return QLatin1String(SHARE_QML_PATH) + "/collectionEditorQmlSource"; -#endif - return Core::ICore::resourcePath("qmldesigner/collectionEditorQmlSource").toString(); -} - -QString getPreferredCollectionName(const QUrl &url, const QString &collectionName) -{ - if (collectionName.isEmpty()) { - QFileInfo fileInfo(url.isLocalFile() ? url.toLocalFile() : url.toString()); - return fileInfo.completeBaseName(); - } - - return collectionName; -} - -} // namespace - -namespace QmlDesigner { -CollectionWidget::CollectionWidget(CollectionView *view) - : QFrame() - , m_view(view) - , m_listModel(new CollectionListModel) - , m_collectionDetailsModel(new CollectionDetailsModel) - , m_collectionDetailsSortFilterModel(std::make_unique<CollectionDetailsSortFilterModel>()) - , m_quickWidget(new StudioQuickWidget(this)) -{ - setWindowTitle(tr("Model Editor", "Title of model editor widget")); - - Core::IContext *icontext = nullptr; - Core::Context context(Constants::C_QMLCOLLECTIONEDITOR); - icontext = new Core::IContext(this); - icontext->setContext(context); - icontext->setWidget(this); - - connect(m_listModel, &CollectionListModel::warning, this, &CollectionWidget::warn); - - m_collectionDetailsSortFilterModel->setSourceModel(m_collectionDetailsModel); - - m_quickWidget->quickWidget()->setObjectName(Constants::OBJECT_NAME_COLLECTION_EDITOR); - m_quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); - m_quickWidget->engine()->addImportPath(collectionViewResourcesPath() + "/imports"); - m_quickWidget->setClearColor(Theme::getColor(Theme::Color::DSpanelBackground)); - - Theme::setupTheme(m_quickWidget->engine()); - m_quickWidget->quickWidget()->installEventFilter(this); - - auto layout = new QVBoxLayout(this); - layout->setContentsMargins({}); - layout->setSpacing(0); - layout->addWidget(m_quickWidget.data()); - - qmlRegisterAnonymousType<CollectionWidget>("CollectionEditorBackend", 1); - auto map = m_quickWidget->registerPropertyMap("CollectionEditorBackend"); - map->setProperties({ - {"rootView", QVariant::fromValue(this)}, - {"model", QVariant::fromValue(m_listModel.data())}, - {"collectionDetailsModel", QVariant::fromValue(m_collectionDetailsModel.data())}, - {"collectionDetailsSortFilterModel", - QVariant::fromValue(m_collectionDetailsSortFilterModel.get())}, - }); - - auto hotReloadShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F4), this); - connect(hotReloadShortcut, &QShortcut::activated, this, &CollectionWidget::reloadQmlSource); - - reloadQmlSource(); - - QmlDesignerPlugin::trackWidgetFocusTime(this, Constants::EVENT_MODELEDITOR_TIME); -} - -void CollectionWidget::contextHelp(const Core::IContext::HelpCallback &callback) const -{ - if (m_view) - QmlDesignerPlugin::contextHelp(callback, m_view->contextHelpId()); - else - callback({}); -} - -QPointer<CollectionListModel> CollectionWidget::listModel() const -{ - return m_listModel; -} - -QPointer<CollectionDetailsModel> CollectionWidget::collectionDetailsModel() const -{ - return m_collectionDetailsModel; -} - -void CollectionWidget::reloadQmlSource() -{ - const QString collectionViewQmlPath = collectionViewResourcesPath() + "/CollectionView.qml"; - - QTC_ASSERT(QFileInfo::exists(collectionViewQmlPath), return); - - m_quickWidget->setSource(QUrl::fromLocalFile(collectionViewQmlPath)); - - if (!m_quickWidget->rootObject()) { - QString errorString; - const auto errors = m_quickWidget->errors(); - for (const QQmlError &error : errors) - errorString.append("\n" + error.toString()); - - Core::AsynchronousMessageBox::warning(tr("Cannot Create QtQuick View"), - tr("StatesEditorWidget: %1 cannot be created.%2") - .arg(collectionViewQmlPath, errorString)); - return; - } -} - -QSize CollectionWidget::minimumSizeHint() const -{ - return {300, 300}; -} - -bool CollectionWidget::loadJsonFile(const QUrl &url, const QString &collectionName) -{ - if (!isJsonFile(url)) - return false; - - m_view->addResource(url, getPreferredCollectionName(url, collectionName)); - - return true; -} - -bool CollectionWidget::loadCsvFile(const QUrl &url, const QString &collectionName) -{ - m_view->addResource(url, getPreferredCollectionName(url, collectionName)); - - return true; -} - -bool CollectionWidget::isJsonFile(const QUrl &url) const -{ - Utils::FilePath filePath = Utils::FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile() - : url.toString()); - Utils::FileReader file; - if (!file.fetch(filePath)) - return false; - - QJsonParseError error; - QJsonDocument::fromJson(file.data(), &error); - if (error.error) - return false; - - return true; -} - -bool CollectionWidget::isCsvFile(const QUrl &url) const -{ - QString filePath = url.isLocalFile() ? url.toLocalFile() : url.toString(); - QFileInfo fileInfo(filePath); - return fileInfo.exists() && !fileInfo.suffix().compare("csv", Qt::CaseInsensitive); -} - -bool CollectionWidget::isValidUrlToImport(const QUrl &url) const -{ - using Utils::FilePath; - FilePath fileInfo = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile() - : url.toString()); - if (fileInfo.suffix() == "json") - return isJsonFile(url); - - if (fileInfo.suffix() == "csv") - return isCsvFile(url); - - return false; -} - -bool CollectionWidget::importFile(const QString &collectionName, - const QUrl &url, - const bool &firstRowIsHeader) -{ - using Utils::FilePath; - - FilePath fileInfo = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile() - : url.toString()); - CollectionDetails loadedCollection; - QByteArray fileContent; - - auto loadUrlContent = [&]() -> bool { - Utils::FileReader file; - if (file.fetch(fileInfo)) { - fileContent = file.data(); - return true; - } - - warn(tr("Import from file"), tr("Cannot import from file \"%1\"").arg(fileInfo.fileName())); - return false; - }; - - if (fileInfo.suffix() == "json") { - if (!loadUrlContent()) - return false; - - QJsonParseError parseError; - loadedCollection = CollectionDetails::fromImportedJson(fileContent, &parseError); - if (parseError.error != QJsonParseError::NoError) { - warn(tr("Json file Import error"), - tr("Cannot parse json content\n%1").arg(parseError.errorString())); - } - } else if (fileInfo.suffix() == "csv") { - if (!loadUrlContent()) - return false; - loadedCollection = CollectionDetails::fromImportedCsv(fileContent, firstRowIsHeader); - } - - if (loadedCollection.columns()) { - m_view->addNewCollection(collectionName, loadedCollection.toLocalJson()); - return true; - } else { - warn(tr("Can not add a model to the JSON file"), - tr("The imported model is empty or is not supported.")); - } - return false; -} - -void CollectionWidget::addCollectionToDataStore(const QString &collectionName) -{ - m_view->addNewCollection(collectionName, CollectionEditorUtils::defaultCollection()); -} - -void CollectionWidget::assignCollectionToSelectedNode(const QString collectionName) -{ - m_view->assignCollectionToSelectedNode(collectionName); -} - -void CollectionWidget::openCollection(const QString &collectionName) -{ - m_listModel->selectCollectionName(collectionName); - QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("CollectionEditor", true); -} - -ModelNode CollectionWidget::dataStoreNode() const -{ - return m_view->dataStoreNode(); -} - -void CollectionWidget::warn(const QString &title, const QString &body) -{ - QMetaObject::invokeMethod(m_quickWidget->rootObject(), - "showWarning", - Q_ARG(QVariant, title), - Q_ARG(QVariant, body)); -} - -void CollectionWidget::setTargetNodeSelected(bool selected) -{ - if (m_targetNodeSelected == selected) - return; - - m_targetNodeSelected = selected; - emit targetNodeSelectedChanged(m_targetNodeSelected); -} - -void CollectionWidget::deleteSelectedCollection() -{ - QMetaObject::invokeMethod(m_quickWidget->quickWidget()->rootObject(), "deleteSelectedCollection"); -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h deleted file mode 100644 index 0957bd81e0..0000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <QFrame> - -#include <coreplugin/icontext.h> - -class StudioQuickWidget; - -namespace QmlDesigner { - -class CollectionDetailsModel; -class CollectionDetailsSortFilterModel; -class CollectionListModel; -class CollectionView; -class ModelNode; - -class CollectionWidget : public QFrame -{ - Q_OBJECT - - Q_PROPERTY(bool targetNodeSelected MEMBER m_targetNodeSelected NOTIFY targetNodeSelectedChanged) - -public: - CollectionWidget(CollectionView *view); - void contextHelp(const Core::IContext::HelpCallback &callback) const; - - QPointer<CollectionListModel> listModel() const; - QPointer<CollectionDetailsModel> collectionDetailsModel() const; - - void reloadQmlSource(); - - virtual QSize minimumSizeHint() const; - - Q_INVOKABLE bool loadJsonFile(const QUrl &url, const QString &collectionName = {}); - Q_INVOKABLE bool loadCsvFile(const QUrl &url, const QString &collectionName = {}); - Q_INVOKABLE bool isJsonFile(const QUrl &url) const; - Q_INVOKABLE bool isCsvFile(const QUrl &url) const; - Q_INVOKABLE bool isValidUrlToImport(const QUrl &url) const; - - Q_INVOKABLE bool importFile(const QString &collectionName, - const QUrl &url, - const bool &firstRowIsHeader = true); - - Q_INVOKABLE void addCollectionToDataStore(const QString &collectionName); - Q_INVOKABLE void assignCollectionToSelectedNode(const QString collectionName); - Q_INVOKABLE void openCollection(const QString &collectionName); - Q_INVOKABLE ModelNode dataStoreNode() const; - - void warn(const QString &title, const QString &body); - void setTargetNodeSelected(bool selected); - - void deleteSelectedCollection(); - -signals: - void targetNodeSelectedChanged(bool); - -private: - QString generateUniqueCollectionName(const ModelNode &node, const QString &name); - - QPointer<CollectionView> m_view; - QPointer<CollectionListModel> m_listModel; - QPointer<CollectionDetailsModel> m_collectionDetailsModel; - std::unique_ptr<CollectionDetailsSortFilterModel> m_collectionDetailsSortFilterModel; - QScopedPointer<StudioQuickWidget> m_quickWidget; - bool m_targetNodeSelected = false; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp deleted file mode 100644 index 5be9c20f9e..0000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp +++ /dev/null @@ -1,511 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "datastoremodelnode.h" - -#include "abstractview.h" -#include "collectioneditorconstants.h" -#include "collectioneditorutils.h" -#include "model/qmltextgenerator.h" -#include "plaintexteditmodifier.h" -#include "qmldesignerbase/qmldesignerbaseplugin.h" -#include "qmldesignerexternaldependencies.h" -#include "rewriterview.h" - -#include <model.h> -#include <nodemetainfo.h> -#include <nodeproperty.h> -#include <variantproperty.h> - -#include <qmljstools/qmljscodestylepreferences.h> -#include <qmljstools/qmljstoolssettings.h> - -#include <coreplugin/documentmanager.h> -#include <projectexplorer/project.h> -#include <projectexplorer/projectexplorer.h> -#include <projectexplorer/projectmanager.h> - -#include <utils/fileutils.h> -#include <utils/qtcassert.h> - -#include <QPlainTextEdit> -#include <QRegularExpression> -#include <QRegularExpressionMatch> -#include <QScopedPointer> - -namespace { - -inline constexpr char CHILDLISTMODEL_TYPENAME[] = "ChildListModel"; - -QmlDesigner::PropertyNameList createNameList(const QmlDesigner::ModelNode &node) -{ - using QmlDesigner::AbstractProperty; - using QmlDesigner::PropertyName; - using QmlDesigner::PropertyNameList; - static PropertyNameList defaultsNodeProps = { - "id", - QmlDesigner::CollectionEditorConstants::SOURCEFILE_PROPERTY, - QmlDesigner::CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY, - "backend"}; - PropertyNameList dynamicPropertyNames = Utils::transform( - node.dynamicProperties(), - [](const AbstractProperty &property) -> PropertyName { return property.name(); }); - - Utils::sort(dynamicPropertyNames); - - return defaultsNodeProps + dynamicPropertyNames; -} - -bool isValidCollectionPropertyName(const QString &collectionId) -{ - static const QmlDesigner::PropertyNameList reservedKeywords = { - QmlDesigner::CollectionEditorConstants::SOURCEFILE_PROPERTY, - QmlDesigner::CollectionEditorConstants::JSONBACKEND_TYPENAME, - "backend", - "models", - }; - - return QmlDesigner::ModelNode::isValidId(collectionId) - && !reservedKeywords.contains(collectionId.toLatin1()); -} - -QMap<QString, QmlDesigner::PropertyName> getModelIdMap(const QmlDesigner::ModelNode &rootNode) -{ - using namespace QmlDesigner; - QMap<QString, PropertyName> modelNameForId; - - const QList<AbstractProperty> propertyNames = rootNode.dynamicProperties(); - - for (const AbstractProperty &property : std::as_const(propertyNames)) { - if (!property.isNodeProperty()) - continue; - - NodeProperty nodeProperty = property.toNodeProperty(); - if (!nodeProperty.hasDynamicTypeName(CHILDLISTMODEL_TYPENAME)) - continue; - - ModelNode childNode = nodeProperty.modelNode(); - if (childNode.hasProperty(CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY)) { - QString modelName = childNode - .property(CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY) - .toVariantProperty() - .value() - .toString(); - - if (!modelName.isEmpty()) - modelNameForId.insert(modelName, property.name()); - } - } - return modelNameForId; -} - -void setQmlContextToModel(QmlDesigner::Model *model, const QString &qmlContext) -{ - using namespace QmlDesigner; - Q_ASSERT(model); - - QScopedPointer<QPlainTextEdit> textEdit(new QPlainTextEdit); - QScopedPointer<NotIndentingTextEditModifier> modifier( - new NotIndentingTextEditModifier(textEdit.data())); - textEdit->hide(); - textEdit->setPlainText(qmlContext); - QmlDesigner::ExternalDependencies externalDependencies{QmlDesignerBasePlugin::settings()}; - QScopedPointer<RewriterView> rewriter( - new RewriterView(externalDependencies, QmlDesigner::RewriterView::Validate)); - - rewriter->setParent(model); - rewriter->setTextModifier(modifier.get()); - rewriter->setCheckSemanticErrors(false); - - model->attachView(rewriter.get()); - model->detachView(rewriter.get()); -} - -} // namespace - -namespace QmlDesigner { - -DataStoreModelNode::DataStoreModelNode() -{ - reloadModel(); -} - -void DataStoreModelNode::reloadModel() -{ - using Utils::FilePath; - if (!ProjectExplorer::ProjectManager::startupProject()) { - reset(); - return; - } - bool forceUpdate = false; - - const FilePath dataStoreQmlPath = CollectionEditorUtils::dataStoreQmlFilePath(); - const FilePath dataStoreJsonPath = CollectionEditorUtils::dataStoreJsonFilePath(); - QUrl dataStoreQmlUrl = dataStoreQmlPath.toUrl(); - - if (dataStoreQmlPath.exists() && dataStoreJsonPath.exists()) { - if (!m_model.get() || m_model->fileUrl() != dataStoreQmlUrl) { -#ifdef QDS_USE_PROJECTSTORAGE - m_model = model()->createModel("JsonListModel"); - forceUpdate = true; - Import import = Import::createLibraryImport("QtQuick.Studio.Utils"); - m_model->changeImports({import}, {}); -#else - m_model = Model::create(CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME, 1, 1); - forceUpdate = true; - Import import = Import::createLibraryImport( - CollectionEditorConstants::COLLECTIONMODEL_IMPORT); - try { - if (!m_model->hasImport(import, true, true)) - m_model->changeImports({import}, {}); - } catch (const Exception &) { - QTC_ASSERT(false, return); - } -#endif - } - } else { - reset(); - } - - if (!m_model.get()) - return; - - if (forceUpdate) { - m_model->setFileUrl(dataStoreQmlUrl); - m_dataRelativePath = dataStoreJsonPath.relativePathFrom(dataStoreQmlPath).toFSPathString(); - preloadFile(); - update(); - } -} - -QStringList DataStoreModelNode::collectionNames() const -{ - return m_collectionPropertyNames.keys(); -} - -Model *DataStoreModelNode::model() const -{ - return m_model.get(); -} - -ModelNode DataStoreModelNode::modelNode() const -{ - if (!m_model.get()) - return {}; - return m_model->rootModelNode(); -} - -QString DataStoreModelNode::getModelQmlText() -{ - ModelNode node = modelNode(); - QTC_ASSERT(node, return {}); - - Internal::QmlTextGenerator textGen(createNameList(node), - QmlJSTools::QmlJSToolsSettings::globalCodeStyle()->tabSettings()); - - QString genText = textGen(node); - return genText; -} - -void DataStoreModelNode::reset() -{ - if (m_model) - m_model.reset(); - - m_dataRelativePath.clear(); - setCollectionNames({}); -} - -void DataStoreModelNode::preloadFile() -{ - using Utils::FilePath; - using Utils::FileReader; - - if (!m_model) - return; - - const FilePath dataStoreQmlPath = dataStoreQmlFilePath(); - FileReader dataStoreQmlFile; - QString sourceQmlContext; - - if (dataStoreQmlFile.fetch(dataStoreQmlPath)) - sourceQmlContext = QString::fromLatin1(dataStoreQmlFile.data()); - - setQmlContextToModel(m_model.get(), sourceQmlContext); - m_collectionPropertyNames = getModelIdMap(m_model->rootModelNode()); -} - -void DataStoreModelNode::updateDataStoreProperties() -{ - QTC_ASSERT(model(), return); - - ModelNode rootNode = modelNode(); - QTC_ASSERT(rootNode.isValid(), return); - - QSet<QString> collectionNamesToBeAdded; - const QStringList allCollectionNames = m_collectionPropertyNames.keys(); - for (const QString &collectionName : allCollectionNames) - collectionNamesToBeAdded << collectionName; - - const QList<AbstractProperty> formerPropertyNames = rootNode.dynamicProperties(); - - // Remove invalid collection names from the properties - for (const AbstractProperty &property : formerPropertyNames) { - if (!property.isNodeProperty()) - continue; - - NodeProperty nodeProprty = property.toNodeProperty(); - if (!nodeProprty.hasDynamicTypeName(CHILDLISTMODEL_TYPENAME)) - continue; - - ModelNode childNode = nodeProprty.modelNode(); - if (childNode.hasProperty(CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY)) { - QString modelName = childNode - .property(CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY) - .toVariantProperty() - .value() - .toString(); - if (collectionNamesToBeAdded.contains(modelName)) { - m_collectionPropertyNames.insert(modelName, property.name()); - collectionNamesToBeAdded.remove(modelName); - } else { - rootNode.removeProperty(property.name()); - } - } else { - rootNode.removeProperty(property.name()); - } - } - - rootNode.setIdWithoutRefactoring("models"); - - QStringList collectionNamesLeft = collectionNamesToBeAdded.values(); - Utils::sort(collectionNamesLeft); - for (const QString &collectionName : std::as_const(collectionNamesLeft)) - addCollectionNameToTheModel(collectionName, getUniquePropertyName(collectionName)); - - // Backend Property - ModelNode backendNode = model()->createModelNode(CollectionEditorConstants::JSONBACKEND_TYPENAME); - NodeProperty backendProperty = rootNode.nodeProperty("backend"); - backendProperty.setDynamicTypeNameAndsetModelNode(CollectionEditorConstants::JSONBACKEND_TYPENAME, - backendNode); - // Source Property - VariantProperty sourceProp = rootNode.variantProperty( - CollectionEditorConstants::SOURCEFILE_PROPERTY); - sourceProp.setValue(m_dataRelativePath); -} - -void DataStoreModelNode::updateSingletonFile() -{ - using Utils::FilePath; - using Utils::FileSaver; - QTC_ASSERT(m_model.get(), return); - - const QString pragmaSingleTone = "pragma Singleton\n"; - QString imports; - - for (const Import &import : m_model->imports()) - imports += QStringLiteral("import %1\n").arg(import.toString(true)); - - QString content = pragmaSingleTone + imports + getModelQmlText(); - Core::DocumentManager::expectFileChange(dataStoreQmlFilePath()); - FileSaver file(dataStoreQmlFilePath()); - file.write(content.toLatin1()); - file.finalize(); -} - -void DataStoreModelNode::update() -{ - if (!m_model.get()) - return; - - updateDataStoreProperties(); - updateSingletonFile(); -} - -void DataStoreModelNode::addCollectionNameToTheModel(const QString &collectionName, - const PropertyName &dataStorePropertyName) -{ - ModelNode rootNode = modelNode(); - QTC_ASSERT(rootNode.isValid(), return); - - if (dataStorePropertyName.isEmpty()) { - qWarning() << __FUNCTION__ << __LINE__ - << QString("The property name cannot be generated from \"%1\"").arg(collectionName); - return; - } - - ModelNode collectionNode = model()->createModelNode(CHILDLISTMODEL_TYPENAME); - VariantProperty modelNameProperty = collectionNode.variantProperty( - CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY); - modelNameProperty.setValue(collectionName); - - NodeProperty nodeProp = rootNode.nodeProperty(dataStorePropertyName); - nodeProp.setDynamicTypeNameAndsetModelNode(CHILDLISTMODEL_TYPENAME, collectionNode); - - m_collectionPropertyNames.insert(collectionName, dataStorePropertyName); -} - -Utils::FilePath DataStoreModelNode::dataStoreQmlFilePath() const -{ - QUrl modelUrl = m_model->fileUrl(); - return Utils::FilePath::fromUserInput(modelUrl.isLocalFile() ? modelUrl.toLocalFile() - : modelUrl.toString()); -} - -PropertyName DataStoreModelNode::getUniquePropertyName(const QString &collectionName) -{ - ModelNode dataStoreNode = modelNode(); - QTC_ASSERT(!collectionName.isEmpty() && dataStoreNode.isValid(), return {}); - - QString newProperty; - - // convert to camel case - QStringList nameWords = collectionName.split(' '); - nameWords[0] = nameWords[0].at(0).toLower() + nameWords[0].mid(1); - for (int i = 1; i < nameWords.size(); ++i) - nameWords[i] = nameWords[i].at(0).toUpper() + nameWords[i].mid(1); - newProperty = nameWords.join(""); - - // if id starts with a number prepend an underscore - if (newProperty.at(0).isDigit()) - newProperty.prepend('_'); - - // If the new id is not valid (e.g. qml keyword match), prepend an underscore - if (!isValidCollectionPropertyName(newProperty)) - newProperty.prepend('_'); - - static const QRegularExpression rgx("\\d+$"); // matches a number at the end of a string - while (dataStoreNode.hasProperty(newProperty.toLatin1())) { // id exists - QRegularExpressionMatch match = rgx.match(newProperty); - if (match.hasMatch()) { // ends with a number, increment it - QString numStr = match.captured(); - int num = numStr.toInt() + 1; - newProperty = newProperty.mid(0, match.capturedStart()) + QString::number(num); - } else { - newProperty.append('1'); - } - } - - return newProperty.toLatin1(); -} - -void DataStoreModelNode::setCollectionNames(const QStringList &newCollectionNames) -{ - m_collectionPropertyNames.clear(); - for (const QString &collectionName : newCollectionNames) - m_collectionPropertyNames.insert(collectionName, {}); - update(); -} - -void DataStoreModelNode::addCollection(const QString &collectionName) -{ - if (!m_collectionPropertyNames.contains(collectionName)) { - m_collectionPropertyNames.insert(collectionName, {}); - update(); - } -} - -void DataStoreModelNode::renameCollection(const QString &oldName, const QString &newName) -{ - ModelNode dataStoreNode = modelNode(); - QTC_ASSERT(dataStoreNode.isValid(), return); - - if (m_collectionPropertyNames.contains(oldName)) { - const PropertyName oldPropertyName = m_collectionPropertyNames.value(oldName); - if (!oldPropertyName.isEmpty() && dataStoreNode.hasProperty(oldPropertyName)) { - NodeProperty collectionNode = dataStoreNode.property(oldPropertyName).toNodeProperty(); - if (collectionNode.isValid()) { - VariantProperty modelNameProperty = collectionNode.modelNode().variantProperty( - CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY); - modelNameProperty.setValue(newName); - m_collectionPropertyNames.remove(oldName); - m_collectionPropertyNames.insert(newName, collectionNode.name()); - update(); - return; - } - qWarning() << __FUNCTION__ << __LINE__ - << "There is no valid node for the old collection name"; - return; - } - qWarning() << __FUNCTION__ << __LINE__ << QString("Invalid old property name") - << oldPropertyName; - return; - } - qWarning() << __FUNCTION__ << __LINE__ - << QString("There is no old collection name registered with this name \"%1\"").arg(oldName); -} - -void DataStoreModelNode::removeCollections(const QStringList &collectionNames) -{ - bool updateRequired = false; - for (const QString &collectionName : collectionNames) { - if (m_collectionPropertyNames.contains(collectionName)) { - m_collectionPropertyNames.remove(collectionName); - updateRequired = true; - } - } - - if (updateRequired) - update(); -} - -void DataStoreModelNode::assignCollectionToNode(AbstractView *view, - const ModelNode &targetNode, - const QString &collectionName, - CollectionColumnFinder collectionHasColumn, - FirstColumnProvider firstColumnProvider) -{ - QTC_ASSERT(targetNode.isValid(), return); - - if (!CollectionEditorUtils::canAcceptCollectionAsModel(targetNode)) - return; - - if (!m_collectionPropertyNames.contains(collectionName)) { - qWarning() << __FUNCTION__ << __LINE__ << "Collection doesn't exist in the DataStore" - << collectionName; - return; - } - - PropertyName propertyName = m_collectionPropertyNames.value(collectionName); - - const ModelNode dataStore = modelNode(); - VariantProperty sourceProperty = dataStore.variantProperty(propertyName); - if (!sourceProperty.exists()) { - qWarning() << __FUNCTION__ << __LINE__ - << "The source property doesn't exist in the DataStore."; - return; - } - - view->executeInTransaction("assignCollectionToNode", [&]() { - QString identifier = QString("DataStore.%1").arg(QString::fromLatin1(sourceProperty.name())); - - // Remove the old model node property if exists - NodeProperty modelNodeProperty = targetNode.nodeProperty("model"); - if (modelNodeProperty.modelNode()) - modelNodeProperty.modelNode().destroy(); - - // Assign the collection to the node - BindingProperty modelProperty = targetNode.bindingProperty("model"); - modelProperty.setExpression(identifier); - - if (CollectionEditorUtils::hasTextRoleProperty(targetNode)) { - VariantProperty textRoleProperty = targetNode.variantProperty("textRole"); - const QVariant currentTextRoleValue = textRoleProperty.value(); - - if (currentTextRoleValue.isValid() && !currentTextRoleValue.isNull()) { - if (currentTextRoleValue.type() == QVariant::String) { - const QString currentTextRole = currentTextRoleValue.toString(); - if (collectionHasColumn(collectionName, currentTextRole)) - return; - } else { - return; - } - } - - QString textRoleValue = firstColumnProvider(collectionName); - textRoleProperty.setValue(textRoleValue); - } - }); -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h deleted file mode 100644 index 6cd969edbe..0000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <modelnode.h> - -#include <QMap> - -namespace Utils { -class FilePath; -} - -namespace QmlDesigner { - -class Model; - -class DataStoreModelNode -{ -public: - using CollectionColumnFinder = std::function<bool(const QString &collectionName, - const QString &columnName)>; - using FirstColumnProvider = std::function<QString(const QString &collectionName)>; - - DataStoreModelNode(); - - void reloadModel(); - QStringList collectionNames() const; - - Model *model() const; - ModelNode modelNode() const; - - void setCollectionNames(const QStringList &newCollectionNames); - void addCollection(const QString &collectionName); - void renameCollection(const QString &oldName, const QString &newName); - void removeCollections(const QStringList &collectionNames); - - void assignCollectionToNode(AbstractView *view, - const ModelNode &targetNode, - const QString &collectionName, - CollectionColumnFinder collectionHasColumn, - FirstColumnProvider firstColumnProvider); - -private: - QString getModelQmlText(); - - void reset(); - void preloadFile(); - void updateDataStoreProperties(); - void updateSingletonFile(); - void update(); - void addCollectionNameToTheModel(const QString &collectionName, - const PropertyName &dataStorePropertyName); - Utils::FilePath dataStoreQmlFilePath() const; - - PropertyName getUniquePropertyName(const QString &collectionName); - - ModelPointer m_model; - QMap<QString, PropertyName> m_collectionPropertyNames; - QString m_dataRelativePath; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/abstractaction.cpp b/src/plugins/qmldesigner/components/componentcore/abstractaction.cpp index 559e8ea69c..3379d99834 100644 --- a/src/plugins/qmldesigner/components/componentcore/abstractaction.cpp +++ b/src/plugins/qmldesigner/components/componentcore/abstractaction.cpp @@ -8,7 +8,7 @@ namespace QmlDesigner { AbstractAction::AbstractAction(const QString &description) - : m_pureAction(new DefaultAction(description)) + : m_pureAction(std::make_unique<DefaultAction>(description)) { const Utils::Icon defaultIcon({ {":/utils/images/select.png", Utils::Theme::QmlDesigner_FormEditorForegroundColor}}, Utils::Icon::MenuTintedStyle); @@ -56,7 +56,7 @@ void AbstractAction::setCheckable(bool checkable) PureActionInterface *AbstractAction::pureAction() const { - return m_pureAction.data(); + return m_pureAction.get(); } SelectionContext AbstractAction::selectionContext() const diff --git a/src/plugins/qmldesigner/components/componentcore/abstractaction.h b/src/plugins/qmldesigner/components/componentcore/abstractaction.h index ca4cc582ce..53b540cc7a 100644 --- a/src/plugins/qmldesigner/components/componentcore/abstractaction.h +++ b/src/plugins/qmldesigner/components/componentcore/abstractaction.h @@ -6,7 +6,8 @@ #include "actioninterface.h" #include <QAction> -#include <QScopedPointer> + +#include <memory> namespace QmlDesigner { @@ -58,7 +59,7 @@ protected: SelectionContext selectionContext() const; private: - QScopedPointer<PureActionInterface> m_pureAction; + std::unique_ptr<PureActionInterface> m_pureAction; SelectionContext m_selectionContext; }; diff --git a/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.cpp b/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.cpp index 288b8e409d..5b340343e7 100644 --- a/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.cpp +++ b/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.cpp @@ -8,14 +8,14 @@ namespace QmlDesigner { -AbstractActionGroup::AbstractActionGroup(const QString &displayName) : - m_displayName(displayName), - m_menu(new QmlEditorMenu) +AbstractActionGroup::AbstractActionGroup(const QString &displayName) + : m_displayName(displayName) + , m_menu(Utils::makeUniqueObjectPtr<QmlEditorMenu>()) { m_menu->setTitle(displayName); m_action = m_menu->menuAction(); - QmlEditorMenu *qmlEditorMenu = qobject_cast<QmlEditorMenu *>(m_menu.data()); + QmlEditorMenu *qmlEditorMenu = qobject_cast<QmlEditorMenu *>(m_menu.get()); if (qmlEditorMenu) qmlEditorMenu->setIconsVisible(false); } @@ -32,7 +32,7 @@ QAction *AbstractActionGroup::action() const QMenu *AbstractActionGroup::menu() const { - return m_menu.data(); + return m_menu.get(); } SelectionContext AbstractActionGroup::selectionContext() const diff --git a/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.h b/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.h index dd89849ecf..f239eeab3d 100644 --- a/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.h +++ b/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.h @@ -5,9 +5,10 @@ #include "actioninterface.h" +#include <utils/uniqueobjectptr.h> + #include <QAction> #include <QMenu> -#include <QScopedPointer> namespace QmlDesigner { @@ -29,7 +30,7 @@ public: private: const QString m_displayName; SelectionContext m_selectionContext; - QScopedPointer<QMenu> m_menu; + Utils::UniqueObjectPtr<QMenu> m_menu; QAction *m_action; }; diff --git a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h index d992a6a5bf..da7c5bf72e 100644 --- a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h +++ b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h @@ -69,7 +69,6 @@ const char mergeTemplateCommandId[] = "MergeTemplate"; const char goToImplementationCommandId[] = "GoToImplementation"; const char makeComponentCommandId[] = "MakeComponent"; const char editMaterialCommandId[] = "EditMaterial"; -const char editCollectionCommandId[] = "EditCollection"; const char addItemToStackedContainerCommandId[] = "AddItemToStackedContainer"; const char addTabBarToStackedContainerCommandId[] = "AddTabBarToStackedContainer"; const char increaseIndexOfStackedContainerCommandId[] = "IncreaseIndexOfStackedContainer"; @@ -128,7 +127,6 @@ const char mergeTemplateDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMen const char goToImplementationDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Go to Implementation"); const char makeComponentDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Create Component"); const char editMaterialDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit Material"); -const char editCollectionDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit Model"); const char editAnnotationsDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit Annotations"); const char addMouseAreaFillDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Add Mouse Area"); const char editIn3dViewDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit in 3D View"); @@ -214,7 +212,6 @@ enum PrioritiesEnum : int { ArrangeCategory, EditCategory, EditListModel, - EditCollection, /******** Section *****************************/ PositionSection = 2000, SnappingCategory, diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 4e32237ee9..bbe64935f6 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -61,11 +61,6 @@ inline static QString captionForModelNode(const ModelNode &modelNode) return modelNode.id(); } -inline static bool contains(const QmlItemNode &node, const QPointF &position) -{ - return node.isValid() && node.instanceSceneTransform().mapRect(node.instanceBoundingRect()).contains(position); -} - DesignerActionManagerView *DesignerActionManager::view() { return m_designerActionManagerView; @@ -118,7 +113,6 @@ void DesignerActionManager::polishActions() const Core::Context qmlDesignerNavigatorContext(Constants::C_QMLNAVIGATOR); Core::Context qmlDesignerMaterialBrowserContext(Constants::C_QMLMATERIALBROWSER); Core::Context qmlDesignerAssetsLibraryContext(Constants::C_QMLASSETSLIBRARY); - Core::Context qmlDesignerCollectionEditorContext(Constants::C_QMLCOLLECTIONEDITOR); Core::Context qmlDesignerUIContext; qmlDesignerUIContext.add(qmlDesignerFormEditorContext); @@ -126,7 +120,6 @@ void DesignerActionManager::polishActions() const qmlDesignerUIContext.add(qmlDesignerNavigatorContext); qmlDesignerUIContext.add(qmlDesignerMaterialBrowserContext); qmlDesignerUIContext.add(qmlDesignerAssetsLibraryContext); - qmlDesignerUIContext.add(qmlDesignerCollectionEditorContext); for (auto *action : actions) { if (!action->menuId().isEmpty()) { @@ -438,8 +431,8 @@ public: } for (const ModelNode &node : selectionContext().view()->allModelNodes()) { if (node != selectionContext().currentSingleSelectedNode() && node != parentNode - && contains(node, selectionContext().scenePosition()) && !node.isRootNode() - && !ModelUtils::isThisOrAncestorLocked(node)) { + && SelectionContextHelpers::contains(node, selectionContext().scenePosition()) + && !node.isRootNode() && !ModelUtils::isThisOrAncestorLocked(node)) { selectionContext().setTargetNode(node); QString what = QString(QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Select: %1")).arg(captionForModelNode(node)); ActionTemplate *selectionAction = new ActionTemplate("SELECT", what, &ModelNodeOperations::select); @@ -1971,7 +1964,7 @@ void DesignerActionManager::createDefaultDesignerActions() QKeySequence(), Priorities::ComponentActions + 1, &editIn3dView, - &singleSelectionView3D, + &SelectionContextFunctors::always, // If action is visible, it is usable &singleSelectionView3D)); addDesignerAction(new ModelNodeContextMenuAction( @@ -1993,8 +1986,8 @@ void DesignerActionManager::createDefaultDesignerActions() QKeySequence(), 44, &editMaterial, - &modelHasMaterial, - &isModel)); + &hasEditableMaterial, + &isModelOrMaterial)); addDesignerAction(new ModelNodeContextMenuAction( mergeTemplateCommandId, @@ -2016,16 +2009,6 @@ void DesignerActionManager::createDefaultDesignerActions() addDesignerAction(new EditListModelAction); - addDesignerAction(new ModelNodeContextMenuAction(editCollectionCommandId, - editCollectionDisplayName, - contextIcon(DesignerIcons::EditIcon), - rootCategory, - QKeySequence("Alt+e"), - ComponentCoreConstants::Priorities::EditCollection, - &editCollection, - &hasCollectionAsModel, - &hasCollectionAsModel)); - addDesignerAction(new ModelNodeContextMenuAction(openSignalDialogCommandId, openSignalDialogDisplayName, {}, @@ -2198,7 +2181,8 @@ void DesignerActionManager::addCustomTransitionEffectAction() void DesignerActionManager::setupIcons() { - m_designerIcons.reset(new DesignerIcons("qtds_propertyIconFont.ttf", designerIconResourcesPath())); + m_designerIcons = std::make_unique<DesignerIcons>("qtds_propertyIconFont.ttf", + designerIconResourcesPath()); } QString DesignerActionManager::designerIconResourcesPath() const diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h index 16d6219cd6..89505fcbe8 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h @@ -138,7 +138,7 @@ private: QList<AddResourceHandler> m_addResourceHandler; QList<ModelNodePreviewImageHandler> m_modelNodePreviewImageHandlers; ExternalDependenciesInterface &m_externalDependencies; - QScopedPointer<DesignerIcons> m_designerIcons; + std::unique_ptr<DesignerIcons> m_designerIcons; QList<ActionAddedInterface> m_callBacks; }; diff --git a/src/plugins/qmldesigner/components/componentcore/dialogutils.cpp b/src/plugins/qmldesigner/components/componentcore/dialogutils.cpp new file mode 100644 index 0000000000..f882ae528d --- /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 <model.h> + +#include <coreplugin/messagebox.h> + +namespace QmlDesigner { + +namespace DialogUtils { + +void showWarningForInvalidId(const QString &id) +{ + constexpr char text[] = R"( +The ID <b>'%1'</b> is invalid. + +Make sure the ID is: +<ul> +<li>Unique within the QML file.</li> +<li>Beginning with a lowercase letter.</li> +<li>Without any blank space or symbol.</li> +<li>Not a reserved QML keyword. </li> +</ul> +)"; + + 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 0000000000..3ca98016dd --- /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 <qmldesignercomponents_global.h> + +#include <QString> + +namespace QmlDesigner { + +namespace DialogUtils { + +QMLDESIGNERCOMPONENTS_EXPORT void showWarningForInvalidId(const QString &id); + +} // namespace DialogUtils +} //QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp b/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp index 8d3412e0e8..89b50c4d1a 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<ModelNode> &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); diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp index f6e18458b2..4cbebd738d 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 (QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().isImport3dPath(fileName)) { return true; + } } return false; } diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h index eb4915b1d0..6734bac568 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h @@ -25,6 +25,16 @@ namespace QmlDesigner { using SelectionContextPredicate = std::function<bool (const SelectionContext&)>; using SelectionContextOperation = std::function<void (const SelectionContext&)>; +namespace SelectionContextHelpers { + +inline bool contains(const QmlItemNode &node, const QPointF &position) +{ + return node.isValid() + && node.instanceSceneTransform().mapRect(node.instanceBoundingRect()).contains(position); +} + +} // namespace SelectionContextHelpers + namespace SelectionContextFunctors { inline bool always(const SelectionContext &) @@ -54,33 +64,24 @@ inline bool addMouseAreaFillCheck(const SelectionContext &selectionContext) return false; } -inline bool isModel(const SelectionContext &selectionState) +inline bool isModelOrMaterial(const SelectionContext &selectionState) { ModelNode node = selectionState.currentSingleSelectedNode(); - return node.metaInfo().isQtQuick3DModel(); + return node.metaInfo().isQtQuick3DModel() || node.metaInfo().isQtQuick3DMaterial(); } -inline bool modelHasMaterial(const SelectionContext &selectionState) +inline bool hasEditableMaterial(const SelectionContext &selectionState) { ModelNode node = selectionState.currentSingleSelectedNode(); + if (node.metaInfo().isQtQuick3DMaterial()) + return true; + BindingProperty prop = node.bindingProperty("materials"); return prop.exists() && (!prop.expression().isEmpty() || !prop.resolveToModelNodeList().empty()); } -inline bool hasCollectionAsModel(const SelectionContext &selectionState) -{ - if (!selectionState.isInBaseState() || !selectionState.singleNodeIsSelected()) - return false; - - const ModelNode singleSelectedNode = selectionState.currentSingleSelectedNode(); - - return singleSelectedNode.metaInfo().isQtQuickListView() - && singleSelectedNode.property("model").toBindingProperty().expression().startsWith( - "DataStore."); -} - inline bool selectionEnabled(const SelectionContext &selectionState) { return selectionState.showSelectionTools(); @@ -99,8 +100,22 @@ inline bool singleSelectionNotRoot(const SelectionContext &selectionState) inline bool singleSelectionView3D(const SelectionContext &selectionState) { - return selectionState.singleNodeIsSelected() - && selectionState.currentSingleSelectedNode().metaInfo().isQtQuick3DView3D(); + if (selectionState.singleNodeIsSelected() + && selectionState.currentSingleSelectedNode().metaInfo().isQtQuick3DView3D()) { + return true; + } + + // If currently selected node is not View3D, check if there is a View3D under the cursor. + if (!selectionState.scenePosition().isNull()) { + // Assumption is that last match in allModelNodes() list is the topmost one. + const QList<ModelNode> allNodes = selectionState.view()->allModelNodes(); + for (int i = allNodes.size() - 1; i >= 0; --i) { + if (SelectionContextHelpers::contains(allNodes[i], selectionState.scenePosition())) + return allNodes[i].metaInfo().isQtQuick3DView3D(); + } + } + + return false; } inline bool selectionHasProperty(const SelectionContext &selectionState, const char *property) diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index cebe7d7c53..bf8e78a2c7 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -817,21 +817,25 @@ void editMaterial(const SelectionContext &selectionContext) QTC_ASSERT(modelNode.isValid(), return); - BindingProperty prop = modelNode.bindingProperty("materials"); - if (!prop.exists()) - return; - AbstractView *view = selectionContext.view(); ModelNode material; - if (view->hasId(prop.expression())) { - material = view->modelNodeForId(prop.expression()); + if (modelNode.metaInfo().isQtQuick3DMaterial()) { + material = modelNode; } else { - QList<ModelNode> materials = prop.resolveToModelNodeList(); + BindingProperty prop = modelNode.bindingProperty("materials"); + if (!prop.exists()) + return; + + if (view->hasId(prop.expression())) { + material = view->modelNodeForId(prop.expression()); + } else { + QList<ModelNode> materials = prop.resolveToModelNodeList(); - if (materials.size() > 0) - material = materials.first(); + if (materials.size() > 0) + material = materials.first(); + } } if (material.isValid()) { @@ -842,30 +846,6 @@ void editMaterial(const SelectionContext &selectionContext) } } -// Open a collection in the collection editor -void editCollection(const SelectionContext &selectionContext) -{ - ModelNode modelNode = selectionContext.targetNode(); - - if (!modelNode) - modelNode = selectionContext.currentSingleSelectedNode(); - - if (!modelNode) - return; - - const QString dataStoreExpression = "DataStore."; - - BindingProperty prop = modelNode.bindingProperty("model"); - if (!prop.exists() || !prop.expression().startsWith(dataStoreExpression)) - return; - - AbstractView *view = selectionContext.view(); - const QString collectionId = prop.expression().mid(dataStoreExpression.size()); - - // to CollectionEditor... - view->emitCustomNotification("open_collection_by_id", {}, {collectionId}); -} - void addItemToStackedContainer(const SelectionContext &selectionContext) { AbstractView *view = selectionContext.view(); @@ -1138,18 +1118,12 @@ 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); - 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(); @@ -1691,16 +1665,44 @@ void updateImported3DAsset(const SelectionContext &selectionContext) void editIn3dView(const SelectionContext &selectionContext) { - if (selectionContext.view() && selectionContext.hasSingleSelectedModelNode() + if (!selectionContext.view()) + return; + + ModelNode targetNode; + + if (selectionContext.hasSingleSelectedModelNode() && selectionContext.currentSingleSelectedNode().metaInfo().isQtQuick3DView3D()) { + targetNode = selectionContext.currentSingleSelectedNode(); + } + + const QPointF scenePos = selectionContext.scenePosition(); + if (!targetNode.isValid() && !scenePos.isNull()) { + // If currently selected node is not View3D, check if there is a View3D under the cursor. + // Assumption is that last match in allModelNodes() list is the topmost one. + const QList<ModelNode> allNodes = selectionContext.view()->allModelNodes(); + for (int i = allNodes.size() - 1; i >= 0; --i) { + if (SelectionContextHelpers::contains(allNodes[i], selectionContext.scenePosition())) { + if (allNodes[i].metaInfo().isQtQuick3DView3D()) + targetNode = allNodes[i]; + break; + } + } + } + + if (targetNode.isValid()) { QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("Editor3D", true); - selectionContext.view()->emitView3DAction(View3DActionType::AlignViewToCamera, true); + if (scenePos.isNull()) { + selectionContext.view()->emitView3DAction(View3DActionType::AlignViewToCamera, true); + } else { + selectionContext.view()->emitCustomNotification("pick_3d_node_from_2d_scene", + {targetNode}, {scenePos}); + } } } bool isEffectComposerActivated() { - const QVector<ExtensionSystem::PluginSpec *> specs = ExtensionSystem::PluginManager::plugins(); + const ExtensionSystem::PluginSpecs specs = ExtensionSystem::PluginManager::plugins(); return std::find_if(specs.begin(), specs.end(), [](ExtensionSystem::PluginSpec *spec) { return spec->name() == "EffectComposer" && spec->isEffectivelyEnabled(); @@ -1727,13 +1729,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 +1770,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 +1792,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/componentcore/modelnodeoperations.h b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h index a67cef4942..26562f429a 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h @@ -92,7 +92,6 @@ void layoutGridLayout(const SelectionContext &selectionState); void goImplementation(const SelectionContext &selectionState); void addNewSignalHandler(const SelectionContext &selectionState); void editMaterial(const SelectionContext &selectionContext); -void editCollection(const SelectionContext &selectionContext); void addSignalHandlerOrGotoImplementation(const SelectionContext &selectionState, bool addAlwaysNewSlot); void removeLayout(const SelectionContext &selectionContext); void removePositioner(const SelectionContext &selectionContext); diff --git a/src/plugins/qmldesigner/components/componentcore/propertycomponentgenerator.cpp b/src/plugins/qmldesigner/components/componentcore/propertycomponentgenerator.cpp index 8cc84058d2..58326dc77a 100644 --- a/src/plugins/qmldesigner/components/componentcore/propertycomponentgenerator.cpp +++ b/src/plugins/qmldesigner/components/componentcore/propertycomponentgenerator.cpp @@ -37,7 +37,7 @@ Type getProperty(const QmlJS::SimpleReaderNode *node, const QString &name) { if (auto property = node->property(name)) { const auto &value = property.value; - if (value.type() == QVariant::List) { + if (value.typeId() == QMetaType::QVariantList) { auto list = value.toList(); if (list.size()) return list.front().value<Type>(); @@ -179,7 +179,7 @@ std::optional<PropertyComponentGenerator::Entry> createEntry(QmlJS::SimpleReader if (moduleName.isEmpty()) return {}; - auto module = model->module(moduleName); + auto module = model->module(moduleName, Storage::ModuleKind::QmlLibrary); auto typeName = getProperty<QByteArray>(node, "typeNames"); diff --git a/src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp b/src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp index 4a229564c6..24047f650f 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()}; diff --git a/src/plugins/qmldesigner/components/componentcore/theme.cpp b/src/plugins/qmldesigner/components/componentcore/theme.cpp index af495fd3b5..ef618447e7 100644 --- a/src/plugins/qmldesigner/components/componentcore/theme.cpp +++ b/src/plugins/qmldesigner/components/componentcore/theme.cpp @@ -10,6 +10,7 @@ #include <utils/stylehelper.h> +#include <qqml.h> #include <QApplication> #include <QMainWindow> #include <QPointer> @@ -18,7 +19,7 @@ #include <QQmlProperty> #include <QRegularExpression> #include <QScreen> -#include <qqml.h> +#include <QWindow> static Q_LOGGING_CATEGORY(themeLog, "qtc.qmldesigner.theme", QtWarningMsg) @@ -140,7 +141,9 @@ bool Theme::highPixelDensity() const QWindow *Theme::mainWindowHandle() const { - return Core::ICore::mainWindow()->windowHandle(); + QWindow *handle = Core::ICore::mainWindow()->windowHandle(); + QQmlEngine::setObjectOwnership(handle, QJSEngine::CppOwnership); + return handle; } QPixmap Theme::getPixmap(const QString &id) diff --git a/src/plugins/qmldesigner/components/componentcore/theme.h b/src/plugins/qmldesigner/components/componentcore/theme.h index 73184d391c..392f6c94f6 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, diff --git a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp index 05d6f5fdf0..a56735862f 100644 --- a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp @@ -7,7 +7,6 @@ #include <abstractview.h> #include <assetslibraryview.h> #include <capturingconnectionmanager.h> -#include <collectionview.h> #include <componentaction.h> #include <componentview.h> #include <contentlibraryview.h> @@ -42,14 +41,6 @@ namespace QmlDesigner { -static bool enableModelEditor() -{ - Utils::QtcSettings *settings = Core::ICore::settings(); - const Utils::Key enableModelManagerKey = "QML/Designer/UseExperimentalFeatures44"; - - return settings->value(enableModelManagerKey, false).toBool(); -} - static Q_LOGGING_CATEGORY(viewBenchmark, "qtc.viewmanager.attach", QtWarningMsg) class ViewManagerData @@ -64,19 +55,22 @@ public: : connectionManager, externalDependencies, true) - , collectionView{externalDependencies} - , contentLibraryView{externalDependencies} + , contentLibraryView{imageCache, 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} {} @@ -86,19 +80,22 @@ public: Internal::DebugView debugView; DesignerActionManagerView designerActionManagerView; NodeInstanceView nodeInstanceView; - 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<std::unique_ptr<AbstractView>> additionalViews; @@ -203,6 +200,7 @@ QList<AbstractView *> ViewManager::views() const QList<AbstractView *> ViewManager::standardViews() const { +#ifndef QTC_USE_QML_DESIGNER_LITE QList<AbstractView *> list = {&d->edit3DView, &d->formEditorView, &d->textEditorView, @@ -215,9 +213,16 @@ QList<AbstractView *> ViewManager::standardViews() const &d->textureEditorView, &d->statesEditorView, &d->designerActionManagerView}; - - if (enableModelEditor()) - list.append(&d->collectionView); +#else + QList<AbstractView *> list = {&d->formEditorView, + &d->textEditorView, + &d->assetsLibraryView, + &d->itemLibraryView, + &d->navigatorView, + &d->propertyEditorView, + &d->statesEditorView, + &d->designerActionManagerView}; +#endif if (QmlDesignerPlugin::instance() ->settings() @@ -384,19 +389,21 @@ QList<WidgetInfo> ViewManager::widgetInfos() const { QList<WidgetInfo> 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()); if (checkEnterpriseLicense()) widgetInfoList.append(d->contentLibraryView.widgetInfo()); diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp index f871bae84b..2cee7b0f97 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp @@ -22,6 +22,7 @@ namespace QmlDesigner { BindingModel::BindingModel(ConnectionView *view) : m_connectionView(view) + , m_delegate(*this) { setHorizontalHeaderLabels(BindingModelItem::headerLabels()); } @@ -246,11 +247,8 @@ void BindingModel::addModelNode(const ModelNode &node) appendRow(new BindingModelItem(property)); } -BindingModelBackendDelegate::BindingModelBackendDelegate() - : m_targetNode() - , m_property() - , m_sourceNode() - , m_sourceNodeProperty() +BindingModelBackendDelegate::BindingModelBackendDelegate(BindingModel &model) + : m_model{model} { connect(&m_sourceNode, &StudioQmlComboBoxBackend::activated, this, [this] { sourceNodeChanged(); @@ -322,17 +320,14 @@ StudioQmlComboBoxBackend *BindingModelBackendDelegate::sourceProperty() void BindingModelBackendDelegate::sourceNodeChanged() { - BindingModel *model = qobject_cast<BindingModel *>(parent()); - QTC_ASSERT(model, return); - - ConnectionView *view = model->connectionView(); + ConnectionView *view = m_model.connectionView(); QTC_ASSERT(view, return); QTC_ASSERT(view->isAttached(), return ); const QString sourceNode = m_sourceNode.currentText(); const QString sourceProperty = m_sourceNodeProperty.currentText(); - BindingProperty targetProperty = model->currentProperty(); + BindingProperty targetProperty = m_model.currentProperty(); QStringList properties = availableSourceProperties(sourceNode, targetProperty, view); if (!properties.contains(sourceProperty)) { @@ -351,9 +346,6 @@ void BindingModelBackendDelegate::sourcePropertyNameChanged() const return; auto commit = [this, sourceProperty]() { - BindingModel *model = qobject_cast<BindingModel *>(parent()); - QTC_ASSERT(model, return); - const QString sourceNode = m_sourceNode.currentText(); QString expression; if (sourceProperty.isEmpty()) @@ -361,8 +353,8 @@ void BindingModelBackendDelegate::sourcePropertyNameChanged() const else expression = sourceNode + QLatin1String(".") + sourceProperty; - int row = model->currentIndex(); - model->commitExpression(row, expression); + int row = m_model.currentIndex(); + m_model.commitExpression(row, expression); }; callLater(commit); @@ -371,11 +363,9 @@ void BindingModelBackendDelegate::sourcePropertyNameChanged() const void BindingModelBackendDelegate::targetPropertyNameChanged() const { auto commit = [this] { - BindingModel *model = qobject_cast<BindingModel *>(parent()); - QTC_ASSERT(model, return); const PropertyName propertyName = m_property.currentText().toUtf8(); - int row = model->currentIndex(); - model->commitPropertyName(row, propertyName); + int row = m_model.currentIndex(); + m_model.commitPropertyName(row, propertyName); }; callLater(commit); diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h index b57cc5c958..69b137e78a 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h @@ -30,7 +30,7 @@ signals: void targetNodeChanged(); public: - BindingModelBackendDelegate(); + BindingModelBackendDelegate(class BindingModel &model); void update(const BindingProperty &property, AbstractView *view); @@ -44,6 +44,7 @@ private: StudioQmlComboBoxBackend *sourceNode(); StudioQmlComboBoxBackend *sourceProperty(); + BindingModel &m_model; QString m_targetNode; StudioQmlComboBoxBackend m_property; StudioQmlComboBoxBackend m_sourceNode; diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp index 57ca619a70..3cbfb8c038 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp @@ -212,7 +212,8 @@ bool isDynamicVariantPropertyType(const TypeName &type) { // "variant" is considered value type as it is initialized as one. // This may need to change if we provide any kind of proper editor for it. - static const QSet<TypeName> valueTypes{"int", "real", "color", "string", "bool", "url", "var", "variant"}; + static const QSet<TypeName> valueTypes{ + "int", "real", "double", "color", "string", "bool", "url", "var", "variant"}; return valueTypes.contains(type); } diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp index 9fdd3daec3..fa29c6c8a1 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp @@ -25,7 +25,7 @@ namespace QmlDesigner { DynamicPropertiesModel::DynamicPropertiesModel(bool exSelection, AbstractView *view) : m_view(view) - , m_delegate(std::make_unique<DynamicPropertiesModelBackendDelegate>()) + , m_delegate(std::make_unique<DynamicPropertiesModelBackendDelegate>(*this)) , m_explicitSelection(exSelection) { setHorizontalHeaderLabels(DynamicPropertiesItem::headerLabels()); @@ -382,8 +382,8 @@ void DynamicPropertiesModel::setSelectedNode(const ModelNode &node) reset(); } -DynamicPropertiesModelBackendDelegate::DynamicPropertiesModelBackendDelegate() - : m_internalNodeId(std::nullopt) +DynamicPropertiesModelBackendDelegate::DynamicPropertiesModelBackendDelegate(DynamicPropertiesModel &model) + : m_model(model) { m_type.setModel({"int", "bool", "var", "real", "string", "url", "color"}); connect(&m_type, &StudioQmlComboBoxBackend::activated, this, [this] { handleTypeChanged(); }); @@ -411,32 +411,26 @@ void DynamicPropertiesModelBackendDelegate::update(const AbstractProperty &prope void DynamicPropertiesModelBackendDelegate::handleTypeChanged() { - DynamicPropertiesModel *model = qobject_cast<DynamicPropertiesModel *>(parent()); - QTC_ASSERT(model, return); - const PropertyName name = m_name.text().toUtf8(); - int current = model->currentIndex(); + int current = m_model.currentIndex(); const TypeName type = m_type.currentText().toUtf8(); - model->commitPropertyType(current, type); + m_model.commitPropertyType(current, type); // The order might have changed! - model->setCurrent(m_internalNodeId.value_or(-1), name); + m_model.setCurrent(m_internalNodeId.value_or(-1), name); } void DynamicPropertiesModelBackendDelegate::handleNameChanged() { - DynamicPropertiesModel *model = qobject_cast<DynamicPropertiesModel *>(parent()); - QTC_ASSERT(model, return); - const PropertyName name = m_name.text().toUtf8(); QTC_ASSERT(!name.isEmpty(), return); - int current = model->currentIndex(); - model->commitPropertyName(current, name); + int current = m_model.currentIndex(); + m_model.commitPropertyName(current, name); // The order might have changed! - model->setCurrent(m_internalNodeId.value_or(-1), name); + m_model.setCurrent(m_internalNodeId.value_or(-1), name); } // TODO: Maybe replace with utils typeConvertVariant? @@ -456,12 +450,9 @@ QVariant valueFromText(const QString &value, const QString &type) void DynamicPropertiesModelBackendDelegate::handleValueChanged() { - DynamicPropertiesModel *model = qobject_cast<DynamicPropertiesModel *>(parent()); - QTC_ASSERT(model, return); - - int current = model->currentIndex(); + int current = m_model.currentIndex(); QVariant value = valueFromText(m_value.text(), m_type.currentText()); - model->commitPropertyValue(current, value); + m_model.commitPropertyValue(current, value); } QString DynamicPropertiesModelBackendDelegate::targetNode() const diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h index c2beed8730..071c72bef8 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h @@ -97,7 +97,7 @@ class DynamicPropertiesModelBackendDelegate : public QObject Q_PROPERTY(StudioQmlTextBackend *value READ value CONSTANT) public: - DynamicPropertiesModelBackendDelegate(); + DynamicPropertiesModelBackendDelegate(DynamicPropertiesModel &model); void update(const AbstractProperty &property); @@ -116,6 +116,7 @@ private: StudioQmlTextBackend *value(); QString targetNode() const; + DynamicPropertiesModel &m_model; std::optional<int> m_internalNodeId; StudioQmlComboBoxBackend m_type; StudioQmlTextBackend m_name; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp index 9e6bdd03b9..6388c93fa9 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp @@ -3,83 +3,67 @@ #include "contentlibrarybundleimporter.h" -#include "documentmanager.h" -#include "import.h" -#include "model.h" -#include "qmldesignerconstants.h" -#include "qmldesignerplugin.h" -#include "rewritingexception.h" +#include <documentmanager.h> +#include <import.h> +#include <model.h> +#include <nodemetainfo.h> +#include <qmldesignerconstants.h> +#include <qmldesignerplugin.h> +#include <rewritingexception.h> #include <qmljs/qmljsmodelmanagerinterface.h> -#include <QFileInfo> #include <QJsonDocument> #include <QJsonObject> #include <QStringList> using namespace Utils; -namespace QmlDesigner::Internal { +namespace QmlDesigner { -ContentLibraryBundleImporter::ContentLibraryBundleImporter(const QString &bundleDir, - const QString &bundleId, - const QStringList &sharedFiles, - QObject *parent) +ContentLibraryBundleImporter::ContentLibraryBundleImporter(QObject *parent) : QObject(parent) - , m_bundleDir(FilePath::fromString(bundleDir)) - , m_bundleId(bundleId) - , m_sharedFiles(sharedFiles) { m_importTimer.setInterval(200); connect(&m_importTimer, &QTimer::timeout, this, &ContentLibraryBundleImporter::handleImportTimer); - 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. // Note that there is also an asynchronous portion to the import, which will only // be done if this method returns success. Once the asynchronous portion of the // import is completed, importFinished signal will be emitted. -QString ContentLibraryBundleImporter::importComponent(const QString &qmlFile, +QString ContentLibraryBundleImporter::importComponent(const QString &bundleDir, + const TypeName &type, + const QString &qmlFile, const QStringList &files) { - FilePath bundleImportPath = resolveBundleImportPath(); + QString module = QString::fromLatin1(type.left(type.lastIndexOf('.'))); + m_bundleId = module.mid(module.lastIndexOf('.') + 1); + + FilePath bundleDirPath = FilePath::fromString(bundleDir); // source dir + FilePath bundleImportPath = resolveBundleImportPath(m_bundleId); // target dir + if (bundleImportPath.isEmpty()) return "Failed to resolve bundle import folder"; - bool bundleImportPathExists = bundleImportPath.exists(); - - if (!bundleImportPathExists && !bundleImportPath.createDir()) + if (!bundleImportPath.exists() && !bundleImportPath.createDir()) return QStringLiteral("Failed to create bundle import folder: '%1'").arg(bundleImportPath.toString()); - for (const QString &file : std::as_const(m_sharedFiles)) { - FilePath target = bundleImportPath.resolvePath(file); - if (!target.exists()) { - FilePath parentDir = target.parentDir(); - if (!parentDir.exists() && !parentDir.createDir()) - return QStringLiteral("Failed to create folder for: '%1'").arg(target.toString()); - FilePath source = m_bundleDir.resolvePath(file); - if (!source.copyFile(target)) - return QStringLiteral("Failed to copy shared file: '%1'").arg(source.toString()); - } - } - - FilePath qmldirPath = bundleImportPath.resolvePath(QStringLiteral("qmldir")); + FilePath qmldirPath = bundleImportPath.pathAppended("qmldir"); QString qmldirContent = QString::fromUtf8(qmldirPath.fileContents().value_or(QByteArray())); if (qmldirContent.isEmpty()) { qmldirContent.append("module "); - qmldirContent.append(m_moduleName); + qmldirContent.append(module); qmldirContent.append('\n'); } - FilePath qmlSourceFile = bundleImportPath.resolvePath(FilePath::fromString(qmlFile)); + FilePath qmlSourceFile = bundleImportPath.pathAppended(qmlFile); const bool qmlFileExists = qmlSourceFile.exists(); const QString qmlType = qmlSourceFile.baseName(); - const QString fullTypeName = QStringLiteral("%1.%2.%3") - .arg(QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1), m_bundleId, qmlType); - if (m_pendingTypes.contains(fullTypeName) && !m_pendingTypes[fullTypeName]) - return QStringLiteral("Unable to import while unimporting the same type: '%1'").arg(fullTypeName); + + if (m_pendingTypes.contains(type) && !m_pendingTypes.value(type)) + return QStringLiteral("Unable to import while unimporting the same type: '%1'").arg(QLatin1String(type)); + if (!qmldirContent.contains(qmlFile)) { qmldirContent.append(qmlType); qmldirContent.append(" 1.0 "); @@ -92,12 +76,12 @@ QString ContentLibraryBundleImporter::importComponent(const QString &qmlFile, allFiles.append(files); allFiles.append(qmlFile); for (const QString &file : std::as_const(allFiles)) { - FilePath target = bundleImportPath.resolvePath(file); + FilePath target = bundleImportPath.pathAppended(file); FilePath parentDir = target.parentDir(); if (!parentDir.exists() && !parentDir.createDir()) return QStringLiteral("Failed to create folder for: '%1'").arg(target.toString()); - FilePath source = m_bundleDir.resolvePath(file); + FilePath source = bundleDirPath.pathAppended(file); if (target.exists()) { if (source.lastModified() == target.lastModified()) continue; @@ -126,23 +110,23 @@ QString ContentLibraryBundleImporter::importComponent(const QString &qmlFile, if (!model) return "Model not available, cannot add import statement or update code model"; - Import import = Import::createLibraryImport(m_moduleName, "1.0"); + Import import = Import::createLibraryImport(module, "1.0"); if (!model->hasImport(import)) { if (model->possibleImports().contains(import)) { - m_importAddPending = false; + m_pendingImport.clear(); try { model->changeImports({import}, {}); } catch (const RewritingException &) { // No point in trying to add import asynchronously either, so just fail out - return QStringLiteral("Failed to add import statement for: '%1'").arg(m_moduleName); + return QStringLiteral("Failed to add import statement for: '%1'").arg(module); } } else { // If import is not yet possible, import statement needs to be added asynchronously to // avoid errors, as code model update takes a while. - m_importAddPending = true; + m_pendingImport = module; } } - m_pendingTypes.insert(fullTypeName, true); + m_pendingTypes.insert(type, true); m_importTimerCount = 0; m_importTimer.start(); @@ -154,17 +138,19 @@ void ContentLibraryBundleImporter::handleImportTimer() auto handleFailure = [this] { m_importTimer.stop(); m_fullReset = false; - m_importAddPending = false; + m_pendingImport.clear(); m_importTimerCount = 0; // Emit dummy finished signals for all pending types - const QStringList pendingTypes = m_pendingTypes.keys(); - for (const QString &pendingType : pendingTypes) { + const QList<TypeName> pendingTypes = m_pendingTypes.keys(); + for (const TypeName &pendingType : pendingTypes) { m_pendingTypes.remove(pendingType); - if (m_pendingTypes[pendingType]) - emit importFinished({}); + if (m_pendingTypes.value(pendingType)) + emit importFinished({}, m_bundleId); else - emit unimportFinished({}); + emit unimportFinished({}, m_bundleId); + + m_bundleId.clear(); } }; @@ -186,12 +172,12 @@ void ContentLibraryBundleImporter::handleImportTimer() QmlDesignerPlugin::instance()->documentManager().resetPossibleImports(); - if (m_importAddPending) { + if (!m_pendingImport.isEmpty()) { try { - Import import = Import::createLibraryImport(m_moduleName, "1.0"); + Import import = Import::createLibraryImport(m_pendingImport, "1.0"); if (model->possibleImports().contains(import)) { model->changeImports({import}, {}); - m_importAddPending = false; + m_pendingImport.clear(); } } catch (const RewritingException &) { // Import adding is unlikely to succeed later, either, so just bail out @@ -201,21 +187,23 @@ void ContentLibraryBundleImporter::handleImportTimer() } // Detect when the code model has the new material(s) fully available - const QStringList pendingTypes = m_pendingTypes.keys(); - for (const QString &pendingType : pendingTypes) { - NodeMetaInfo metaInfo = model->metaInfo(pendingType.toUtf8()); - const bool isImport = m_pendingTypes[pendingType]; + const QList<TypeName> pendingTypes = m_pendingTypes.keys(); + for (const TypeName &pendingType : pendingTypes) { + NodeMetaInfo metaInfo = model->metaInfo(pendingType); + const bool isImport = m_pendingTypes.value(pendingType); const bool typeComplete = metaInfo.isValid() && !metaInfo.prototypes().empty(); if (isImport == typeComplete) { m_pendingTypes.remove(pendingType); if (isImport) #ifdef QDS_USE_PROJECTSTORAGE - emit importFinished(pendingType.toUtf8()); + emit importFinished(pendingType, m_bundleId); #else - emit importFinished(metaInfo); + emit importFinished(metaInfo, m_bundleId); #endif else - emit unimportFinished(metaInfo); + emit unimportFinished(metaInfo, m_bundleId); + + m_bundleId.clear(); } } @@ -225,10 +213,10 @@ void ContentLibraryBundleImporter::handleImportTimer() } } -QVariantHash ContentLibraryBundleImporter::loadAssetRefMap(const Utils::FilePath &bundlePath) +QVariantHash ContentLibraryBundleImporter::loadAssetRefMap(const FilePath &bundlePath) { FilePath assetRefPath = bundlePath.resolvePath(QLatin1String(Constants::COMPONENT_BUNDLES_ASSET_REF_FILE)); - const Utils::expected_str<QByteArray> content = assetRefPath.fileContents(); + const expected_str<QByteArray> content = assetRefPath.fileContents(); if (content) { QJsonParseError error; QJsonDocument bundleDataJsonDoc = QJsonDocument::fromJson(*content, &error); @@ -242,7 +230,7 @@ QVariantHash ContentLibraryBundleImporter::loadAssetRefMap(const Utils::FilePath return {}; } -void ContentLibraryBundleImporter::writeAssetRefMap(const Utils::FilePath &bundlePath, +void ContentLibraryBundleImporter::writeAssetRefMap(const FilePath &bundlePath, const QVariantHash &assetRefMap) { FilePath assetRefPath = bundlePath.resolvePath(QLatin1String(Constants::COMPONENT_BUNDLES_ASSET_REF_FILE)); @@ -253,9 +241,14 @@ void ContentLibraryBundleImporter::writeAssetRefMap(const Utils::FilePath &bundl } } -QString ContentLibraryBundleImporter::unimportComponent(const QString &qmlFile) +QString ContentLibraryBundleImporter::unimportComponent(const TypeName &type, const QString &qmlFile) { - FilePath bundleImportPath = resolveBundleImportPath(); + QString module = QString::fromLatin1(type.left(type.lastIndexOf('.'))); + m_bundleId = module.mid(module.lastIndexOf('.') + 1); + + emit aboutToUnimport(type, m_bundleId); + + FilePath bundleImportPath = resolveBundleImportPath(m_bundleId); if (bundleImportPath.isEmpty()) return QStringLiteral("Failed to resolve bundle import folder for: '%1'").arg(qmlFile); @@ -274,10 +267,10 @@ QString ContentLibraryBundleImporter::unimportComponent(const QString &qmlFile) QByteArray newContent; QString qmlType = qmlFilePath.baseName(); - const QString fullTypeName = QStringLiteral("%1.%2.%3") - .arg(QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1), m_bundleId, qmlType); - if (m_pendingTypes.contains(fullTypeName) && m_pendingTypes[fullTypeName]) - return QStringLiteral("Unable to unimport while importing the same type: '%1'").arg(fullTypeName); + if (m_pendingTypes.contains(type) && m_pendingTypes.value(type)) { + return QStringLiteral("Unable to unimport while importing the same type: '%1'") + .arg(QString::fromLatin1(type)); + } if (qmldirContent) { int typeIndex = qmldirContent->indexOf(qmlType.toUtf8()); @@ -293,7 +286,7 @@ QString ContentLibraryBundleImporter::unimportComponent(const QString &qmlFile) } } - m_pendingTypes.insert(fullTypeName, false); + m_pendingTypes.insert(type, false); QVariantHash assetRefMap = loadAssetRefMap(bundleImportPath); bool writeAssetRefs = false; @@ -327,7 +320,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(module, "1.0"); if (model->imports().contains(import)) model->changeImports({}, {import}); } @@ -340,18 +333,14 @@ QString ContentLibraryBundleImporter::unimportComponent(const QString &qmlFile) return {}; } -FilePath ContentLibraryBundleImporter::resolveBundleImportPath() +FilePath ContentLibraryBundleImporter::resolveBundleImportPath(const QString &bundleId) { - 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 {}; - return bundleImportPath.resolvePath(projectBundlePath); + return bundleImportPath.resolvePath(bundleId); } -} // namespace QmlDesigner::Internal +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h index 3aff09fe34..8155311e3e 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h @@ -3,59 +3,53 @@ #pragma once -#include <utils/filepath.h> +#include <modelfwd.h> -#include "nodemetainfo.h" +#include <utils/filepath.h> #include <QTimer> #include <QVariantHash> -QT_BEGIN_NAMESPACE -QT_END_NAMESPACE +namespace QmlDesigner { -namespace QmlDesigner::Internal { +class NodeMetaInfo; class ContentLibraryBundleImporter : public QObject { Q_OBJECT public: - ContentLibraryBundleImporter(const QString &bundleDir, - const QString &bundleId, - const QStringList &sharedFiles, - QObject *parent = nullptr); + ContentLibraryBundleImporter(QObject *parent = nullptr); ~ContentLibraryBundleImporter() = default; - QString importComponent(const QString &qmlFile, + QString importComponent(const QString &bundleDir, const TypeName &type, const QString &qmlFile, const QStringList &files); - QString unimportComponent(const QString &qmlFile); - Utils::FilePath resolveBundleImportPath(); + QString unimportComponent(const TypeName &type, const QString &qmlFile); + Utils::FilePath resolveBundleImportPath(const QString &bundleId); signals: // The metaInfo parameter will be invalid if an error was encountered during // asynchronous part of the import. In this case all remaining pending imports have been // terminated, and will not receive separate importFinished notifications. #ifdef QDS_USE_PROJECTSTORAGE - void importFinished(const QmlDesigner::TypeName &typeName); + void importFinished(const QmlDesigner::TypeName &typeName, const QString &bundleId); #else - void importFinished(const QmlDesigner::NodeMetaInfo &metaInfo); + void importFinished(const QmlDesigner::NodeMetaInfo &metaInfo, const QString &bundleId); #endif - void unimportFinished(const QmlDesigner::NodeMetaInfo &metaInfo); + void unimportFinished(const QmlDesigner::NodeMetaInfo &metaInfo, const QString &bundleId); + void aboutToUnimport(const TypeName &type, const QString &bundleId); private: void handleImportTimer(); QVariantHash loadAssetRefMap(const Utils::FilePath &bundlePath); void writeAssetRefMap(const Utils::FilePath &bundlePath, const QVariantHash &assetRefMap); - Utils::FilePath m_bundleDir; - QString m_bundleId; - QString m_moduleName; - QStringList m_sharedFiles; QTimer m_importTimer; int m_importTimerCount = 0; - bool m_importAddPending = false; + QString m_pendingImport; + QString m_bundleId; bool m_fullReset = false; - QHash<QString, bool> m_pendingTypes; // <type, isImport> + QHash<TypeName, bool> m_pendingTypes; // <type, isImport> }; -} // namespace QmlDesigner::Internal +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.cpp deleted file mode 100644 index f572fbe65f..0000000000 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.cpp +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "contentlibraryeffect.h" - -#include <QFileInfo> - -namespace QmlDesigner { - -ContentLibraryEffect::ContentLibraryEffect(QObject *parent, - const QString &name, - const QString &qml, - const TypeName &type, - const QUrl &icon, - const QStringList &files) - : QObject(parent), m_name(name), m_qml(qml), m_type(type), m_icon(icon), m_files(files) -{ - m_allFiles = m_files; - m_allFiles.push_back(m_qml); -} - -bool ContentLibraryEffect::filter(const QString &searchText) -{ - if (m_visible != m_name.contains(searchText, Qt::CaseInsensitive)) { - m_visible = !m_visible; - emit itemVisibleChanged(); - } - - return m_visible; -} - -QUrl ContentLibraryEffect::icon() const -{ - return m_icon; -} - -QString ContentLibraryEffect::qml() const -{ - return m_qml; -} - -TypeName ContentLibraryEffect::type() const -{ - return m_type; -} - -QStringList ContentLibraryEffect::files() const -{ - return m_files; -} - -bool ContentLibraryEffect::visible() const -{ - return m_visible; -} - -bool ContentLibraryEffect::setImported(bool imported) -{ - if (m_imported != imported) { - m_imported = imported; - emit itemImportedChanged(); - return true; - } - - return false; -} - -bool ContentLibraryEffect::imported() const -{ - return m_imported; -} - -QStringList ContentLibraryEffect::allFiles() const -{ - return m_allFiles; -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.cpp index 38e6eed3da..f904775d52 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.cpp @@ -3,14 +3,12 @@ #include "contentlibraryeffectscategory.h" -#include "contentlibraryeffect.h" - namespace QmlDesigner { ContentLibraryEffectsCategory::ContentLibraryEffectsCategory(QObject *parent, const QString &name) : QObject(parent), m_name(name) {} -void ContentLibraryEffectsCategory::addBundleItem(ContentLibraryEffect *bundleItem) +void ContentLibraryEffectsCategory::addBundleItem(ContentLibraryItem *bundleItem) { m_categoryItems.append(bundleItem); } @@ -19,7 +17,7 @@ bool ContentLibraryEffectsCategory::updateImportedState(const QStringList &impor { bool changed = false; - for (ContentLibraryEffect *item : std::as_const(m_categoryItems)) + for (ContentLibraryItem *item : std::as_const(m_categoryItems)) changed |= item->setImported(importedItems.contains(item->qml().chopped(4))); return changed; @@ -28,7 +26,7 @@ bool ContentLibraryEffectsCategory::updateImportedState(const QStringList &impor bool ContentLibraryEffectsCategory::filter(const QString &searchText) { bool visible = false; - for (ContentLibraryEffect *item : std::as_const(m_categoryItems)) + for (ContentLibraryItem *item : std::as_const(m_categoryItems)) visible |= item->filter(searchText); if (visible != m_visible) { @@ -55,7 +53,7 @@ bool ContentLibraryEffectsCategory::expanded() const return m_expanded; } -QList<ContentLibraryEffect *> ContentLibraryEffectsCategory::categoryItems() const +QList<ContentLibraryItem *> ContentLibraryEffectsCategory::categoryItems() const { return m_categoryItems; } diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.h index 79737c1ec2..9f56de3c6b 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.h @@ -3,12 +3,12 @@ #pragma once +#include "contentlibraryitem.h" + #include <QObject> namespace QmlDesigner { -class ContentLibraryEffect; - class ContentLibraryEffectsCategory : public QObject { Q_OBJECT @@ -16,20 +16,20 @@ class ContentLibraryEffectsCategory : public QObject Q_PROPERTY(QString bundleCategoryName MEMBER m_name CONSTANT) Q_PROPERTY(bool bundleCategoryVisible MEMBER m_visible NOTIFY categoryVisibleChanged) Q_PROPERTY(bool bundleCategoryExpanded MEMBER m_expanded NOTIFY categoryExpandChanged) - Q_PROPERTY(QList<ContentLibraryEffect *> bundleCategoryItems MEMBER m_categoryItems + Q_PROPERTY(QList<ContentLibraryItem *> bundleCategoryItems MEMBER m_categoryItems NOTIFY categoryItemsChanged) public: ContentLibraryEffectsCategory(QObject *parent, const QString &name); - void addBundleItem(ContentLibraryEffect *bundleItem); + void addBundleItem(ContentLibraryItem *bundleItem); bool updateImportedState(const QStringList &importedMats); bool filter(const QString &searchText); QString name() const; bool visible() const; bool expanded() const; - QList<ContentLibraryEffect *> categoryItems() const; + QList<ContentLibraryItem *> categoryItems() const; signals: void categoryVisibleChanged(); @@ -41,7 +41,7 @@ private: bool m_visible = true; bool m_expanded = true; - QList<ContentLibraryEffect *> m_categoryItems; + QList<ContentLibraryItem *> m_categoryItems; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp index 6b1de2d2a7..e0fe2b6c54 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp @@ -4,11 +4,11 @@ #include "contentlibraryeffectsmodel.h" #include "contentlibrarybundleimporter.h" -#include "contentlibraryeffect.h" #include "contentlibraryeffectscategory.h" +#include "contentlibraryitem.h" #include "contentlibrarywidget.h" -#include "qmldesignerconstants.h" +#include <qmldesignerplugin.h> #include <utils/algorithm.h> #include <utils/qtcassert.h> #include <utils/hostosinfo.h> @@ -63,6 +63,11 @@ bool ContentLibraryEffectsModel::isValidIndex(int idx) const return idx > -1 && idx < rowCount(); } +QString ContentLibraryEffectsModel::bundleId() const +{ + return m_bundleId; +} + void ContentLibraryEffectsModel::updateIsEmpty() { bool anyCatVisible = Utils::anyOf(m_bundleCategories, [&](ContentLibraryEffectsCategory *cat) { @@ -88,49 +93,23 @@ QHash<int, QByteArray> ContentLibraryEffectsModel::roleNames() const return roles; } -void ContentLibraryEffectsModel::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 bundleItemImported(typeName); - }); -#else - connect(m_importer, - &Internal::ContentLibraryBundleImporter::importFinished, - this, - [&](const QmlDesigner::NodeMetaInfo &metaInfo) { - m_importerRunning = false; - emit importerRunningChanged(); - if (metaInfo.isValid()) - emit bundleItemImported(metaInfo); - }); -#endif - - connect(m_importer, &Internal::ContentLibraryBundleImporter::unimportFinished, this, - [&](const QmlDesigner::NodeMetaInfo &metaInfo) { - Q_UNUSED(metaInfo) - m_importerRunning = false; - emit importerRunningChanged(); - emit bundleItemUnimported(metaInfo); - }); - - resetModel(); - updateIsEmpty(); -} - void ContentLibraryEffectsModel::loadBundle() { - if (m_bundleExists || m_probeBundleDir) + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + + if (m_probeBundleDir || (m_bundleExists && m_bundleId == compUtils.effectsBundleId())) return; + // clean up + qDeleteAll(m_bundleCategories); + m_bundleCategories.clear(); + m_bundleExists = false; + m_isEmpty = true; + m_probeBundleDir = false; + m_bundleObj = {}; + m_bundleId.clear(); + m_bundlePath.clear(); + QDir bundleDir; if (!qEnvironmentVariable("EFFECT_BUNDLE_PATH").isEmpty()) @@ -145,30 +124,32 @@ void ContentLibraryEffectsModel::loadBundle() while (!bundleDir.cd("effect_bundle") && bundleDir.cdUp()) ; // do nothing - if (bundleDir.dirName() != "effect_bundle") // bundlePathDir not found + if (bundleDir.dirName() != "effect_bundle") { // bundlePathDir not found + resetModel(); return; + } } QString bundlePath = bundleDir.filePath("effect_bundle.json"); - if (m_bundleObj.isEmpty()) { - QFile propsFile(bundlePath); - - if (!propsFile.open(QIODevice::ReadOnly)) { - qWarning("Couldn't open effect_bundle.json"); - return; - } + QFile bundleFile(bundlePath); + if (!bundleFile.open(QIODevice::ReadOnly)) { + qWarning("Couldn't open effect_bundle.json"); + resetModel(); + return; + } - QJsonDocument bundleJsonDoc = QJsonDocument::fromJson(propsFile.readAll()); - if (bundleJsonDoc.isNull()) { - qWarning("Invalid effect_bundle.json file"); - return; - } else { - m_bundleObj = bundleJsonDoc.object(); - } + QJsonDocument bundleJsonDoc = QJsonDocument::fromJson(bundleFile.readAll()); + if (bundleJsonDoc.isNull()) { + qWarning("Invalid effect_bundle.json file"); + resetModel(); + return; } - QString bundleId = m_bundleObj.value("id").toString(); + m_bundleObj = bundleJsonDoc.object(); + + QString bundleType = compUtils.effectsBundleType(); + m_bundleId = compUtils.effectsBundleId(); const QJsonObject catsObj = m_bundleObj.value("categories").toObject(); const QStringList categories = catsObj.keys(); @@ -176,38 +157,36 @@ void ContentLibraryEffectsModel::loadBundle() auto category = new ContentLibraryEffectsCategory(this, cat); const QJsonObject itemsObj = catsObj.value(cat).toObject(); - const QStringList items = itemsObj.keys(); - for (const QString &item : items) { - const QJsonObject itemObj = itemsObj.value(item).toObject(); + const QStringList itemsNames = itemsObj.keys(); + for (const QString &itemName : itemsNames) { + const QJsonObject itemObj = itemsObj.value(itemName).toObject(); QStringList files; const QJsonArray assetsArr = itemObj.value("files").toArray(); - for (const auto /*QJson{Const,}ValueRef*/ &asset : assetsArr) + for (const QJsonValueConstRef &asset : assetsArr) files.append(asset.toString()); QUrl icon = QUrl::fromLocalFile(bundleDir.filePath(itemObj.value("icon").toString())); 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") + .arg(bundleType, qml.chopped(4)).toLatin1(); // chopped(4): remove .qml - auto bundleItem = new ContentLibraryEffect(category, item, qml, type, icon, files); + auto bundleItem = new ContentLibraryItem(category, itemName, qml, type, icon, files); category->addBundleItem(bundleItem); } m_bundleCategories.append(category); } - QStringList sharedFiles; + m_bundleSharedFiles.clear(); 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); + for (const QJsonValueConstRef &file : sharedFilesArr) + m_bundleSharedFiles.append(file.toString()); + m_bundlePath = bundleDir.path(); m_bundleExists = true; - emit bundleExistsChanged(); + updateIsEmpty(); + resetModel(); } bool ContentLibraryEffectsModel::hasRequiredQuick3DImport() const @@ -220,11 +199,6 @@ bool ContentLibraryEffectsModel::bundleExists() const return m_bundleExists; } -Internal::ContentLibraryBundleImporter *ContentLibraryEffectsModel::bundleImporter() const -{ - return m_importer; -} - void ContentLibraryEffectsModel::setSearchText(const QString &searchText) { QString lowerSearchText = searchText.toLower(); @@ -277,30 +251,26 @@ void ContentLibraryEffectsModel::resetModel() endResetModel(); } -void ContentLibraryEffectsModel::addInstance(ContentLibraryEffect *bundleItem) +void ContentLibraryEffectsModel::addInstance(ContentLibraryItem *bundleItem) { - QString err = m_importer->importComponent(bundleItem->qml(), bundleItem->files()); + QString err = m_widget->importer()->importComponent(m_bundlePath, bundleItem->type(), + bundleItem->qml(), + bundleItem->files() + m_bundleSharedFiles); - if (err.isEmpty()) { - m_importerRunning = true; - emit importerRunningChanged(); - } else { + if (err.isEmpty()) + m_widget->setImporterRunning(true); + else qWarning() << __FUNCTION__ << err; - } } -void ContentLibraryEffectsModel::removeFromProject(ContentLibraryEffect *bundleItem) +void ContentLibraryEffectsModel::removeFromProject(ContentLibraryItem *bundleItem) { - emit bundleItemAboutToUnimport(bundleItem->type()); + QString err = m_widget->importer()->unimportComponent(bundleItem->type(), bundleItem->qml()); - QString err = m_importer->unimportComponent(bundleItem->qml()); - - if (err.isEmpty()) { - m_importerRunning = true; - emit importerRunningChanged(); - } else { + if (err.isEmpty()) + m_widget->setImporterRunning(true); + else qWarning() << __FUNCTION__ << err; - } } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h index 5d67ac3da8..5bcb857fca 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h @@ -3,22 +3,15 @@ #pragma once -#include "nodemetainfo.h" - #include <QAbstractListModel> -#include <QDir> #include <QJsonObject> namespace QmlDesigner { -class ContentLibraryEffect; +class ContentLibraryItem; class ContentLibraryEffectsCategory; class ContentLibraryWidget; -namespace Internal { -class ContentLibraryBundleImporter; -} - class ContentLibraryEffectsModel : public QAbstractListModel { Q_OBJECT @@ -26,7 +19,6 @@ class ContentLibraryEffectsModel : public QAbstractListModel Q_PROPERTY(bool bundleExists READ bundleExists NOTIFY bundleExistsChanged) Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) Q_PROPERTY(bool hasRequiredQuick3DImport READ hasRequiredQuick3DImport NOTIFY hasRequiredQuick3DImportChanged) - Q_PROPERTY(bool importerRunning MEMBER m_importerRunning NOTIFY importerRunningChanged) public: ContentLibraryEffectsModel(ContentLibraryWidget *parent = nullptr); @@ -49,46 +41,33 @@ public: void resetModel(); void updateIsEmpty(); - Internal::ContentLibraryBundleImporter *bundleImporter() const; + Q_INVOKABLE void addInstance(QmlDesigner::ContentLibraryItem *bundleItem); + Q_INVOKABLE void removeFromProject(QmlDesigner::ContentLibraryItem *bundleItem); - Q_INVOKABLE void addInstance(QmlDesigner::ContentLibraryEffect *bundleItem); - Q_INVOKABLE void removeFromProject(QmlDesigner::ContentLibraryEffect *bundleItem); + QString bundleId() const; signals: void isEmptyChanged(); void hasRequiredQuick3DImportChanged(); -#ifdef QDS_USE_PROJECTSTORAGE - void bundleItemImported(const QmlDesigner::TypeName &typeName); -#else - void bundleItemImported(const QmlDesigner::NodeMetaInfo &metaInfo); -#endif - void bundleItemAboutToUnimport(const QmlDesigner::TypeName &type); - void bundleItemUnimported(const QmlDesigner::NodeMetaInfo &metaInfo); - void importerRunningChanged(); void bundleExistsChanged(); private: bool isValidIndex(int idx) const; - void createImporter(const QString &bundlePath, const QString &bundleId, - const QStringList &sharedFiles); ContentLibraryWidget *m_widget = nullptr; QString m_searchText; + QString m_bundlePath; + QString m_bundleId; + QStringList m_bundleSharedFiles; QList<ContentLibraryEffectsCategory *> m_bundleCategories; QJsonObject m_bundleObj; - Internal::ContentLibraryBundleImporter *m_importer = nullptr; bool m_isEmpty = true; bool m_bundleExists = false; bool m_probeBundleDir = false; - bool m_importerRunning = false; int m_quick3dMajorVersion = -1; int m_quick3dMinorVersion = -1; - - QString m_importerBundlePath; - QString m_importerBundleId; - QStringList m_importerSharedFiles; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryitem.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryitem.cpp new file mode 100644 index 0000000000..38fb69abbc --- /dev/null +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryitem.cpp @@ -0,0 +1,76 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "contentlibraryitem.h" + +namespace QmlDesigner { + +ContentLibraryItem::ContentLibraryItem(QObject *parent, + const QString &name, + const QString &qml, + const TypeName &type, + const QUrl &icon, + const QStringList &files) + : QObject(parent), m_name(name), m_qml(qml), m_type(type), m_icon(icon), m_files(files) +{ + m_allFiles = m_files; + m_allFiles.push_back(m_qml); +} + +bool ContentLibraryItem::filter(const QString &searchText) +{ + if (m_visible != m_name.contains(searchText, Qt::CaseInsensitive)) { + m_visible = !m_visible; + emit itemVisibleChanged(); + } + + return m_visible; +} + +QUrl ContentLibraryItem::icon() const +{ + return m_icon; +} + +QString ContentLibraryItem::qml() const +{ + return m_qml; +} + +TypeName ContentLibraryItem::type() const +{ + return m_type; +} + +QStringList ContentLibraryItem::files() const +{ + return m_files; +} + +bool ContentLibraryItem::visible() const +{ + return m_visible; +} + +bool ContentLibraryItem::setImported(bool imported) +{ + if (m_imported != imported) { + m_imported = imported; + emit itemImportedChanged(); + return true; + } + + return false; +} + +bool ContentLibraryItem::imported() const +{ + return m_imported; +} + +QStringList ContentLibraryItem::allFiles() const +{ + return m_allFiles; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryitem.h index fdb302b613..bdf8736728 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryitem.h @@ -10,7 +10,7 @@ namespace QmlDesigner { -class ContentLibraryEffect : public QObject +class ContentLibraryItem : public QObject { Q_OBJECT @@ -19,14 +19,15 @@ class ContentLibraryEffect : public QObject Q_PROPERTY(QStringList bundleItemFiles READ allFiles CONSTANT) Q_PROPERTY(bool bundleItemVisible MEMBER m_visible NOTIFY itemVisibleChanged) Q_PROPERTY(bool bundleItemImported READ imported WRITE setImported NOTIFY itemImportedChanged) + Q_PROPERTY(QString itemType MEMBER m_itemType CONSTANT) public: - ContentLibraryEffect(QObject *parent, - const QString &name, - const QString &qml, - const TypeName &type, - const QUrl &icon, - const QStringList &files); + ContentLibraryItem(QObject *parent, + const QString &name, + const QString &qml, + const TypeName &type, + const QUrl &icon, + const QStringList &files); bool filter(const QString &searchText); @@ -35,11 +36,9 @@ public: TypeName type() const; QStringList files() const; bool visible() const; - QString qmlFilePath() const; bool setImported(bool imported); bool imported() const; - QString parentDirPath() const; QStringList allFiles() const; signals: @@ -57,6 +56,7 @@ private: bool m_imported = false; QStringList m_allFiles; + const QString m_itemType = "item"; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.cpp index 834bc8aa30..25d1523199 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.cpp @@ -32,6 +32,11 @@ bool ContentLibraryMaterial::filter(const QString &searchText) return m_visible; } +QString ContentLibraryMaterial::name() const +{ + return m_name; +} + QUrl ContentLibraryMaterial::icon() const { return m_icon; @@ -84,7 +89,7 @@ QString ContentLibraryMaterial::qmlFilePath() const return m_downloadPath + "/" + m_qml; } -QString ContentLibraryMaterial::parentDirPath() const +QString ContentLibraryMaterial::dirPath() const { return m_downloadPath; } diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h index f546ea98cd..aaaf9517f9 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 <QDataStream> #include <QObject> #include <QUrl> @@ -18,10 +17,11 @@ class ContentLibraryMaterial : public QObject Q_PROPERTY(QString bundleMaterialName MEMBER m_name CONSTANT) Q_PROPERTY(QUrl bundleMaterialIcon MEMBER m_icon CONSTANT) Q_PROPERTY(bool bundleMaterialVisible MEMBER m_visible NOTIFY materialVisibleChanged) - Q_PROPERTY(bool bundleMaterialImported READ imported WRITE setImported NOTIFY materialImportedChanged) + Q_PROPERTY(bool bundleItemImported READ imported WRITE setImported NOTIFY materialImportedChanged) Q_PROPERTY(QString bundleMaterialBaseWebUrl MEMBER m_baseWebUrl CONSTANT) - Q_PROPERTY(QString bundleMaterialParentPath READ parentDirPath CONSTANT) + Q_PROPERTY(QString bundleMaterialDirPath READ dirPath CONSTANT) Q_PROPERTY(QStringList bundleMaterialFiles READ allFiles CONSTANT) + Q_PROPERTY(QString itemType MEMBER m_itemType CONSTANT) public: ContentLibraryMaterial(QObject *parent, @@ -31,12 +31,13 @@ public: const QUrl &icon, const QStringList &files, const QString &downloadPath, - const QString &baseWebUrl); + const QString &baseWebUrl = {}); bool filter(const QString &searchText); Q_INVOKABLE bool isDownloaded() const; + QString name() const; QUrl icon() const; QString qml() const; TypeName type() const; @@ -46,7 +47,7 @@ public: bool setImported(bool imported); bool imported() const; - QString parentDirPath() const; + QString dirPath() const; QStringList allFiles() const; signals: @@ -66,6 +67,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/contentlibrarymaterialsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp index 7594c691b5..adb47108a9 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp @@ -8,18 +8,19 @@ #include "contentlibrarymaterialscategory.h" #include "contentlibrarywidget.h" -#include <designerpaths.h> +#include "designerpaths.h" #include "filedownloader.h" #include "fileextractor.h" #include "multifiledownloader.h" -#include "qmldesignerconstants.h" -#include "qmldesignerplugin.h" + +#include <qmldesignerplugin.h> #include <utils/algorithm.h> #include <utils/hostosinfo.h> #include <utils/qtcassert.h> #include <QCoreApplication> +#include <QDir> #include <QJsonArray> #include <QJsonDocument> #include <QQmlEngine> @@ -40,7 +41,10 @@ ContentLibraryMaterialsModel::ContentLibraryMaterialsModel(ContentLibraryWidget qmlRegisterType<QmlDesigner::FileDownloader>("WebFetcher", 1, 0, "FileDownloader"); qmlRegisterType<QmlDesigner::MultiFileDownloader>("WebFetcher", 1, 0, "MultiFileDownloader"); +} +void ContentLibraryMaterialsModel::loadBundle() +{ QDir bundleDir{m_downloadPath}; if (fetchBundleMetadata(bundleDir) && fetchBundleIcons(bundleDir)) loadMaterialBundle(bundleDir); @@ -192,11 +196,9 @@ void ContentLibraryMaterialsModel::downloadSharedFiles(const QDir &targetDir, co extractor->setAlwaysCreateDir(false); extractor->setClearTargetPathContents(false); - QObject::connect(extractor, &FileExtractor::finishedChanged, this, [this, downloader, extractor]() { + QObject::connect(extractor, &FileExtractor::finishedChanged, this, [downloader, extractor]() { downloader->deleteLater(); extractor->deleteLater(); - - createImporter(m_importerBundlePath, m_importerBundleId, m_importerSharedFiles); }); extractor->extract(); @@ -205,93 +207,68 @@ void ContentLibraryMaterialsModel::downloadSharedFiles(const QDir &targetDir, co downloader->start(); } -void ContentLibraryMaterialsModel::createImporter(const QString &bundlePath, const QString &bundleId, - const QStringList &sharedFiles) +QString ContentLibraryMaterialsModel::bundleId() const { - 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(); + return m_bundleId; } -void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &matBundleDir) +void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &bundleDir) { - if (m_matBundleExists) + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + + if (m_bundleExists && m_bundleId == compUtils.materialsBundleId()) return; - QString matBundlePath = matBundleDir.filePath("material_bundle.json"); + // clean up + qDeleteAll(m_bundleCategories); + m_bundleCategories.clear(); + m_bundleExists = false; + m_isEmpty = true; + m_bundleObj = {}; + m_bundleId.clear(); - if (m_matBundleObj.isEmpty()) { - QFile matPropsFile(matBundlePath); + QString bundlePath = bundleDir.filePath("material_bundle.json"); - if (!matPropsFile.open(QIODevice::ReadOnly)) { - qWarning("Couldn't open material_bundle.json"); - return; - } + QFile bundleFile(bundlePath); + if (!bundleFile.open(QIODevice::ReadOnly)) { + qWarning("Couldn't open material_bundle.json"); + resetModel(); + return; + } - QJsonDocument matBundleJsonDoc = QJsonDocument::fromJson(matPropsFile.readAll()); - if (matBundleJsonDoc.isNull()) { - qWarning("Invalid material_bundle.json file"); - return; - } else { - m_matBundleObj = matBundleJsonDoc.object(); - } + QJsonDocument bundleJsonDoc = QJsonDocument::fromJson(bundleFile.readAll()); + if (bundleJsonDoc.isNull()) { + qWarning("Invalid material_bundle.json file"); + resetModel(); + return; } - QString bundleId = m_matBundleObj.value("id").toString(); + m_bundleObj = bundleJsonDoc.object(); - const QJsonObject catsObj = m_matBundleObj.value("categories").toObject(); + QString bundleType = compUtils.materialsBundleType(); + m_bundleId = compUtils.materialsBundleId(); + + const QJsonObject catsObj = m_bundleObj.value("categories").toObject(); const QStringList categories = catsObj.keys(); for (const QString &cat : categories) { 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(); - for (const auto /*QJson{Const,}ValueRef*/ &asset : assetsArr) + for (const QJsonValueConstRef &asset : assetsArr) files.append(asset.toString()); - QUrl icon = QUrl::fromLocalFile(matBundleDir.filePath(matObj.value("icon").toString())); + 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 = QLatin1String("%1.%2") + .arg(bundleType, 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); @@ -299,30 +276,23 @@ void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &matBundleDir) m_bundleCategories.append(category); } - QStringList sharedFiles; - const QJsonArray sharedFilesArr = m_matBundleObj.value("sharedFiles").toArray(); - for (const auto /*QJson{Const,}ValueRef*/ &file : sharedFilesArr) - sharedFiles.append(file.toString()); + m_bundleSharedFiles.clear(); + const QJsonArray sharedFilesArr = m_bundleObj.value("sharedFiles").toArray(); + for (const QJsonValueConstRef &file : sharedFilesArr) + m_bundleSharedFiles.append(file.toString()); QStringList missingSharedFiles; - for (const QString &s : std::as_const(sharedFiles)) { - const QString fullSharedFilePath = matBundleDir.filePath(s); - - if (!QFileInfo::exists(fullSharedFilePath)) + for (const QString &s : std::as_const(m_bundleSharedFiles)) { + if (!QFileInfo::exists(bundleDir.filePath(s))) missingSharedFiles.push_back(s); } - if (missingSharedFiles.length() > 0) { - m_importerBundlePath = matBundleDir.path(); - m_importerBundleId = bundleId; - m_importerSharedFiles = sharedFiles; - downloadSharedFiles(matBundleDir, missingSharedFiles); - } else { - createImporter(matBundleDir.path(), bundleId, sharedFiles); - } + if (missingSharedFiles.length() > 0) + downloadSharedFiles(bundleDir, missingSharedFiles); - m_matBundleExists = true; - emit matBundleExistsChanged(); + m_bundleExists = true; + updateIsEmpty(); + resetModel(); } bool ContentLibraryMaterialsModel::hasRequiredQuick3DImport() const @@ -332,12 +302,7 @@ bool ContentLibraryMaterialsModel::hasRequiredQuick3DImport() const bool ContentLibraryMaterialsModel::matBundleExists() const { - return m_matBundleExists; -} - -Internal::ContentLibraryBundleImporter *ContentLibraryMaterialsModel::bundleImporter() const -{ - return m_importer; + return m_bundleExists; } void ContentLibraryMaterialsModel::setSearchText(const QString &searchText) @@ -359,11 +324,11 @@ void ContentLibraryMaterialsModel::setSearchText(const QString &searchText) updateIsEmpty(); } -void ContentLibraryMaterialsModel::updateImportedState(const QStringList &importedMats) +void ContentLibraryMaterialsModel::updateImportedState(const QStringList &importedItems) { bool changed = false; for (ContentLibraryMaterialsCategory *cat : std::as_const(m_bundleCategories)) - changed |= cat->updateImportedState(importedMats); + changed |= cat->updateImportedState(importedItems); if (changed) resetModel(); @@ -399,42 +364,23 @@ void ContentLibraryMaterialsModel::applyToSelected(ContentLibraryMaterial *mat, void ContentLibraryMaterialsModel::addToProject(ContentLibraryMaterial *mat) { - QString err = m_importer->importComponent(mat->qml(), mat->files()); + QString err = m_widget->importer()->importComponent(mat->dirPath(), mat->type(), mat->qml(), + mat->files() + m_bundleSharedFiles); - if (err.isEmpty()) { - m_importerRunning = true; - emit importerRunningChanged(); - } else { + if (err.isEmpty()) + m_widget->setImporterRunning(true); + else qWarning() << __FUNCTION__ << err; - } } void ContentLibraryMaterialsModel::removeFromProject(ContentLibraryMaterial *mat) { - emit bundleMaterialAboutToUnimport(mat->type()); + QString err = m_widget->importer()->unimportComponent(mat->type(), mat->qml()); - QString err = m_importer->unimportComponent(mat->qml()); - - if (err.isEmpty()) { - m_importerRunning = true; - emit importerRunningChanged(); - } else { + if (err.isEmpty()) + m_widget->setImporterRunning(true); + else qWarning() << __FUNCTION__ << err; - } -} - -bool ContentLibraryMaterialsModel::hasModelSelection() const -{ - return m_hasModelSelection; -} - -void ContentLibraryMaterialsModel::setHasModelSelection(bool b) -{ - if (b == m_hasModelSelection) - return; - - m_hasModelSelection = b; - emit hasModelSelectionChanged(); } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h index 21bd374137..c0840dc3cc 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h @@ -3,22 +3,17 @@ #pragma once -#include "nodemetainfo.h" - #include <QAbstractListModel> -#include <QDir> #include <QJsonObject> +QT_FORWARD_DECLARE_CLASS(QDir) + namespace QmlDesigner { class ContentLibraryMaterial; class ContentLibraryMaterialsCategory; class ContentLibraryWidget; -namespace Internal { -class ContentLibraryBundleImporter; -} - class ContentLibraryMaterialsModel : public QAbstractListModel { Q_OBJECT @@ -26,8 +21,6 @@ class ContentLibraryMaterialsModel : public QAbstractListModel Q_PROPERTY(bool matBundleExists READ matBundleExists NOTIFY matBundleExistsChanged) Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) Q_PROPERTY(bool hasRequiredQuick3DImport READ hasRequiredQuick3DImport NOTIFY hasRequiredQuick3DImportChanged) - Q_PROPERTY(bool hasModelSelection READ hasModelSelection NOTIFY hasModelSelectionChanged) - Q_PROPERTY(bool importerRunning MEMBER m_importerRunning NOTIFY importerRunningChanged) public: ContentLibraryMaterialsModel(ContentLibraryWidget *parent = nullptr); @@ -38,40 +31,27 @@ public: QHash<int, QByteArray> roleNames() const override; void setSearchText(const QString &searchText); - void updateImportedState(const QStringList &importedMats); - + void updateImportedState(const QStringList &importedItems); void setQuick3DImportVersion(int major, int minor); bool hasRequiredQuick3DImport() const; - bool matBundleExists() const; - bool hasModelSelection() const; - void setHasModelSelection(bool b); - void resetModel(); void updateIsEmpty(); - - Internal::ContentLibraryBundleImporter *bundleImporter() const; + void loadBundle(); Q_INVOKABLE void applyToSelected(QmlDesigner::ContentLibraryMaterial *mat, bool add = false); Q_INVOKABLE void addToProject(QmlDesigner::ContentLibraryMaterial *mat); Q_INVOKABLE void removeFromProject(QmlDesigner::ContentLibraryMaterial *mat); + QString bundleId() const; + signals: void isEmptyChanged(); void hasRequiredQuick3DImportChanged(); - void hasModelSelectionChanged(); void materialVisibleChanged(); void applyToSelectedTriggered(QmlDesigner::ContentLibraryMaterial *mat, bool add = false); -#ifdef QDS_USE_PROJECTSTORAGE - void bundleMaterialImported(const QmlDesigner::TypeName &typeName); -#else - void bundleMaterialImported(const QmlDesigner::NodeMetaInfo &metaInfo); -#endif - void bundleMaterialAboutToUnimport(const QmlDesigner::TypeName &type); - void bundleMaterialUnimported(const QmlDesigner::NodeMetaInfo &metaInfo); - void importerRunningChanged(); void matBundleExistsChanged(); private: @@ -80,29 +60,22 @@ private: bool fetchBundleMetadata(const QDir &bundleDir); bool isValidIndex(int idx) const; void downloadSharedFiles(const QDir &targetDir, const QStringList &files); - void createImporter(const QString &bundlePath, const QString &bundleId, - const QStringList &sharedFiles); ContentLibraryWidget *m_widget = nullptr; QString m_searchText; + QString m_bundleId; + QStringList m_bundleSharedFiles; QList<ContentLibraryMaterialsCategory *> m_bundleCategories; - QJsonObject m_matBundleObj; - Internal::ContentLibraryBundleImporter *m_importer = nullptr; + QJsonObject m_bundleObj; bool m_isEmpty = true; - bool m_matBundleExists = false; - bool m_hasModelSelection = false; - bool m_importerRunning = false; + bool m_bundleExists = false; int m_quick3dMajorVersion = -1; int m_quick3dMinorVersion = -1; QString m_downloadPath; QString m_baseUrl; - - QString m_importerBundlePath; - QString m_importerBundleId; - QStringList m_importerSharedFiles; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp index 7ab239aab4..d2c2df2baa 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 QString &dirPath, const QString &suffix, const QSize &dimensions, const qint64 sizeInBytes, - bool hasUpdate, bool isNew) + const QString &key, const QString &textureUrl, + const QString &iconUrl, 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); + imageInfo = ImageUtils::imageInfoString(m_dimensions, m_sizeInBytes); } else { - QString fullDownloadPath = m_downloadPath + '/' + fileName; - imageInfo = ImageUtils::imageInfo(fullDownloadPath); + QString fullDownloadPath = m_dirPath + '/' + fileName; + imageInfo = ImageUtils::imageInfoString(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,21 @@ 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(); } +bool ContentLibraryTexture::visible() const +{ + return m_visible; +} + 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 9f5b46630f..7f5db6d7d6 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h @@ -19,17 +19,18 @@ 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) + Q_PROPERTY(QString itemType MEMBER m_itemType 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 &suffix, const QSize &dimensions, const qint64 sizeInBytes, + const QString &key = {}, const QString &textureUrl = {}, + const QString &iconUrl = {}, bool hasUpdate = false, bool isNew = false); Q_INVOKABLE bool isDownloaded() const; Q_INVOKABLE void setDownloaded(); @@ -38,30 +39,32 @@ public: QUrl icon() const; QString iconPath() const; - QString downloadedTexturePath() const; + QString texturePath() const; QString parentDirPath() const; QString textureKey() const; void setHasUpdate(bool value); bool hasUpdate() const; + bool visible() const; + signals: void textureVisibleChanged(); void textureToolTipChanged(); 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; @@ -71,6 +74,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 77519ad88f..0cafe8d138 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, suffix, dimensions, sizeInBytes, + key, webTextureUrl, iconUrl, 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 166528f05a..857346df06 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 319ca2686f..b575b6b9b2 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp @@ -95,37 +95,37 @@ QHash<int, QByteArray> 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()); - - QString localDownloadPath = QString("%1/%2/%3") + 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 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 92db4151a8..94e223a251 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 &textureBundleUrl, const QString &bundlePath, + const QVariantMap &metaData); signals: void isEmptyChanged(); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp new file mode 100644 index 0000000000..8dcf4575f7 --- /dev/null +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp @@ -0,0 +1,686 @@ +// 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 "contentlibraryitem.h" +#include "contentlibrarymaterial.h" +#include "contentlibrarymaterialscategory.h" +#include "contentlibrarytexture.h" +#include "contentlibrarywidget.h" + +#include <designerpaths.h> +#include <imageutils.h> +#include <qmldesignerconstants.h> +#include <qmldesignerplugin.h> +#include <uniquename.h> + +#include <utils/algorithm.h> +#include <utils/qtcassert.h> + +#include <QFileInfo> +#include <QJsonArray> +#include <QJsonDocument> +#include <QUrl> + +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 +} + +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() == MaterialsSectionIdx) + return QVariant::fromValue(m_userMaterials); + if (index.row() == TexturesSectionIdx) + return QVariant::fromValue(m_userTextures); + if (index.row() == Items3DSectionIdx) + return QVariant::fromValue(m_user3DItems); + if (index.row() == EffectsSectionIdx) + return QVariant::fromValue(m_userEffects); + } + + if (role == NoMatchRole) { + if (index.row() == MaterialsSectionIdx) + return m_noMatchMaterials; + if (index.row() == TexturesSectionIdx) + return m_noMatchTextures; + if (index.row() == Items3DSectionIdx) + return m_noMatch3D; + if (index.row() == EffectsSectionIdx) + return m_noMatchEffects; + } + + if (role == VisibleRole) { + if (index.row() == MaterialsSectionIdx) + return !m_userMaterials.isEmpty(); + if (index.row() == TexturesSectionIdx) + return !m_userTextures.isEmpty(); + if (index.row() == Items3DSectionIdx) + return !m_user3DItems.isEmpty(); + if (index.row() == EffectsSectionIdx) + return !m_userEffects.isEmpty(); + } + + return {}; +} + +bool ContentLibraryUserModel::isValidIndex(int idx) const +{ + return idx > -1 && idx < rowCount(); +} + +void ContentLibraryUserModel::updateNoMatchMaterials() +{ + m_noMatchMaterials = Utils::allOf(m_userMaterials, [&](ContentLibraryMaterial *item) { + return !item->visible(); + }); +} + +void ContentLibraryUserModel::updateNoMatchTextures() +{ + m_noMatchTextures = Utils::allOf(m_userTextures, [&](ContentLibraryTexture *item) { + return !item->visible(); + }); +} + +void ContentLibraryUserModel::updateNoMatch3D() +{ + m_noMatch3D = Utils::allOf(m_user3DItems, [&](ContentLibraryItem *item) { + return !item->visible(); + }); +} + +void ContentLibraryUserModel::addMaterial(const QString &name, const QString &qml, + const QUrl &icon, const QStringList &files) +{ + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + + QString typePrefix = compUtils.userMaterialsBundleType(); + TypeName type = QLatin1String("%1.%2").arg(typePrefix, qml.chopped(4)).toLatin1(); + + auto libMat = new ContentLibraryMaterial(this, name, qml, type, icon, files, + Paths::bundlesPathSetting().append("/User/materials")); + m_userMaterials.append(libMat); + + emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx)); +} + +void ContentLibraryUserModel::add3DItem(const QString &name, const QString &qml, + const QUrl &icon, const QStringList &files) +{ + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + + QString typePrefix = compUtils.user3DBundleType(); + TypeName type = QLatin1String("%1.%2").arg(typePrefix, qml.chopped(4)).toLatin1(); + + m_user3DItems.append(new ContentLibraryItem(this, name, qml, type, icon, files)); +} + +void ContentLibraryUserModel::refresh3DSection() +{ + emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx)); +} + +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<QSize, qint64> 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); + } + + emit dataChanged(index(TexturesSectionIdx), index(TexturesSectionIdx)); +} + +void ContentLibraryUserModel::add3DInstance(ContentLibraryItem *bundleItem) +{ + QString err = m_widget->importer()->importComponent(m_bundlePath3D.path(), bundleItem->type(), + bundleItem->qml(), + bundleItem->files() + m_bundle3DSharedFiles); + + if (err.isEmpty()) + m_widget->setImporterRunning(true); + else + qWarning() << __FUNCTION__ << err; +} + +void ContentLibraryUserModel::removeTexture(ContentLibraryTexture *tex) +{ + // remove resources + Utils::FilePath::fromString(tex->texturePath()).removeFile(); + Utils::FilePath::fromString(tex->iconPath()).removeFile(); + + // remove from model + m_userTextures.removeOne(tex); + tex->deleteLater(); + + // update model + emit dataChanged(index(TexturesSectionIdx), index(TexturesSectionIdx)); +} + +void ContentLibraryUserModel::removeFromContentLib(QObject *item) +{ + if (auto mat = qobject_cast<ContentLibraryMaterial *>(item)) + removeMaterialFromContentLib(mat); + else if (auto itm = qobject_cast<ContentLibraryItem *>(item)) + remove3DFromContentLib(itm); +} + +void ContentLibraryUserModel::removeMaterialFromContentLib(ContentLibraryMaterial *item) +{ + auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/materials/"); + + QJsonArray itemsArr = m_bundleObjMaterial.value("items").toArray(); + + // remove qml and icon files + Utils::FilePath::fromString(item->qmlFilePath()).removeFile(); + Utils::FilePath::fromUrl(item->icon()).removeFile(); + + // remove from the bundle json file + for (int i = 0; i < itemsArr.size(); ++i) { + if (itemsArr.at(i).toObject().value("qml") == item->qml()) { + itemsArr.removeAt(i); + break; + } + } + m_bundleObjMaterial.insert("items", itemsArr); + + auto result = bundlePath.pathAppended(Constants::BUNDLE_JSON_FILENAME) + .writeFileContents(QJsonDocument(m_bundleObjMaterial).toJson()); + if (!result) + qWarning() << __FUNCTION__ << result.error(); + + // delete dependency files if they are only used by the deleted material + QStringList allFiles; + for (const QJsonValueConstRef &itemRef : std::as_const(itemsArr)) + allFiles.append(itemRef.toObject().value("files").toVariant().toStringList()); + + const QStringList itemFiles = item->files(); + for (const QString &file : itemFiles) { + if (allFiles.count(file) == 0) // only used by the deleted item + bundlePath.pathAppended(file).removeFile(); + } + + // remove from model + m_userMaterials.removeOne(item); + item->deleteLater(); + + // update model + emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx)); +} + +void ContentLibraryUserModel::remove3DFromContentLibByName(const QString &qmlFileName) +{ + ContentLibraryItem *itemToRemove = Utils::findOr(m_user3DItems, nullptr, + [&qmlFileName](ContentLibraryItem *item) { + return item->qml() == qmlFileName; + }); + + if (itemToRemove) + remove3DFromContentLib(itemToRemove); +} + +void ContentLibraryUserModel::remove3DFromContentLib(ContentLibraryItem *item) +{ + QJsonArray itemsArr = m_bundleObj3D.value("items").toArray(); + + // remove qml and icon files + m_bundlePath3D.pathAppended(item->qml()).removeFile(); + Utils::FilePath::fromUrl(item->icon()).removeFile(); + + // remove from the bundle json file + for (int i = 0; i < itemsArr.size(); ++i) { + if (itemsArr.at(i).toObject().value("qml") == item->qml()) { + itemsArr.removeAt(i); + break; + } + } + m_bundleObj3D.insert("items", itemsArr); + + auto result = m_bundlePath3D.pathAppended(Constants::BUNDLE_JSON_FILENAME) + .writeFileContents(QJsonDocument(m_bundleObj3D).toJson()); + if (!result) + qWarning() << __FUNCTION__ << result.error(); + + // delete dependency files if they are only used by the deleted item + QStringList allFiles; + for (const QJsonValueConstRef &itemRef : std::as_const(itemsArr)) + allFiles.append(itemRef.toObject().value("files").toVariant().toStringList()); + + const QStringList itemFiles = item->files(); + for (const QString &file : itemFiles) { + if (allFiles.count(file) == 0) // only used by the deleted item + m_bundlePath3D.pathAppended(file).removeFile(); + } + + // remove from model + m_user3DItems.removeOne(item); + item->deleteLater(); + + // update model + emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx)); +} + +/** + * @brief Gets unique Qml component and icon file material names from a given name + * @param defaultName input name + * @return <Qml, icon> file names + */ +QPair<QString, QString> ContentLibraryUserModel::getUniqueLibMaterialNames(const QString &defaultName) const +{ + return getUniqueLibItemNames(defaultName, m_bundleObjMaterial); +} + +/** + * @brief Gets unique Qml component and icon file 3d item names from a given name + * @param defaultName input name + * @return <Qml, icon> file names + */ +QPair<QString, QString> ContentLibraryUserModel::getUniqueLib3DNames(const QString &defaultName) const +{ + return getUniqueLibItemNames(defaultName, m_bundleObj3D); +} + +QPair<QString, QString> ContentLibraryUserModel::getUniqueLibItemNames(const QString &defaultName, + const QJsonObject &bundleObj) const +{ + QTC_ASSERT(!bundleObj.isEmpty(), return {}); + + const QJsonArray itemsArr = bundleObj.value("items").toArray(); + + QStringList itemQmls, itemIcons; + for (const QJsonValueConstRef &itemRef : itemsArr) { + const QJsonObject &obj = itemRef.toObject(); + itemQmls.append(obj.value("qml").toString().chopped(4)); // remove .qml + itemIcons.append(QFileInfo(obj.value("icon").toString()).baseName()); + } + + QString baseQml = UniqueName::generateId(defaultName); + baseQml[0] = baseQml.at(0).toUpper(); + baseQml.prepend("My"); + + QString uniqueQml = UniqueName::generate(baseQml, [&] (const QString &name) { + return itemQmls.contains(name); + }); + + QString uniqueIcon = UniqueName::generate(defaultName, [&] (const QString &name) { + return itemIcons.contains(name); + }); + + return {uniqueQml + ".qml", uniqueIcon + ".png"}; +} + +QHash<int, QByteArray> ContentLibraryUserModel::roleNames() const +{ + static const QHash<int, QByteArray> roles { + {NameRole, "categoryName"}, + {VisibleRole, "categoryVisible"}, + {ItemsRole, "categoryItems"}, + {NoMatchRole, "categoryNoMatch"} + }; + return roles; +} + +QJsonObject &ContentLibraryUserModel::bundleJsonMaterialObjectRef() +{ + return m_bundleObjMaterial; +} + +QJsonObject &ContentLibraryUserModel::bundleJson3DObjectRef() +{ + return m_bundleObj3D; +} + +void ContentLibraryUserModel::loadBundles() +{ + loadMaterialBundle(); + load3DBundle(); + loadTextureBundle(); +} + +void ContentLibraryUserModel::loadMaterialBundle() +{ + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + if (m_matBundleExists && m_bundleIdMaterial == compUtils.userMaterialsBundleId()) + return; + + // clean up + qDeleteAll(m_userMaterials); + m_userMaterials.clear(); + m_matBundleExists = false; + m_noMatchMaterials = true; + m_bundleObjMaterial = {}; + m_bundleIdMaterial.clear(); + + m_bundlePathMaterial = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/materials"); + m_bundlePathMaterial.ensureWritableDir(); + m_bundlePathMaterial.pathAppended("icons").ensureWritableDir(); + + auto jsonFilePath = m_bundlePathMaterial.pathAppended(Constants::BUNDLE_JSON_FILENAME); + if (!jsonFilePath.exists()) { + QString jsonContent = "{\n"; + jsonContent += " \"id\": \"UserMaterials\",\n"; + jsonContent += " \"items\": []\n"; + jsonContent += "}"; + Utils::expected_str<qint64> res = jsonFilePath.writeFileContents(jsonContent.toLatin1()); + if (!res.has_value()) { + qWarning() << __FUNCTION__ << res.error(); + emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx)); + return; + } + } + + Utils::expected_str<QByteArray> jsonContents = jsonFilePath.fileContents(); + if (!jsonContents.has_value()) { + qWarning() << __FUNCTION__ << jsonContents.error(); + emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx)); + return; + } + + QJsonDocument bundleJsonDoc = QJsonDocument::fromJson(jsonContents.value()); + if (bundleJsonDoc.isNull()) { + qWarning() << __FUNCTION__ << "Invalid json file" << jsonFilePath; + emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx)); + return; + } + + m_bundleIdMaterial = compUtils.userMaterialsBundleId(); + m_bundleObjMaterial = bundleJsonDoc.object(); + m_bundleObjMaterial["id"] = m_bundleIdMaterial; + + // parse items + QString typePrefix = compUtils.userMaterialsBundleType(); + const QJsonArray itemsArr = m_bundleObjMaterial.value("items").toArray(); + for (const QJsonValueConstRef &itemRef : itemsArr) { + const QJsonObject itemObj = itemRef.toObject(); + + QString name = itemObj.value("name").toString(); + QString qml = itemObj.value("qml").toString(); + TypeName type = QLatin1String("%1.%2").arg(typePrefix, qml.chopped(4)).toLatin1(); + QUrl icon = m_bundlePathMaterial.pathAppended(itemObj.value("icon").toString()).toUrl(); + QStringList files; + const QJsonArray assetsArr = itemObj.value("files").toArray(); + for (const QJsonValueConstRef &asset : assetsArr) + files.append(asset.toString()); + + m_userMaterials.append(new ContentLibraryMaterial(this, name, qml, type, icon, files, + m_bundlePathMaterial.path(), "")); + } + + m_bundleMaterialSharedFiles.clear(); + const QJsonArray sharedFilesArr = m_bundleObjMaterial.value("sharedFiles").toArray(); + for (const QJsonValueConstRef &file : sharedFilesArr) + m_bundleMaterialSharedFiles.append(file.toString()); + + m_matBundleExists = true; + updateNoMatchMaterials(); + emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx)); +} + +void ContentLibraryUserModel::load3DBundle() +{ + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + + if (m_bundle3DExists && m_bundleId3D == compUtils.user3DBundleId()) + return; + + // clean up + qDeleteAll(m_user3DItems); + m_user3DItems.clear(); + m_bundle3DExists = false; + m_noMatch3D = true; + m_bundleObj3D = {}; + m_bundleId3D.clear(); + + m_bundlePath3D = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/3d"); + m_bundlePath3D.ensureWritableDir(); + m_bundlePath3D.pathAppended("icons").ensureWritableDir(); + + auto jsonFilePath = m_bundlePath3D.pathAppended(Constants::BUNDLE_JSON_FILENAME); + if (!jsonFilePath.exists()) { + QByteArray jsonContent = "{\n"; + jsonContent += " \"id\": \"User3D\",\n"; + jsonContent += " \"items\": []\n"; + jsonContent += "}"; + Utils::expected_str<qint64> res = jsonFilePath.writeFileContents(jsonContent); + if (!res.has_value()) { + qWarning() << __FUNCTION__ << res.error(); + emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx)); + return; + } + } + + Utils::expected_str<QByteArray> jsonContents = jsonFilePath.fileContents(); + if (!jsonContents.has_value()) { + qWarning() << __FUNCTION__ << jsonContents.error(); + emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx)); + return; + } + + QJsonDocument bundleJsonDoc = QJsonDocument::fromJson(jsonContents.value()); + if (bundleJsonDoc.isNull()) { + qWarning() << __FUNCTION__ << "Invalid json file" << jsonFilePath; + emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx)); + return; + } + + m_bundleId3D = compUtils.user3DBundleId(); + m_bundleObj3D = bundleJsonDoc.object(); + m_bundleObj3D["id"] = m_bundleId3D; + + // parse items + QString typePrefix = compUtils.user3DBundleType(); + const QJsonArray itemsArr = m_bundleObj3D.value("items").toArray(); + for (const QJsonValueConstRef &itemRef : itemsArr) { + const QJsonObject itemObj = itemRef.toObject(); + + QString name = itemObj.value("name").toString(); + QString qml = itemObj.value("qml").toString(); + TypeName type = QLatin1String("%1.%2").arg(typePrefix, qml.chopped(4)).toLatin1(); + QUrl icon = m_bundlePath3D.pathAppended(itemObj.value("icon").toString()).toUrl(); + QStringList files; + const QJsonArray assetsArr = itemObj.value("files").toArray(); + for (const QJsonValueConstRef &asset : assetsArr) + files.append(asset.toString()); + + m_user3DItems.append(new ContentLibraryItem(nullptr, name, qml, type, icon, files)); + } + + m_bundle3DSharedFiles.clear(); + const QJsonArray sharedFilesArr = m_bundleObj3D.value("sharedFiles").toArray(); + for (const QJsonValueConstRef &file : sharedFilesArr) + m_bundle3DSharedFiles.append(file.toString()); + + m_bundle3DExists = true; + updateNoMatch3D(); + emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx)); +} + +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) { + QString suffix = '.' + fileInfo.suffix(); + auto iconFileInfo = QFileInfo(fileInfo.path().append("/icons/").append(fileInfo.baseName() + ".png")); + QPair<QSize, qint64> info = ImageUtils::imageInfo(fileInfo.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); + } + + updateNoMatchTextures(); + emit dataChanged(index(TexturesSectionIdx), index(TexturesSectionIdx)); +} + +bool ContentLibraryUserModel::hasRequiredQuick3DImport() const +{ + return m_widget->hasQuick3DImport() && m_quick3dMajorVersion == 6 && m_quick3dMinorVersion >= 3; +} + +bool ContentLibraryUserModel::matBundleExists() const +{ + return m_matBundleExists; +} + +void ContentLibraryUserModel::setSearchText(const QString &searchText) +{ + QString lowerSearchText = searchText.toLower(); + + if (m_searchText == lowerSearchText) + return; + + m_searchText = lowerSearchText; + + for (ContentLibraryMaterial *item : std::as_const(m_userMaterials)) + item->filter(m_searchText); + + for (ContentLibraryTexture *item : std::as_const(m_userTextures)) + item->filter(m_searchText); + + for (ContentLibraryItem *item : std::as_const(m_user3DItems)) + item->filter(m_searchText); + + updateNoMatchMaterials(); + updateNoMatchTextures(); + updateNoMatch3D(); + resetModel(); +} + +void ContentLibraryUserModel::updateMaterialsImportedState(const QStringList &importedItems) +{ + bool changed = false; + for (ContentLibraryMaterial *mat : std::as_const(m_userMaterials)) + changed |= mat->setImported(importedItems.contains(mat->qml().chopped(4))); + + if (changed) + emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx)); +} + +void ContentLibraryUserModel::update3DImportedState(const QStringList &importedItems) +{ + bool changed = false; + for (ContentLibraryItem *item : std::as_const(m_user3DItems)) + changed |= item->setImported(importedItems.contains(item->qml().chopped(4))); + + if (changed) + emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx)); +} + +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(); +} + +void ContentLibraryUserModel::resetModel() +{ + beginResetModel(); + endResetModel(); +} + +void ContentLibraryUserModel::applyToSelected(ContentLibraryMaterial *mat, bool add) +{ + emit applyToSelectedTriggered(mat, add); +} + +void ContentLibraryUserModel::addToProject(QObject *item) +{ + QString bundleDir; + TypeName type; + QString qmlFile; + QStringList files; + if (auto mat = qobject_cast<ContentLibraryMaterial *>(item)) { + bundleDir = mat->dirPath(); + type = mat->type(); + qmlFile = mat->qml(); + files = mat->files() + m_bundleMaterialSharedFiles; + } else if (auto itm = qobject_cast<ContentLibraryItem *>(item)) { + bundleDir = m_bundlePath3D.toString(); + type = itm->type(); + qmlFile = itm->qml(); + files = itm->files() + m_bundle3DSharedFiles; + } else { + qWarning() << __FUNCTION__ << "Unsupported Item"; + return; + } + + QString err = m_widget->importer()->importComponent(bundleDir, type, qmlFile, files); + + if (err.isEmpty()) + m_widget->setImporterRunning(true); + else + qWarning() << __FUNCTION__ << err; +} + +void ContentLibraryUserModel::removeFromProject(QObject *item) +{ + TypeName type; + QString qml; + if (auto mat = qobject_cast<ContentLibraryMaterial *>(item)) { + type = mat->type(); + qml = mat->qml(); + } else if (auto itm = qobject_cast<ContentLibraryItem *>(item)) { + type = itm->type(); + qml = itm->qml(); + } else { + qWarning() << __FUNCTION__ << "Unsupported Item"; + return; + } + + QString err = m_widget->importer()->unimportComponent(type, qml); + + if (err.isEmpty()) + m_widget->setImporterRunning(true); + else + qWarning() << __FUNCTION__ << err; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h new file mode 100644 index 0000000000..2a7f9a66f3 --- /dev/null +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h @@ -0,0 +1,135 @@ +// 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 <QAbstractListModel> +#include <QJsonObject> + +QT_FORWARD_DECLARE_CLASS(QUrl) + +namespace QmlDesigner { + +class ContentLibraryItem; +class ContentLibraryMaterial; +class ContentLibraryTexture; +class ContentLibraryWidget; +class NodeMetaInfo; + +class ContentLibraryUserModel : public QAbstractListModel +{ + Q_OBJECT + + Q_PROPERTY(bool matBundleExists READ matBundleExists NOTIFY matBundleExistsChanged) + Q_PROPERTY(bool bundle3DExists MEMBER m_bundle3DExists NOTIFY bundle3DExistsChanged) + Q_PROPERTY(bool hasRequiredQuick3DImport READ hasRequiredQuick3DImport NOTIFY hasRequiredQuick3DImportChanged) + Q_PROPERTY(QList<ContentLibraryMaterial *> userMaterials MEMBER m_userMaterials NOTIFY userMaterialsChanged) + Q_PROPERTY(QList<ContentLibraryTexture *> userTextures MEMBER m_userTextures NOTIFY userTexturesChanged) + Q_PROPERTY(QList<ContentLibraryItem *> user3DItems MEMBER m_user3DItems NOTIFY user3DItemsChanged) + Q_PROPERTY(QList<ContentLibraryItem *> 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<int, QByteArray> roleNames() const override; + + void setSearchText(const QString &searchText); + void updateMaterialsImportedState(const QStringList &importedItems); + void update3DImportedState(const QStringList &importedItems); + + QPair<QString, QString> getUniqueLibMaterialNames(const QString &defaultName = "Material") const; + QPair<QString, QString> getUniqueLib3DNames(const QString &defaultName = "Item") const; + + void setQuick3DImportVersion(int major, int minor); + + bool hasRequiredQuick3DImport() const; + + bool matBundleExists() const; + + void resetModel(); + void updateNoMatchMaterials(); + void updateNoMatchTextures(); + void updateNoMatch3D(); + + void addMaterial(const QString &name, const QString &qml, const QUrl &icon, const QStringList &files); + void add3DItem(const QString &name, const QString &qml, const QUrl &icon, const QStringList &files); + void refresh3DSection(); + void addTextures(const QStringList &paths); + + void add3DInstance(ContentLibraryItem *bundleItem); + + void remove3DFromContentLibByName(const QString &qmlFileName); + + void setBundleObj(const QJsonObject &newBundleObj); + QJsonObject &bundleJsonMaterialObjectRef(); + QJsonObject &bundleJson3DObjectRef(); + + void loadBundles(); + + Q_INVOKABLE void applyToSelected(QmlDesigner::ContentLibraryMaterial *mat, bool add = false); + Q_INVOKABLE void addToProject(QObject *item); + Q_INVOKABLE void removeFromProject(QObject *item); + Q_INVOKABLE void removeTexture(QmlDesigner::ContentLibraryTexture *tex); + Q_INVOKABLE void removeFromContentLib(QObject *item); + +signals: + void hasRequiredQuick3DImportChanged(); + void userMaterialsChanged(); + void userTexturesChanged(); + void user3DItemsChanged(); + void userEffectsChanged(); + void applyToSelectedTriggered(QmlDesigner::ContentLibraryMaterial *mat, bool add = false); + void matBundleExistsChanged(); + void bundle3DExistsChanged(); + +private: + enum SectionIndex { MaterialsSectionIdx = 0, + TexturesSectionIdx, + Items3DSectionIdx, + EffectsSectionIdx }; + + void loadMaterialBundle(); + void load3DBundle(); + void loadTextureBundle(); + bool isValidIndex(int idx) const; + void removeMaterialFromContentLib(ContentLibraryMaterial *mat); + void remove3DFromContentLib(ContentLibraryItem *item); + QPair<QString, QString> getUniqueLibItemNames(const QString &defaultName, + const QJsonObject &bundleObj) const; + + ContentLibraryWidget *m_widget = nullptr; + QString m_searchText; + QString m_bundleIdMaterial; + QString m_bundleId3D; + QStringList m_bundleMaterialSharedFiles; + QStringList m_bundle3DSharedFiles; + Utils::FilePath m_bundlePathMaterial; + Utils::FilePath m_bundlePath3D; + + QList<ContentLibraryMaterial *> m_userMaterials; + QList<ContentLibraryTexture *> m_userTextures; + QList<ContentLibraryItem *> m_userEffects; + QList<ContentLibraryItem *> m_user3DItems; + QStringList m_userCategories; + + QJsonObject m_bundleObjMaterial; + QJsonObject m_bundleObj3D; + + bool m_noMatchMaterials = true; + bool m_noMatchTextures = true; + bool m_noMatch3D = true; + bool m_noMatchEffects = true; + bool m_matBundleExists = false; + bool m_bundle3DExists = false; + + int m_quick3dMajorVersion = -1; + int m_quick3dMinorVersion = -1; + + enum Roles { NameRole = Qt::UserRole + 1, VisibleRole, ItemsRole, NoMatchRole }; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp index 61ae078ea8..9258faaf33 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp @@ -4,22 +4,29 @@ #include "contentlibraryview.h" #include "contentlibrarybundleimporter.h" -#include "contentlibraryeffect.h" +#include "contentlibraryitem.h" #include "contentlibraryeffectsmodel.h" #include "contentlibrarymaterial.h" #include "contentlibrarymaterialsmodel.h" #include "contentlibrarytexture.h" #include "contentlibrarytexturesmodel.h" +#include "contentlibraryusermodel.h" #include "contentlibrarywidget.h" -#include "externaldependenciesinterface.h" -#include "nodelistproperty.h" -#include "qmldesignerconstants.h" -#include "qmlobjectnode.h" -#include "variantproperty.h" -#include <utils3d.h> -#include <coreplugin/messagebox.h> +#include <asset.h> +#include <bindingproperty.h> +#include <designerpaths.h> +#include <documentmanager.h> #include <enumeration.h> +#include <externaldependenciesinterface.h> +#include <nodelistproperty.h> +#include <qmldesignerconstants.h> +#include <qmldesignerplugin.h> +#include <qmlobjectnode.h> +#include <uniquename.h> +#include <utils3d.h> +#include <variantproperty.h> + #include <utils/algorithm.h> #ifndef QMLDESIGNER_TEST @@ -30,12 +37,19 @@ #include <qtsupport/qtkitaspect.h> #endif +#include <QJsonArray> +#include <QJsonDocument> +#include <QJsonObject> +#include <QMessageBox> +#include <QPixmap> #include <QVector3D> namespace QmlDesigner { -ContentLibraryView::ContentLibraryView(ExternalDependenciesInterface &externalDependencies) +ContentLibraryView::ContentLibraryView(AsynchronousImageCache &imageCache, + ExternalDependenciesInterface &externalDependencies) : AbstractView(externalDependencies) + , m_imageCache(imageCache) , m_createTexture(this) {} @@ -60,9 +74,9 @@ WidgetInfo ContentLibraryView::widgetInfo() [&] (QmlDesigner::ContentLibraryTexture *tex) { m_draggedBundleTexture = tex; }); - connect(m_widget, &ContentLibraryWidget::bundleEffectDragStarted, this, - [&] (QmlDesigner::ContentLibraryEffect *eff) { - m_draggedBundleEffect = eff; + connect(m_widget, &ContentLibraryWidget::bundleItemDragStarted, this, + [&] (QmlDesigner::ContentLibraryItem *item) { + m_draggedBundleItem = item; }); connect(m_widget, &ContentLibraryWidget::addTextureRequested, this, @@ -79,138 +93,150 @@ WidgetInfo ContentLibraryView::widgetInfo() m_widget->environmentsModel()->setHasSceneEnv(sceneEnvExists); }); - ContentLibraryMaterialsModel *materialsModel = m_widget->materialsModel().data(); - - connect(materialsModel, + connect(m_widget->materialsModel(), &ContentLibraryMaterialsModel::applyToSelectedTriggered, this, [&](ContentLibraryMaterial *bundleMat, bool add) { - if (m_selectedModels.isEmpty()) - return; + if (m_selectedModels.isEmpty()) + return; - m_bundleMaterialTargets = m_selectedModels; - m_bundleMaterialAddToSelected = add; + m_bundleMaterialTargets = m_selectedModels; + m_bundleMaterialAddToSelected = add; - ModelNode defaultMat = getBundleMaterialDefaultInstance(bundleMat->type()); - if (defaultMat.isValid()) - applyBundleMaterialToDropTarget(defaultMat); - else - m_widget->materialsModel()->addToProject(bundleMat); - }); + ModelNode defaultMat = getBundleMaterialDefaultInstance(bundleMat->type()); + if (defaultMat.isValid()) + applyBundleMaterialToDropTarget(defaultMat); + else + m_widget->materialsModel()->addToProject(bundleMat); + }); -#ifdef QDS_USE_PROJECTSTORAGE - connect(materialsModel, - &ContentLibraryMaterialsModel::bundleMaterialImported, - this, - [&](const QmlDesigner::TypeName &typeName) { - applyBundleMaterialToDropTarget({}, typeName); - updateBundleMaterialsImportedState(); - }); -#else - connect(materialsModel, - &ContentLibraryMaterialsModel::bundleMaterialImported, + connect(m_widget->userModel(), + &ContentLibraryUserModel::applyToSelectedTriggered, this, - [&](const QmlDesigner::NodeMetaInfo &metaInfo) { - applyBundleMaterialToDropTarget({}, metaInfo); - updateBundleMaterialsImportedState(); - }); -#endif + [&](ContentLibraryMaterial *bundleMat, bool add) { + if (m_selectedModels.isEmpty()) + return; - connect(materialsModel, &ContentLibraryMaterialsModel::bundleMaterialAboutToUnimport, this, - [&] (const QmlDesigner::TypeName &type) { - // delete instances of the bundle material that is about to be unimported - executeInTransaction("ContentLibraryView::widgetInfo", [&] { - ModelNode matLib = Utils3D::materialLibraryNode(this); - if (!matLib.isValid()) - return; + m_bundleMaterialTargets = m_selectedModels; + m_bundleMaterialAddToSelected = add; - Utils::reverseForeach(matLib.directSubModelNodes(), [&](const ModelNode &mat) { - if (mat.isValid() && mat.type() == type) - QmlObjectNode(mat).destroy(); - }); - }); + ModelNode defaultMat = getBundleMaterialDefaultInstance(bundleMat->type()); + if (defaultMat.isValid()) + applyBundleMaterialToDropTarget(defaultMat); + else + m_widget->userModel()->addToProject(bundleMat); }); - connect(materialsModel, &ContentLibraryMaterialsModel::bundleMaterialUnimported, this, - &ContentLibraryView::updateBundleMaterialsImportedState); + connectImporter(); + } - ContentLibraryEffectsModel *effectsModel = m_widget->effectsModel().data(); + return createWidgetInfo(m_widget.data(), + "ContentLibrary", + WidgetInfo::LeftPane, + 0, + tr("Content Library")); +} +void ContentLibraryView::connectImporter() +{ #ifdef QDS_USE_PROJECTSTORAGE - connect(effectsModel, - &ContentLibraryEffectsModel::bundleItemImported, - this, - [&](const QmlDesigner::TypeName &typeName) { - QTC_ASSERT(typeName.size(), return); - - if (!m_bundleEffectTarget) - m_bundleEffectTarget = Utils3D::active3DSceneNode(this); + connect(m_widget->importer(), + &ContentLibraryBundleImporter::importFinished, + this, + [&](const QmlDesigner::TypeName &typeName, const QString &bundleId) { + QTC_ASSERT(typeName.size(), return); + if (isMaterialBundle(bundleId)) { + applyBundleMaterialToDropTarget({}, typeName); + } else if (isItemBundle(bundleId)) { + if (!m_bundleItemTarget) + m_bundleItemTarget = Utils3D::active3DSceneNode(this); - QTC_ASSERT(m_bundleEffectTarget, return); + QTC_ASSERT(m_bundleItemTarget, return); executeInTransaction("ContentLibraryView::widgetInfo", [&] { - QVector3D pos = m_bundleEffectPos.value<QVector3D>(); - ModelNode newEffNode = createModelNode( + QVector3D pos = m_bundleItemPos.value<QVector3D>(); + ModelNode newNode = createModelNode( typeName, -1, -1, {{"x", pos.x()}, {"y", pos.y()}, {"z", pos.z()}}); - m_bundleEffectTarget.defaultNodeListProperty().reparentHere(newEffNode); + m_bundleItemTarget.defaultNodeListProperty().reparentHere(newNode); clearSelectedModelNodes(); - selectModelNode(newEffNode); + selectModelNode(newNode); }); - updateBundleEffectsImportedState(); - m_bundleEffectTarget = {}; - m_bundleEffectPos = {}; - }); + m_bundleItemTarget = {}; + m_bundleItemPos = {}; + } + }); #else - connect(effectsModel, - &ContentLibraryEffectsModel::bundleItemImported, - this, - [&](const QmlDesigner::NodeMetaInfo &metaInfo) { - QTC_ASSERT(metaInfo.isValid(), return); - - if (!m_bundleEffectTarget) - m_bundleEffectTarget = Utils3D::active3DSceneNode(this); + connect(m_widget->importer(), + &ContentLibraryBundleImporter::importFinished, + this, + [&](const QmlDesigner::NodeMetaInfo &metaInfo, const QString &bundleId) { + QTC_ASSERT(metaInfo.isValid(), return); + if (isMaterialBundle(bundleId)) { + applyBundleMaterialToDropTarget({}, metaInfo); + } else if (isItemBundle(bundleId)) { + if (!m_bundleItemTarget) + m_bundleItemTarget = Utils3D::active3DSceneNode(this); - QTC_ASSERT(m_bundleEffectTarget, return); + QTC_ASSERT(m_bundleItemTarget, return); - executeInTransaction("ContentLibraryView::widgetInfo", [&] { - QVector3D pos = m_bundleEffectPos.value<QVector3D>(); - ModelNode newEffNode = createModelNode(metaInfo.typeName(), + executeInTransaction("ContentLibraryView::connectImporter", [&] { + QVector3D pos = m_bundleItemPos.value<QVector3D>(); + ModelNode newNode = createModelNode(metaInfo.typeName(), metaInfo.majorVersion(), metaInfo.minorVersion(), {{"x", pos.x()}, {"y", pos.y()}, {"z", pos.z()}}); - m_bundleEffectTarget.defaultNodeListProperty().reparentHere(newEffNode); + m_bundleItemTarget.defaultNodeListProperty().reparentHere(newNode); clearSelectedModelNodes(); - selectModelNode(newEffNode); + selectModelNode(newNode); }); - updateBundleEffectsImportedState(); - m_bundleEffectTarget = {}; - m_bundleEffectPos = {}; - }); + m_bundleItemTarget = {}; + m_bundleItemPos = {}; + } + }); #endif - connect(effectsModel, &ContentLibraryEffectsModel::bundleItemAboutToUnimport, this, - [&] (const QmlDesigner::TypeName &type) { - // delete instances of the bundle effect that is about to be unimported - executeInTransaction("ContentLibraryView::widgetInfo", [&] { - NodeMetaInfo metaInfo = model()->metaInfo(type); - QList<ModelNode> effects = allModelNodesOfType(metaInfo); - for (ModelNode &eff : effects) - eff.destroy(); - }); + + connect(m_widget->importer(), &ContentLibraryBundleImporter::aboutToUnimport, this, + [&] (const QmlDesigner::TypeName &type, const QString &bundleId) { + if (isMaterialBundle(bundleId)) { + // delete instances of the bundle material that is about to be unimported + executeInTransaction("ContentLibraryView::connectImporter", [&] { + ModelNode matLib = Utils3D::materialLibraryNode(this); + if (!matLib.isValid()) + return; + + Utils::reverseForeach(matLib.directSubModelNodes(), [&](const ModelNode &mat) { + if (mat.isValid() && mat.type() == type) + QmlObjectNode(mat).destroy(); }); + }); + } else if (isItemBundle(bundleId)) { + // delete instances of the bundle item that is about to be unimported + executeInTransaction("ContentLibraryView::connectImporter", [&] { + NodeMetaInfo metaInfo = model()->metaInfo(type); + QList<ModelNode> nodes = allModelNodesOfType(metaInfo); + for (ModelNode &node : nodes) + node.destroy(); + }); + } + }); +} - connect(effectsModel, &ContentLibraryEffectsModel::bundleItemUnimported, this, - &ContentLibraryView::updateBundleEffectsImportedState); - } +bool ContentLibraryView::isMaterialBundle(const QString &bundleId) const +{ + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + return bundleId == compUtils.materialsBundleId() || bundleId == compUtils.userMaterialsBundleId(); +} - return createWidgetInfo(m_widget.data(), - "ContentLibrary", - WidgetInfo::LeftPane, - 0, - tr("Content Library")); +// item bundle includes effects and 3D components +bool ContentLibraryView::isItemBundle(const QString &bundleId) const +{ + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + return bundleId == compUtils.effectsBundleId() || bundleId == compUtils.userEffectsBundleId() + || bundleId == compUtils.user3DBundleId(); } void ContentLibraryView::modelAttached(Model *model) @@ -220,7 +246,6 @@ void ContentLibraryView::modelAttached(Model *model) m_hasQuick3DImport = model->hasImport("QtQuick3D"); updateBundlesQuick3DVersion(); - updateBundleMaterialsImportedState(); const bool hasLibrary = Utils3D::materialLibraryNode(this).isValid(); m_widget->setHasMaterialLibrary(hasLibrary); @@ -232,8 +257,17 @@ void ContentLibraryView::modelAttached(Model *model) m_widget->setHasActive3DScene(m_sceneId != -1); m_widget->clearSearchFilter(); + // bundles loading has to happen here, otherwise project path is not ready which will + // cause bundle items types to resolve incorrectly + m_widget->materialsModel()->loadBundle(); m_widget->effectsModel()->loadBundle(); - updateBundleEffectsImportedState(); + m_widget->userModel()->loadBundles(); + + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + m_widget->updateImportedState(compUtils.materialsBundleId()); + m_widget->updateImportedState(compUtils.effectsBundleId()); + m_widget->updateImportedState(compUtils.userMaterialsBundleId()); + m_widget->updateImportedState(compUtils.user3DBundleId()); } void ContentLibraryView::modelAboutToBeDetached(Model *model) @@ -275,7 +309,7 @@ void ContentLibraryView::selectedNodesChanged(const QList<ModelNode> &selectedNo return node.metaInfo().isQtQuick3DModel(); }); - m_widget->materialsModel()->setHasModelSelection(!m_selectedModels.isEmpty()); + m_widget->setHasModelSelection(!m_selectedModels.isEmpty()); } void ContentLibraryView::customNotification(const AbstractView *view, @@ -283,8 +317,6 @@ void ContentLibraryView::customNotification(const AbstractView *view, const QList<ModelNode> &nodeList, const QList<QVariant> &data) { - Q_UNUSED(data) - if (view == this) return; @@ -318,12 +350,29 @@ void ContentLibraryView::customNotification(const AbstractView *view, m_widget->addTexture(m_draggedBundleTexture); m_draggedBundleTexture = nullptr; - } else if (identifier == "drop_bundle_effect") { + } else if (identifier == "drop_bundle_item") { QTC_ASSERT(nodeList.size() == 1, return); - m_bundleEffectPos = data.size() == 1 ? data.first() : QVariant(); - m_widget->effectsModel()->addInstance(m_draggedBundleEffect); - m_bundleEffectTarget = nodeList.first() ? nodeList.first() : Utils3D::active3DSceneNode(this); + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + bool is3D = m_draggedBundleItem->type().startsWith(compUtils.user3DBundleType().toLatin1()); + + m_bundleItemPos = data.size() == 1 ? data.first() : QVariant(); + if (is3D) + m_widget->userModel()->add3DInstance(m_draggedBundleItem); + else + m_widget->effectsModel()->addInstance(m_draggedBundleItem); + m_bundleItemTarget = nodeList.first() ? nodeList.first() : Utils3D::active3DSceneNode(this); + } else if (identifier == "add_material_to_content_lib") { + QTC_ASSERT(nodeList.size() == 1 && data.size() == 1, return); + + addLibMaterial(nodeList.first(), data.first().value<QPixmap>()); + } else if (identifier == "add_assets_to_content_lib") { + addLibAssets(data.first().toStringList()); + } else if (identifier == "add_3d_to_content_lib") { + if (nodeList.first().isComponent()) + addLib3DComponent(nodeList.first()); + else + addLib3DItem(nodeList.first()); } } @@ -452,6 +501,325 @@ void ContentLibraryView::applyBundleMaterialToDropTarget(const ModelNode &bundle } #endif +// Add a project material to Content Library's user tab +void ContentLibraryView::addLibMaterial(const ModelNode &node, const QPixmap &iconPixmap) +{ + auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/materials/"); + + QString name = node.variantProperty("objectName").value().toString(); + auto [qml, icon] = m_widget->userModel()->getUniqueLibMaterialNames(node.id()); + + QString iconPath = QLatin1String("icons/%1").arg(icon); + QString fullIconPath = bundlePath.pathAppended(iconPath).toString(); + + // save icon + bool iconSaved = iconPixmap.save(fullIconPath); + if (!iconSaved) + qWarning() << __FUNCTION__ << "icon save failed"; + + // generate and save material Qml file + const QStringList depAssets = writeLibItemQml(node, qml); + + // add the material to the bundle json + QJsonObject &jsonRef = m_widget->userModel()->bundleJsonMaterialObjectRef(); + QJsonArray itemsArr = jsonRef.value("items").toArray(); + QJsonObject itemObj; + itemObj.insert("name", name); + itemObj.insert("qml", qml); + itemObj.insert("icon", iconPath); + QJsonArray filesArr; + for (const QString &assetPath : depAssets) + filesArr.append(assetPath); + itemObj.insert("files", filesArr); + + itemsArr.append(itemObj); + jsonRef["items"] = itemsArr; + + auto result = bundlePath.pathAppended(Constants::BUNDLE_JSON_FILENAME) + .writeFileContents(QJsonDocument(jsonRef).toJson()); + if (!result) + qWarning() << __FUNCTION__ << result.error(); + + // copy material assets to bundle folder + for (const QString &assetPath : depAssets) { + Utils::FilePath assetPathSource = DocumentManager::currentResourcePath().pathAppended(assetPath); + Utils::FilePath assetPathTarget = bundlePath.pathAppended(assetPath); + assetPathTarget.parentDir().ensureWritableDir(); + + auto result = assetPathSource.copyFile(assetPathTarget); + if (!result) + qWarning() << __FUNCTION__ << result.error(); + } + + m_widget->userModel()->addMaterial(name, qml, QUrl::fromLocalFile(fullIconPath), depAssets); +} + +QStringList ContentLibraryView::writeLibItemQml(const ModelNode &node, const QString &qml) +{ + QStringList depListIds; + auto [qmlString, assets] = modelNodeToQmlString(node, depListIds); + + qmlString.prepend("import QtQuick\nimport QtQuick3D\n\n"); + + QString itemType = QLatin1String(node.metaInfo().isQtQuick3DMaterial() ? "materials" : "3d"); + auto qmlPath = Utils::FilePath::fromString(QLatin1String("%1/User/%2/%3") + .arg(Paths::bundlesPathSetting(), itemType, qml)); + auto result = qmlPath.writeFileContents(qmlString.toUtf8()); + if (!result) + qWarning() << __FUNCTION__ << result.error(); + + return assets.values(); +} + +QPair<QString, QSet<QString>> ContentLibraryView::modelNodeToQmlString(const ModelNode &node, + QStringList &depListIds, + int depth) +{ + QString qml; + QSet<QString> 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<PropertyName> excludedProps = {"x", "y", "z", "eulerRotation.x", "eulerRotation.y", + "eulerRotation.z", "scale.x", "scale.y", "scale.z", + "pivot.x", "pivot.y", "pivot.z"}; + const QList<AbstractProperty> matProps = node.properties(); + for (const AbstractProperty &p : matProps) { + if (excludedProps.contains(p.name())) + continue; + + if (p.isVariantProperty()) { + QVariant pValue = p.toVariantProperty().value(); + QString val; + if (strcmp(pValue.typeName(), "QString") == 0 || strcmp(pValue.typeName(), "QColor") == 0) { + val = QLatin1String("\"%1\"").arg(pValue.toString()); + } else if (strcmp(pValue.typeName(), "QUrl") == 0) { + QString pValueStr = pValue.toString(); + val = QLatin1String("\"%1\"").arg(pValueStr); + if (!pValueStr.startsWith("#")) + assets.insert(pValue.toString()); + } else if (strcmp(pValue.typeName(), "QmlDesigner::Enumeration") == 0) { + val = pValue.value<QmlDesigner::Enumeration>().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}; +} + +void ContentLibraryView::addLibAssets(const QStringList &paths) +{ + auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/textures"); + QStringList pathsInBundle; + + const QStringList existingTextures = Utils::transform(bundlePath.dirEntries(QDir::Files), + [](const Utils::FilePath &path) { + return path.fileName(); + }); + + for (const QString &path : paths) { + auto assetFilePath = Utils::FilePath::fromString(path); + if (existingTextures.contains(assetFilePath.fileName())) + continue; + + Asset asset(path); + + // save icon + QString iconSavePath = bundlePath.pathAppended("icons/" + assetFilePath.baseName() + ".png") + .toString(); + QPixmap icon = asset.pixmap({120, 120}); + bool iconSaved = icon.save(iconSavePath); + if (!iconSaved) + qWarning() << __FUNCTION__ << "icon save failed"; + + // save asset + auto result = assetFilePath.copyFile(bundlePath.pathAppended(asset.fileName())); + if (!result) + qWarning() << __FUNCTION__ << result.error(); + + pathsInBundle.append(bundlePath.pathAppended(asset.fileName()).toString()); + } + + m_widget->userModel()->addTextures(pathsInBundle); +} + +void ContentLibraryView::addLib3DComponent(const ModelNode &node) +{ + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + + QString compBaseName = node.simplifiedTypeName(); + QString compFileName = compBaseName + ".qml"; + + Utils::FilePath compDir = DocumentManager::currentProjectDirPath() + .pathAppended(compUtils.import3dTypePath() + '/' + compBaseName); + + auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/3d/"); + + // confirm overwrite if an item with same name exists + if (bundlePath.pathAppended(compFileName).exists()) { + // Show a QML confirmation dialog before proceeding + QMessageBox::StandardButton reply = QMessageBox::question(m_widget, tr("3D Item Exists"), + tr("A 3D item with the same name '%1' already exists in the Content Library, are you sure you want to overwrite it?") + .arg(compFileName), QMessageBox::Yes | QMessageBox::No); + if (reply == QMessageBox::No) + return; + + // before overwriting remove old item (to avoid partial items and dangling assets) + m_widget->userModel()->remove3DFromContentLibByName(compFileName); + } + + // generate and save icon + QString iconPath = QLatin1String("icons/%1").arg(UniqueName::generateId(compBaseName) + ".png"); + QString fullIconPath = bundlePath.pathAppended(iconPath).toString(); + genAndSaveIcon(compDir.pathAppended(compFileName).path(), fullIconPath); + + const Utils::FilePaths sourceFiles = compDir.dirEntries({{}, QDir::Files, QDirIterator::Subdirectories}); + const QStringList ignoreList {"_importdata.json", "qmldir", compBaseName + ".hints"}; + QStringList filesList; // 3D component's assets (dependencies) + + for (const Utils::FilePath &sourcePath : sourceFiles) { + Utils::FilePath relativePath = sourcePath.relativePathFrom(compDir); + if (ignoreList.contains(sourcePath.fileName()) || relativePath.startsWith("source scene")) + continue; + + Utils::FilePath targetPath = bundlePath.pathAppended(relativePath.path()); + targetPath.parentDir().ensureWritableDir(); + + // copy item from project to user bundle + auto result = sourcePath.copyFile(targetPath); + if (!result) + qWarning() << __FUNCTION__ << result.error(); + + if (sourcePath.fileName() != compFileName) // skip component file (only collect dependencies) + filesList.append(relativePath.path()); + } + + // add the item to the bundle json + QJsonObject &jsonRef = m_widget->userModel()->bundleJson3DObjectRef(); + QJsonArray itemsArr = jsonRef.value("items").toArray(); + itemsArr.append(QJsonObject { + {"name", node.simplifiedTypeName()}, + {"qml", compFileName}, + {"icon", iconPath}, + {"files", QJsonArray::fromStringList(filesList)} + }); + + jsonRef["items"] = itemsArr; + + auto result = bundlePath.pathAppended(Constants::BUNDLE_JSON_FILENAME) + .writeFileContents(QJsonDocument(jsonRef).toJson()); + if (!result) + qWarning() << __FUNCTION__ << result.error(); + + m_widget->userModel()->add3DItem(compBaseName, compFileName, QUrl::fromLocalFile(fullIconPath), + filesList); +} + +void ContentLibraryView::addLib3DItem(const ModelNode &node) +{ + auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/3d/"); + + QString name = node.variantProperty("objectName").value().toString(); + auto [qml, icon] = m_widget->userModel()->getUniqueLib3DNames(node.id()); + QString iconPath = QLatin1String("icons/%1").arg(icon); + + if (name.isEmpty()) + name = node.id(); + + // generate and save item Qml file + const QStringList depAssets = writeLibItemQml(node, qml); + + // generate and save icon + QString qmlPath = QLatin1String("%1/User/3d/%2").arg(Paths::bundlesPathSetting(), qml); + QString fullIconPath = bundlePath.pathAppended(iconPath).toString(); + genAndSaveIcon(qmlPath, fullIconPath); + + // add the item to the bundle json + QJsonObject &jsonRef = m_widget->userModel()->bundleJson3DObjectRef(); + QJsonArray itemsArr = jsonRef.value("items").toArray(); + itemsArr.append(QJsonObject { + {"name", name}, + {"qml", qml}, + {"icon", iconPath}, + {"files", QJsonArray::fromStringList(depAssets)} + }); + + jsonRef["items"] = itemsArr; + + auto result = bundlePath.pathAppended(Constants::BUNDLE_JSON_FILENAME) + .writeFileContents(QJsonDocument(jsonRef).toJson()); + if (!result) + qWarning() << __FUNCTION__ << result.error(); + + // copy item's assets to bundle folder + for (const QString &assetPath : depAssets) { + Utils::FilePath assetPathSource = DocumentManager::currentResourcePath().pathAppended(assetPath); + Utils::FilePath assetPathTarget = bundlePath.pathAppended(assetPath); + assetPathTarget.parentDir().ensureWritableDir(); + + auto result = assetPathSource.copyFile(assetPathTarget); + if (!result) + qWarning() << __FUNCTION__ << result.error(); + } + + m_widget->userModel()->add3DItem(name, qml, QUrl::fromLocalFile(fullIconPath), depAssets); +} + +/** + * @brief Generates an icon image from a qml component + * @param qmlPath path to the qml component file to be rendered + * @param iconPath output save path of the generated icon + */ +void ContentLibraryView::genAndSaveIcon(const QString &qmlPath, const QString &iconPath) +{ + m_imageCache.requestSmallImage( + Utils::PathString{qmlPath}, + [&, qmlPath, iconPath](const QImage &image) { + bool iconSaved = image.save(iconPath); + if (iconSaved) + m_widget->userModel()->refresh3DSection(); + else + qWarning() << "ContentLibraryView::genAndSaveIcon(): icon save failed"; + }, + [&](ImageCache::AbortReason abortReason) { + if (abortReason == ImageCache::AbortReason::Abort) { + qWarning() << QLatin1String("ContentLibraryView::genAndSaveIcon(): icon generation " + "failed for path %1, reason: Abort").arg(qmlPath); + } else if (abortReason == ImageCache::AbortReason::Failed) { + qWarning() << QLatin1String("ContentLibraryView::genAndSaveIcon(): icon generation " + "failed for path %1, reason: Failed").arg(qmlPath); + } else if (abortReason == ImageCache::AbortReason::NoEntry) { + qWarning() << QLatin1String("ContentLibraryView::genAndSaveIcon(): icon generation " + "failed for path %1, reason: NoEntry").arg(qmlPath); + } + }); +} + ModelNode ContentLibraryView::getBundleMaterialDefaultInstance(const TypeName &type) { ModelNode matLib = Utils3D::materialLibraryNode(this); @@ -477,6 +845,7 @@ ModelNode ContentLibraryView::getBundleMaterialDefaultInstance(const TypeName &t return {}; } + #ifdef QDS_USE_PROJECTSTORAGE ModelNode ContentLibraryView::createMaterial(const TypeName &typeName) { @@ -491,7 +860,7 @@ ModelNode ContentLibraryView::createMaterial(const TypeName &typeName) QString newName = QString::fromUtf8(typeName).replace(rgx, " \\1\\2").trimmed(); if (newName.endsWith(" Material")) newName.chop(9); // remove trailing " Material" - QString newId = model()->generateIdFromName(newName, "material"); + QString newId = model()->generateNewId(newName, "material"); newMatNode.setIdWithRefactoring(newId); VariantProperty objNameProp = newMatNode.variantProperty("objectName"); @@ -517,7 +886,7 @@ ModelNode ContentLibraryView::createMaterial(const NodeMetaInfo &metaInfo) QString newName = QString::fromLatin1(metaInfo.simplifiedTypeName()).replace(rgx, " \\1\\2").trimmed(); if (newName.endsWith(" Material")) newName.chop(9); // remove trailing " Material" - QString newId = model()->generateIdFromName(newName, "material"); + QString newId = model()->generateNewId(newName, "material"); newMatNode.setIdWithRefactoring(newId); VariantProperty objNameProp = newMatNode.variantProperty("objectName"); @@ -529,44 +898,6 @@ ModelNode ContentLibraryView::createMaterial(const NodeMetaInfo &metaInfo) } #endif -void ContentLibraryView::updateBundleMaterialsImportedState() -{ - using namespace Utils; - - if (!m_widget->materialsModel()->bundleImporter()) - return; - - QStringList importedBundleMats; - - FilePath materialBundlePath = m_widget->materialsModel()->bundleImporter()->resolveBundleImportPath(); - - if (materialBundlePath.exists()) { - importedBundleMats = transform(materialBundlePath.dirEntries({{"*.qml"}, QDir::Files}), - [](const FilePath &f) { return f.fileName().chopped(4); }); - } - - m_widget->materialsModel()->updateImportedState(importedBundleMats); -} - -void ContentLibraryView::updateBundleEffectsImportedState() -{ - using namespace Utils; - - if (!m_widget->effectsModel()->bundleImporter()) - return; - - QStringList importedBundleEffs; - - FilePath bundlePath = m_widget->effectsModel()->bundleImporter()->resolveBundleImportPath(); - - if (bundlePath.exists()) { - importedBundleEffs = transform(bundlePath.dirEntries({{"*.qml"}, QDir::Files}), - [](const FilePath &f) { return f.fileName().chopped(4); }); - } - - m_widget->effectsModel()->updateImportedState(importedBundleEffs); -} - void ContentLibraryView::updateBundlesQuick3DVersion() { bool hasImport = false; @@ -601,6 +932,7 @@ void ContentLibraryView::updateBundlesQuick3DVersion() #endif m_widget->materialsModel()->setQuick3DImportVersion(major, minor); m_widget->effectsModel()->setQuick3DImportVersion(major, minor); + m_widget->userModel()->setQuick3DImportVersion(major, minor); } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h index 3b57b7a4ab..914a8b8ea0 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h @@ -3,16 +3,19 @@ #pragma once -#include "abstractview.h" -#include "createtexture.h" -#include "nodemetainfo.h" +#include <asynchronousimagecache.h> +#include <abstractview.h> +#include <createtexture.h> +#include <nodemetainfo.h> #include <QObject> #include <QPointer> +QT_FORWARD_DECLARE_CLASS(QPixmap) + namespace QmlDesigner { -class ContentLibraryEffect; +class ContentLibraryItem; class ContentLibraryMaterial; class ContentLibraryTexture; class ContentLibraryWidget; @@ -23,7 +26,8 @@ class ContentLibraryView : public AbstractView Q_OBJECT public: - ContentLibraryView(ExternalDependenciesInterface &externalDependencies); + ContentLibraryView(AsynchronousImageCache &imageCache, + ExternalDependenciesInterface &externalDependencies); ~ContentLibraryView() override; bool hasWidget() const override; @@ -46,10 +50,20 @@ public: const QVariant &data) override; private: + void connectImporter(); + bool isMaterialBundle(const QString &bundleId) const; + bool isItemBundle(const QString &bundleId) const; void active3DSceneChanged(qint32 sceneId); - void updateBundleMaterialsImportedState(); - void updateBundleEffectsImportedState(); void updateBundlesQuick3DVersion(); + void addLibMaterial(const ModelNode &node, const QPixmap &iconPixmap); + void addLibAssets(const QStringList &paths); + void addLib3DComponent(const ModelNode &node); + void addLib3DItem(const ModelNode &node); + void genAndSaveIcon(const QString &qmlPath, const QString &iconPath); + QStringList writeLibItemQml(const ModelNode &node, const QString &qml); + QPair<QString, QSet<QString>> modelNodeToQmlString(const ModelNode &node, QStringList &depListIds, + int depth = 0); + #ifdef QDS_USE_PROJECTSTORAGE void applyBundleMaterialToDropTarget(const ModelNode &bundleMat, const TypeName &typeName = {}); #else @@ -64,12 +78,13 @@ private: #endif QPointer<ContentLibraryWidget> m_widget; QList<ModelNode> m_bundleMaterialTargets; - ModelNode m_bundleEffectTarget; // target of the dropped bundle effect - QVariant m_bundleEffectPos; // pos of the dropped bundle effect + ModelNode m_bundleItemTarget; // target of the dropped bundle item + QVariant m_bundleItemPos; // pos of the dropped bundle item QList<ModelNode> m_selectedModels; // selected 3D model nodes ContentLibraryMaterial *m_draggedBundleMaterial = nullptr; ContentLibraryTexture *m_draggedBundleTexture = nullptr; - ContentLibraryEffect *m_draggedBundleEffect = nullptr; + ContentLibraryItem *m_draggedBundleItem = nullptr; + AsynchronousImageCache &m_imageCache; bool m_bundleMaterialAddToSelected = false; bool m_hasQuick3DImport = false; qint32 m_sceneId = -1; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp index c885a76ba7..72bece4c98 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp @@ -3,13 +3,15 @@ #include "contentlibrarywidget.h" -#include "contentlibraryeffect.h" +#include "contentlibrarybundleimporter.h" #include "contentlibraryeffectsmodel.h" +#include "contentlibraryitem.h" #include "contentlibrarymaterial.h" #include "contentlibrarymaterialsmodel.h" #include "contentlibrarytexture.h" #include "contentlibrarytexturesmodel.h" #include "contentlibraryiconprovider.h" +#include "contentlibraryusermodel.h" #include "utils/filedownloader.h" #include "utils/fileextractor.h" @@ -17,6 +19,7 @@ #include <coreplugin/icore.h> #include <designerpaths.h> +#include <nodemetainfo.h> #include <qmldesignerconstants.h> #include <qmldesignerplugin.h> @@ -39,7 +42,6 @@ #include <QQuickWidget> #include <QRegularExpression> #include <QShortcut> -#include <QStandardPaths> #include <QVBoxLayout> namespace QmlDesigner { @@ -66,18 +68,18 @@ bool ContentLibraryWidget::eventFilter(QObject *obj, QEvent *event) Model *model = document->currentModel(); QTC_ASSERT(model, return false); - if (m_effectToDrag) { + if (m_itemToDrag) { QMouseEvent *me = static_cast<QMouseEvent *>(event); if ((me->globalPos() - m_dragStartPoint).manhattanLength() > 20) { QByteArray data; QMimeData *mimeData = new QMimeData; QDataStream stream(&data, QIODevice::WriteOnly); - stream << m_effectToDrag->type(); - mimeData->setData(Constants::MIME_TYPE_BUNDLE_EFFECT, data); + stream << m_itemToDrag->type(); + mimeData->setData(Constants::MIME_TYPE_BUNDLE_ITEM, data); - emit bundleEffectDragStarted(m_effectToDrag); - model->startDrag(mimeData, m_effectToDrag->icon().toLocalFile()); - m_effectToDrag = nullptr; + emit bundleItemDragStarted(m_itemToDrag); + model->startDrag(mimeData, m_itemToDrag->icon().toLocalFile()); + m_itemToDrag = nullptr; } } else if (m_materialToDrag) { QMouseEvent *me = static_cast<QMouseEvent *>(event); @@ -100,10 +102,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()); @@ -111,7 +113,7 @@ bool ContentLibraryWidget::eventFilter(QObject *obj, QEvent *event) } } } else if (event->type() == QMouseEvent::MouseButtonRelease) { - m_effectToDrag = nullptr; + m_itemToDrag = nullptr; m_materialToDrag = nullptr; m_textureToDrag = nullptr; setIsDragging(false); @@ -121,11 +123,12 @@ bool ContentLibraryWidget::eventFilter(QObject *obj, QEvent *event) } ContentLibraryWidget::ContentLibraryWidget() - : m_quickWidget(new StudioQuickWidget(this)) + : m_quickWidget(Utils::makeUniqueObjectPtr<StudioQuickWidget>(this)) , m_materialsModel(new ContentLibraryMaterialsModel(this)) , m_texturesModel(new ContentLibraryTexturesModel("Textures", this)) , m_environmentsModel(new ContentLibraryTexturesModel("Environments", this)) , m_effectsModel(new ContentLibraryEffectsModel(this)) + , m_userModel(new ContentLibraryUserModel(this)) { qmlRegisterType<QmlDesigner::FileDownloader>("WebFetcher", 1, 0, "FileDownloader"); qmlRegisterType<QmlDesigner::FileExtractor>("WebFetcher", 1, 0, "FileExtractor"); @@ -140,18 +143,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); @@ -159,7 +156,7 @@ ContentLibraryWidget::ContentLibraryWidget() auto layout = new QVBoxLayout(this); layout->setContentsMargins({}); layout->setSpacing(0); - layout->addWidget(m_quickWidget.data()); + layout->addWidget(m_quickWidget.get()); updateSearch(); @@ -177,38 +174,95 @@ 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(); + createImporter(); +} + +void ContentLibraryWidget::createImporter() +{ + m_importer = new ContentLibraryBundleImporter(); +#ifdef QDS_USE_PROJECTSTORAGE + connect(m_importer, + &ContentLibraryBundleImporter::importFinished, + this, + [&](const QmlDesigner::TypeName &typeName, const QString &bundleId) { + setImporterRunning(false); + if (typeName.size()) + updateImportedState(bundleId); + }); +#else + connect(m_importer, + &ContentLibraryBundleImporter::importFinished, + this, + [&](const QmlDesigner::NodeMetaInfo &metaInfo, const QString &bundleId) { + setImporterRunning(false); + if (metaInfo.isValid()) + updateImportedState(bundleId); + }); +#endif + + connect(m_importer, &ContentLibraryBundleImporter::unimportFinished, this, + [&](const QmlDesigner::NodeMetaInfo &metaInfo, const QString &bundleId) { + Q_UNUSED(metaInfo) + setImporterRunning(false); + updateImportedState(bundleId); + }); +} + +void ContentLibraryWidget::updateImportedState(const QString &bundleId) +{ + if (!m_importer) + return; + + Utils::FilePath bundlePath = m_importer->resolveBundleImportPath(bundleId); + + QStringList importedItems; + if (bundlePath.exists()) { + importedItems = transform(bundlePath.dirEntries({{"*.qml"}, QDir::Files}), + [](const Utils::FilePath &f) { return f.baseName(); }); + } + + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + if (bundleId == compUtils.materialsBundleId()) + m_materialsModel->updateImportedState(importedItems); + else if (bundleId == compUtils.effectsBundleId()) + m_effectsModel->updateImportedState(importedItems); + else if (bundleId == compUtils.userMaterialsBundleId()) + m_userModel->updateMaterialsImportedState(importedItems); + else if (bundleId == compUtils.user3DBundleId()) + m_userModel->update3DImportedState(importedItems); +} + +ContentLibraryBundleImporter *ContentLibraryWidget::importer() const +{ + return m_importer; } -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<QVariantMap, QVariantMap, QVariantMap> ContentLibraryWidget::compareTextureMetaFiles( @@ -272,9 +326,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); @@ -314,15 +368,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(); @@ -433,50 +480,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; @@ -498,32 +540,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); @@ -543,13 +588,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(); @@ -572,7 +611,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(); @@ -589,7 +628,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(); @@ -700,6 +739,20 @@ void ContentLibraryWidget::setIsQt6Project(bool b) emit isQt6ProjectChanged(); } +bool ContentLibraryWidget::importerRunning() const +{ + return m_importerRunning; +} + +void ContentLibraryWidget::setImporterRunning(bool b) +{ + if (m_importerRunning == b) + return; + + m_importerRunning = b; + emit importerRunningChanged(); +} + void ContentLibraryWidget::reloadQmlSource() { const QString materialBrowserQmlPath = qmlSourcesPath() + "/ContentLibrary.qml"; @@ -715,6 +768,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(); } @@ -726,32 +780,9 @@ void ContentLibraryWidget::setIsDragging(bool val) } } -QString ContentLibraryWidget::findTextureBundlePath() -{ - QDir texBundleDir; - - if (!qEnvironmentVariable("TEXTURE_BUNDLE_PATH").isEmpty()) - texBundleDir.setPath(qEnvironmentVariable("TEXTURE_BUNDLE_PATH")); - else if (Utils::HostOsInfo::isMacHost()) - texBundleDir.setPath(QCoreApplication::applicationDirPath() + "/../Resources/texture_bundle"); - - // search for matBundleDir from exec dir and up - if (texBundleDir.dirName() == ".") { - texBundleDir.setPath(QCoreApplication::applicationDirPath()); - while (!texBundleDir.cd("texture_bundle") && texBundleDir.cdUp()) - ; // do nothing - - if (texBundleDir.dirName() != "texture_bundle") // bundlePathDir not found - return {}; - } - - return texBundleDir.path(); -} - -void ContentLibraryWidget::startDragEffect(QmlDesigner::ContentLibraryEffect *eff, - const QPointF &mousePos) +void ContentLibraryWidget::startDragItem(QmlDesigner::ContentLibraryItem *item, const QPointF &mousePos) { - m_effectToDrag = eff; + m_itemToDrag = item; m_dragStartPoint = mousePos.toPoint(); setIsDragging(true); } @@ -777,7 +808,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) @@ -785,7 +816,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) @@ -793,7 +824,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() @@ -821,4 +852,23 @@ QPointer<ContentLibraryEffectsModel> ContentLibraryWidget::effectsModel() const return m_effectsModel; } +QPointer<ContentLibraryUserModel> ContentLibraryWidget::userModel() const +{ + return m_userModel; +} + +bool ContentLibraryWidget::hasModelSelection() const +{ + return m_hasModelSelection; +} + +void ContentLibraryWidget::setHasModelSelection(bool b) +{ + if (b == m_hasModelSelection) + return; + + m_hasModelSelection = b; + emit hasModelSelectionChanged(); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h index ab71a3dc79..8e96d9d2f3 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h @@ -5,6 +5,10 @@ #include "createtexture.h" +#include <modelfwd.h> + +#include <utils/uniqueobjectptr.h> + #include <QFrame> #include <QPointer> @@ -18,12 +22,15 @@ class StudioQuickWidget; namespace QmlDesigner { -class ContentLibraryEffect; +class ContentLibraryBundleImporter; class ContentLibraryEffectsModel; +class ContentLibraryItem; class ContentLibraryMaterial; class ContentLibraryMaterialsModel; class ContentLibraryTexture; class ContentLibraryTexturesModel; +class ContentLibraryUserModel; +class NodeMetaInfo; class ContentLibraryWidget : public QFrame { @@ -33,6 +40,8 @@ class ContentLibraryWidget : public QFrame Q_PROPERTY(bool hasMaterialLibrary READ hasMaterialLibrary NOTIFY hasMaterialLibraryChanged) Q_PROPERTY(bool hasActive3DScene READ hasActive3DScene WRITE setHasActive3DScene NOTIFY hasActive3DSceneChanged) Q_PROPERTY(bool isQt6Project READ isQt6Project NOTIFY isQt6ProjectChanged) + Q_PROPERTY(bool importerRunning READ importerRunning WRITE setImporterRunning NOTIFY importerRunningChanged) + Q_PROPERTY(bool hasModelSelection READ hasModelSelection NOTIFY hasModelSelectionChanged) // Needed for a workaround for a bug where after drag-n-dropping an item, the ScrollView scrolls to a random position Q_PROPERTY(bool isDragging MEMBER m_isDragging NOTIFY isDraggingChanged) @@ -57,16 +66,23 @@ public: bool isQt6Project() const; void setIsQt6Project(bool b); - Q_INVOKABLE void handleSearchFilterChanged(const QString &filterText); + bool importerRunning() const; + void setImporterRunning(bool b); + + bool hasModelSelection() const; + void setHasModelSelection(bool b); void setMaterialsModel(QPointer<ContentLibraryMaterialsModel> newMaterialsModel); + void updateImportedState(const QString &bundleId); QPointer<ContentLibraryMaterialsModel> materialsModel() const; QPointer<ContentLibraryTexturesModel> texturesModel() const; QPointer<ContentLibraryTexturesModel> environmentsModel() const; QPointer<ContentLibraryEffectsModel> effectsModel() const; + QPointer<ContentLibraryUserModel> userModel() const; - Q_INVOKABLE void startDragEffect(QmlDesigner::ContentLibraryEffect *eff, const QPointF &mousePos); + Q_INVOKABLE void handleSearchFilterChanged(const QString &filterText); + Q_INVOKABLE void startDragItem(QmlDesigner::ContentLibraryItem *item, const QPointF &mousePos); Q_INVOKABLE void startDragMaterial(QmlDesigner::ContentLibraryMaterial *mat, const QPointF &mousePos); Q_INVOKABLE void startDragTexture(QmlDesigner::ContentLibraryTexture *tex, const QPointF &mousePos); Q_INVOKABLE void addImage(QmlDesigner::ContentLibraryTexture *tex); @@ -77,8 +93,10 @@ public: QSize sizeHint() const override; + ContentLibraryBundleImporter *importer() const; + signals: - void bundleEffectDragStarted(QmlDesigner::ContentLibraryEffect *bundleEff); + void bundleItemDragStarted(QmlDesigner::ContentLibraryItem *item); void bundleMaterialDragStarted(QmlDesigner::ContentLibraryMaterial *bundleMat); void bundleTextureDragStarted(QmlDesigner::ContentLibraryTexture *bundleTex); void addTextureRequested(const QString texPath, QmlDesigner::AddTextureMode mode); @@ -88,6 +106,8 @@ signals: void hasActive3DSceneChanged(); void isDraggingChanged(); void isQt6ProjectChanged(); + void importerRunningChanged(); + void hasModelSelectionChanged(); protected: bool eventFilter(QObject *obj, QEvent *event) override; @@ -96,28 +116,31 @@ private: void reloadQmlSource(); 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<QVariantMap, QVariantMap, QVariantMap> compareTextureMetaFiles( const QString &existingMetaFile, const QString downloadedMetaFile); QStringList saveNewTextures(const QDir &bundleDir, const QStringList &newFiles); + void populateTextureBundleModels(); + void createImporter(); - QScopedPointer<StudioQuickWidget> m_quickWidget; + Utils::UniqueObjectPtr<StudioQuickWidget> m_quickWidget; QPointer<ContentLibraryMaterialsModel> m_materialsModel; QPointer<ContentLibraryTexturesModel> m_texturesModel; QPointer<ContentLibraryTexturesModel> m_environmentsModel; QPointer<ContentLibraryEffectsModel> m_effectsModel; + QPointer<ContentLibraryUserModel> m_userModel; + ContentLibraryBundleImporter *m_importer = nullptr; QShortcut *m_qmlSourceUpdateShortcut = nullptr; QString m_filterText; - ContentLibraryEffect *m_effectToDrag = nullptr; + ContentLibraryItem *m_itemToDrag = nullptr; ContentLibraryMaterial *m_materialToDrag = nullptr; ContentLibraryTexture *m_textureToDrag = nullptr; QPoint m_dragStartPoint; @@ -127,12 +150,10 @@ 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; + bool m_importerRunning = false; + bool m_hasModelSelection = false; + QString m_textureBundleUrl; + QString m_bundlePath; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/createtexture.cpp b/src/plugins/qmldesigner/components/createtexture.cpp index b6e99ae972..56056e3fd2 100644 --- a/src/plugins/qmldesigner/components/createtexture.cpp +++ b/src/plugins/qmldesigner/components/createtexture.cpp @@ -17,6 +17,7 @@ #include <coreplugin/messagebox.h> #include <QTimer> +#include <QUrl> 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); } diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp b/src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp index e84623e26c..36ce3373e5 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 72410ffb07..8da87ce119 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(); diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.cpp b/src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.cpp index a1c229f57e..159e7c31ee 100644 --- a/src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.cpp +++ b/src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.cpp @@ -422,7 +422,7 @@ QVariant KeyframeItem::itemChange(QGraphicsItem::GraphicsItemChange change, cons rseg.moveLeftTo(position); if (legalLeft() && legalRight()) { - if (qApp->keyboardModifiers().testFlag(Qt::ShiftModifier) && m_validPos.has_value()) { + if (qApp->keyboardModifiers().testFlag(Qt::ShiftModifier) && m_validPos) { if (m_firstPos) { auto firstToNow = QLineF(*m_firstPos, position); if (std::abs(firstToNow.dx()) > std::abs(firstToNow.dy())) diff --git a/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp b/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp index 95a260c26f..a1dfcc8f98 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 <model/modelutils.h> + #include <utils3d.h> #include <utils/expected.h> @@ -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/components/edit3d/cameraspeedconfiguration.cpp b/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.cpp index 76560ac192..f5a74ee864 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 55256890cb..fef06efc49 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/edit3dcanvas.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp index 2e8ef8304f..63d5e958b1 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp @@ -51,6 +51,8 @@ Edit3DCanvas::Edit3DCanvas(Edit3DWidget *parent) setAcceptDrops(true); setFocusPolicy(Qt::ClickFocus); m_busyIndicator->show(); + + installEventFilter(this); } void Edit3DCanvas::updateRenderImage(const QImage &img) @@ -79,11 +81,20 @@ QWidget *Edit3DCanvas::busyIndicator() const return m_busyIndicator; } +#ifdef Q_OS_MACOS +extern "C" bool AXIsProcessTrusted(); +#endif + void Edit3DCanvas::setFlyMode(bool enabled, const QPoint &pos) { if (m_flyMode == enabled) return; +#ifdef Q_OS_MACOS + if (!AXIsProcessTrusted()) + m_isTrusted = false; +#endif + m_flyMode = enabled; if (enabled) { @@ -132,6 +143,23 @@ void Edit3DCanvas::setFlyMode(bool enabled, const QPoint &pos) m_parent->view()->setFlyMode(enabled); } +bool Edit3DCanvas::eventFilter(QObject *obj, QEvent *event) +{ + if (m_flyMode && event->type() == QEvent::ShortcutOverride) { + // Suppress shortcuts that conflict with fly mode keys + const QList<int> controlKeys = { Qt::Key_W, Qt::Key_A, Qt::Key_S, + Qt::Key_D, Qt::Key_Q, Qt::Key_E, + Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, + Qt::Key_Right, Qt::Key_PageDown, Qt::Key_PageUp, + Qt::Key_Alt, Qt::Key_Shift }; + auto ke = static_cast<QKeyEvent *>(event); + if (controlKeys.contains(ke->key())) + event->accept(); + } + + return QObject::eventFilter(obj, event); +} + void Edit3DCanvas::mousePressEvent(QMouseEvent *e) { m_contextMenuPending = false; @@ -171,7 +199,8 @@ void Edit3DCanvas::mouseMoveEvent(QMouseEvent *e) // We notify explicit camera rotation need for puppet rather than rely in mouse events, // as mouse isn't grabbed on puppet side and can't handle fast movements that go out of // edit camera mouse area. This also simplifies split view handling. - QPointF diff = m_hiddenCursorPos - e->globalPos(); + QPointF diff = m_isTrusted ? (m_hiddenCursorPos - e->globalPos()) : (m_lastCursorPos - e->globalPos()); + if (e->buttons() == (Qt::LeftButton | Qt::RightButton)) { m_parent->view()->emitView3DAction(View3DActionType::EditCameraMove, QVector3D{float(-diff.x()), float(-diff.y()), 0.f}); @@ -182,13 +211,26 @@ void Edit3DCanvas::mouseMoveEvent(QMouseEvent *e) // Skip first move to avoid undesirable jump occasionally when initiating flight mode m_flyModeFirstUpdate = false; } - QCursor::setPos(m_hiddenCursorPos); + + if (m_isTrusted) + QCursor::setPos(m_hiddenCursorPos); + else + m_lastCursorPos = e->globalPos(); } } void Edit3DCanvas::wheelEvent(QWheelEvent *e) { - m_parent->view()->sendInputEvent(e); + if (m_flyMode) { + // In fly mode, wheel controls the camera speed slider (value range 1-100) + double speed; + double mult; + m_parent->view()->getCameraSpeedAuxData(speed, mult); + speed = qMin(100., qMax(1., speed + double(e->angleDelta().y()) / 40.)); + m_parent->view()->setCameraSpeedAuxData(speed, mult); + } else { + m_parent->view()->sendInputEvent(e); + } QWidget::wheelEvent(e); } diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h index 39207554a7..16c1063dd6 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h @@ -30,6 +30,7 @@ public: bool isFlyMode() const { return m_flyMode; } protected: + bool eventFilter(QObject *obj, QEvent *event) override; void mousePressEvent(QMouseEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override; void mouseDoubleClickEvent(QMouseEvent *e) override; @@ -52,10 +53,12 @@ private: qint32 m_activeScene = -1; QElapsedTimer m_usageTimer; qreal m_opacity = 1.0; + bool m_isTrusted = true; QWidget *m_busyIndicator = nullptr; bool m_flyMode = false; QPoint m_flyModeStartCursorPos; QPoint m_hiddenCursorPos; + QPoint m_lastCursorPos; qint64 m_flyModeStartTime = 0; bool m_flyModeFirstUpdate = false; bool m_contextMenuPending = false; diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index 911ee1fb15..bb7404f252 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -20,9 +20,11 @@ #include "nodeinstanceview.h" #include "qmldesignerconstants.h" #include "qmldesignerplugin.h" +#include "qmlitemnode.h" #include "qmlvisualnode.h" #include "seekerslider.h" #include "snapconfiguration.h" +#include "variantproperty.h" #include <auxiliarydataproperties.h> #include <model/modelutils.h> @@ -133,6 +135,7 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState) const QString orientationKey = QStringLiteral("globalOrientation"); const QString editLightKey = QStringLiteral("showEditLight"); const QString gridKey = QStringLiteral("showGrid"); + const QString showLookAtKey = QStringLiteral("showLookAt"); const QString selectionBoxKey = QStringLiteral("showSelectionBox"); const QString iconGizmoKey = QStringLiteral("showIconGizmo"); const QString cameraFrustumKey = QStringLiteral("showCameraFrustum"); @@ -187,6 +190,11 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState) else m_showGridAction->action()->setChecked(false); + if (sceneState.contains(showLookAtKey)) + m_showLookAtAction->action()->setChecked(sceneState[showLookAtKey].toBool()); + else + m_showLookAtAction->action()->setChecked(false); + if (sceneState.contains(selectionBoxKey)) m_showSelectionBoxAction->action()->setChecked(sceneState[selectionBoxKey].toBool()); else @@ -235,36 +243,10 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState) state.showWireframe = false; } - // Syncing background color only makes sense for children of View3D instances - bool syncValue = false; - bool syncEnabled = false; - bool desiredSyncValue = false; if (sceneState.contains(syncEnvBgKey)) - desiredSyncValue = sceneState[syncEnvBgKey].toBool(); - ModelNode checkNode = Utils3D::active3DSceneNode(this); - const bool activeSceneValid = checkNode.isValid(); - - while (checkNode.isValid()) { - if (checkNode.metaInfo().isQtQuick3DView3D()) { - syncValue = desiredSyncValue; - syncEnabled = true; - break; - } - if (checkNode.hasParentProperty()) - checkNode = checkNode.parentProperty().parentModelNode(); - else - break; - } - - if (activeSceneValid && syncValue != desiredSyncValue) { - // Update actual toolstate as well if we overrode it. - QTimer::singleShot(0, this, [this, syncValue]() { - emitView3DAction(View3DActionType::SyncEnvBackground, syncValue); - }); - } - - m_syncEnvBackgroundAction->action()->setChecked(syncValue); - m_syncEnvBackgroundAction->action()->setEnabled(syncEnabled); + m_syncEnvBackgroundAction->action()->setChecked(sceneState[syncEnvBgKey].toBool()); + else + m_syncEnvBackgroundAction->action()->setChecked(false); // Selection context change updates visible and enabled states SelectionContext selectionContext(this); @@ -273,12 +255,22 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState) m_bakeLightsAction->currentContextChanged(selectionContext); syncCameraSpeedToNewView(); + + storeCurrentSceneEnvironment(); } 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, @@ -342,11 +334,10 @@ void Edit3DView::handleEntriesChanged() {EK_importedModels, {tr("Imported Models"), contextIcon(DesignerIcons::ImportedModelsIcon)}}}; #ifdef QDS_USE_PROJECTSTORAGE - const auto &projectStorage = *model()->projectStorage(); auto append = [&](const NodeMetaInfo &metaInfo, ItemLibraryEntryKeys key) { auto entries = metaInfo.itemLibrariesEntries(); if (entries.size()) - entriesMap[key].entryList.append(toItemLibraryEntries(entries, projectStorage)); + entriesMap[key].entryList.append(toItemLibraryEntries(entries)); }; append(model()->qtQuick3DModelMetaInfo(), EK_primitives); @@ -356,7 +347,12 @@ void Edit3DView::handleEntriesChanged() append(model()->qtQuick3DOrthographicCameraMetaInfo(), EK_cameras); append(model()->qtQuick3DPerspectiveCameraMetaInfo(), EK_cameras); - auto assetsModule = model()->module("Quick3DAssets"); + Utils::PathString import3dTypePrefix = QmlDesignerPlugin::instance() + ->documentManager() + .generatedComponentUtils() + .import3dTypePrefix(); + + auto assetsModule = model()->module(import3dTypePrefix, Storage::ModuleKind::QmlLibrary); for (const auto &metaInfo : model()->metaInfosForModule(assetsModule)) append(metaInfo, EK_importedModels); @@ -373,8 +369,12 @@ void Edit3DView::handleEntriesChanged() } else if (entry.typeName() == "QtQuick3D.OrthographicCamera" || entry.typeName() == "QtQuick3D.PerspectiveCamera") { entryKey = EK_cameras; - } else if (entry.typeName().startsWith("Quick3DAssets.") - && NodeHints::fromItemLibraryEntry(entry).canBeDroppedInView3D()) { + } else if (entry.typeName().startsWith(QmlDesignerPlugin::instance() + ->documentManager() + .generatedComponentUtils() + .import3dTypePrefix() + .toUtf8()) + && NodeHints::fromItemLibraryEntry(entry, model()).canBeDroppedInView3D()) { entryKey = EK_importedModels; } else { continue; @@ -448,6 +448,7 @@ void Edit3DView::customNotification([[maybe_unused]] const AbstractView *view, self->emitView3DAction(View3DActionType::GetNodeAtMainScenePos, QVariantList{data[0], nodeList[0].internalId()}); self->m_nodeAtPosReqType = NodeAtPosReqType::MainScenePick; + self->m_pickView3dNode = nodeList[0]; }); } } @@ -490,7 +491,7 @@ void Edit3DView::nodeAtPosReady(const ModelNode &modelNode, const QVector3D &pos } else if (m_nodeAtPosReqType == NodeAtPosReqType::BundleMaterialDrop) { emitCustomNotification("drop_bundle_material", {modelNode}); // To ContentLibraryView } else if (m_nodeAtPosReqType == NodeAtPosReqType::BundleEffectDrop) { - emitCustomNotification("drop_bundle_effect", {modelNode}, {pos3d}); // To ContentLibraryView + emitCustomNotification("drop_bundle_item", {modelNode}, {pos3d}); // To ContentLibraryView } else if (m_nodeAtPosReqType == NodeAtPosReqType::TextureDrop) { emitCustomNotification("apply_texture_to_model3D", {modelNode, m_droppedModelNode}); } else if (m_nodeAtPosReqType == NodeAtPosReqType::AssetDrop) { @@ -500,6 +501,8 @@ void Edit3DView::nodeAtPosReady(const ModelNode &modelNode, const QVector3D &pos } else if (m_nodeAtPosReqType == NodeAtPosReqType::MainScenePick) { if (modelNode.isValid()) setSelectedModelNode(modelNode); + else if (m_pickView3dNode.isValid() && !m_pickView3dNode.isSelected()) + setSelectedModelNode(m_pickView3dNode); emitView3DAction(View3DActionType::AlignViewToCamera, true); } @@ -523,6 +526,21 @@ void Edit3DView::nodeRemoved(const ModelNode &, updateAlignActionStates(); } +void Edit3DView::propertiesRemoved(const QList<AbstractProperty> &propertyList) +{ + maybeStoreCurrentSceneEnvironment(propertyList); +} + +void Edit3DView::bindingPropertiesChanged(const QList<BindingProperty> &propertyList, PropertyChangeFlags) +{ + maybeStoreCurrentSceneEnvironment(propertyList); +} + +void Edit3DView::variantPropertiesChanged(const QList<VariantProperty> &propertyList, PropertyChangeFlags) +{ + maybeStoreCurrentSceneEnvironment(propertyList); +} + void Edit3DView::sendInputEvent(QEvent *e) const { if (nodeInstanceView()) @@ -699,6 +717,30 @@ QPoint Edit3DView::resolveToolbarPopupPos(Edit3DAction *action) const return pos; } +template<typename T, typename> +void Edit3DView::maybeStoreCurrentSceneEnvironment(const QList<T> &propertyList) +{ + QSet<qint32> handledNodes; + QmlObjectNode sceneEnv; + for (const AbstractProperty &prop : propertyList) { + ModelNode node = prop.parentModelNode(); + const qint32 id = node.internalId(); + if (handledNodes.contains(id)) + continue; + + handledNodes.insert(id); + if (!node.metaInfo().isQtQuick3DSceneEnvironment()) + continue; + + if (!sceneEnv.isValid()) + sceneEnv = currentSceneEnv(); + if (sceneEnv == node) { + storeCurrentSceneEnvironment(); + break; + } + } +} + void Edit3DView::showContextMenu() { // If request for context menu is still pending, skip for now @@ -719,32 +761,6 @@ void Edit3DView::showContextMenu() void Edit3DView::setFlyMode(bool enabled) { emitView3DAction(View3DActionType::FlyModeToggle, enabled); - - // Disable any actions with conflicting hotkeys - if (enabled) { - m_flyModeDisabledActions.clear(); - const QList<QKeySequence> controlKeys = { Qt::Key_W, Qt::Key_A, Qt::Key_S, - Qt::Key_D, Qt::Key_Q, Qt::Key_E, - Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, - Qt::Key_Right, Qt::Key_PageDown, Qt::Key_PageUp}; - for (auto i = m_edit3DActions.cbegin(), end = m_edit3DActions.cend(); i != end; ++i) { - for (const QKeySequence &controlKey : controlKeys) { - if (Core::Command *cmd = m_edit3DWidget->actionToCommandHash().value(i.value()->action())) { - if (cmd->keySequence().matches(controlKey) == QKeySequence::ExactMatch) { - if (i.value()->action()->isEnabled()) { - m_flyModeDisabledActions.append(i.value()); - i.value()->action()->setEnabled(false); - } - break; - } - } - } - } - } else { - for (Edit3DAction *action : std::as_const(m_flyModeDisabledActions)) - action->action()->setEnabled(true); - m_flyModeDisabledActions.clear(); - } } void Edit3DView::syncSnapAuxPropsToSettings() @@ -814,6 +830,75 @@ void Edit3DView::syncCameraSpeedToNewView() setCameraSpeedAuxData(speed, multiplier); } +QmlObjectNode Edit3DView::currentSceneEnv() +{ + PropertyName envProp{"environment"}; + ModelNode checkNode = Utils3D::active3DSceneNode(this); + while (checkNode.isValid()) { + if (checkNode.metaInfo().isQtQuick3DView3D()) { + QmlObjectNode sceneEnvNode = QmlItemNode(checkNode).bindingProperty(envProp) + .resolveToModelNode(); + if (sceneEnvNode.isValid()) + return sceneEnvNode; + break; + } + if (checkNode.hasParentProperty()) + checkNode = checkNode.parentProperty().parentModelNode(); + else + break; + } + return {}; +} + +void Edit3DView::storeCurrentSceneEnvironment() +{ + // If current active scene has scene environment, store relevant properties + QmlObjectNode sceneEnvNode = currentSceneEnv(); + if (sceneEnvNode.isValid()) { + QVariantMap lastSceneEnvData; + + auto insertPropValue = [](const PropertyName prop, const QmlObjectNode &node, + QVariantMap &map) { + if (!node.hasProperty(prop)) + return; + + map.insert(QString::fromUtf8(prop), node.modelValue(prop)); + }; + + auto insertTextureProps = [&](const PropertyName prop) { + // For now we just grab the absolute path of texture source for simplicity + if (!sceneEnvNode.hasProperty(prop)) + return; + + QmlObjectNode bindNode = QmlItemNode(sceneEnvNode).bindingProperty(prop) + .resolveToModelNode(); + if (bindNode.isValid()) { + QVariantMap props; + const PropertyName sourceProp = "source"; + if (bindNode.hasProperty(sourceProp)) { + Utils::FilePath qmlPath = Utils::FilePath::fromUrl( + model()->fileUrl()).absolutePath(); + Utils::FilePath sourcePath = Utils::FilePath::fromUrl( + bindNode.modelValue(sourceProp).toUrl()); + + sourcePath = qmlPath.resolvePath(sourcePath); + + props.insert(QString::fromUtf8(sourceProp), + sourcePath.absoluteFilePath().toUrl()); + } + lastSceneEnvData.insert(QString::fromUtf8(prop), props); + } + }; + + insertPropValue("backgroundMode", sceneEnvNode, lastSceneEnvData); + insertPropValue("clearColor", sceneEnvNode, lastSceneEnvData); + insertTextureProps("lightProbe"); + insertTextureProps("skyBoxCubeMap"); + + emitView3DAction(View3DActionType::SetLastSceneEnvData, lastSceneEnvData); + } +} + const QList<Edit3DView::SplitToolState> &Edit3DView::splitToolStates() const { return m_splitToolStates; @@ -961,6 +1046,18 @@ void Edit3DView::createEdit3DActions() nullptr, QCoreApplication::translate("ShowGridAction", "Toggle the visibility of the helper grid.")); + m_showLookAtAction = std::make_unique<Edit3DAction>( + QmlDesigner::Constants::EDIT3D_EDIT_SHOW_LOOKAT, + View3DActionType::ShowLookAt, + QCoreApplication::translate("ShowLookAtAction", "Show Look-at"), + QKeySequence(Qt::Key_L), + true, + true, + QIcon(), + this, + nullptr, + QCoreApplication::translate("ShowLookAtAction", "Toggle the visibility of the edit camera look-at indicator.")); + m_showSelectionBoxAction = std::make_unique<Edit3DAction>( QmlDesigner::Constants::EDIT3D_EDIT_SHOW_SELECTION_BOX, View3DActionType::ShowSelectionBox, @@ -1264,6 +1361,7 @@ void Edit3DView::createEdit3DActions() m_rightActions << m_resetAction.get(); m_visibilityToggleActions << m_showGridAction.get(); + m_visibilityToggleActions << m_showLookAtAction.get(); m_visibilityToggleActions << m_showSelectionBoxAction.get(); m_visibilityToggleActions << m_showIconGizmoAction.get(); m_visibilityToggleActions << m_showCameraFrustumAction.get(); diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h index 781b26d8d8..755efc0ae3 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h @@ -8,6 +8,7 @@ #include <abstractview.h> #include <modelcache.h> +#include <qmlobjectnode.h> #include <QImage> #include <QPointer> @@ -59,6 +60,11 @@ public: PropertyChangeFlags propertyChange) override; void nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty, PropertyChangeFlags propertyChange) override; + void propertiesRemoved(const QList<AbstractProperty> &propertyList) override; + void bindingPropertiesChanged(const QList<BindingProperty> &propertyList, + PropertyChangeFlags propertyChange) override; + void variantPropertiesChanged(const QList<VariantProperty> &propertyList, + PropertyChangeFlags propertyChange) override; void sendInputEvent(QEvent *e) const; void edit3DViewResized(const QSize &size) const; @@ -127,9 +133,14 @@ private: void createSyncEnvBackgroundAction(); void createSeekerSliderAction(); void syncCameraSpeedToNewView(); + QmlObjectNode currentSceneEnv(); + void storeCurrentSceneEnvironment(); QPoint resolveToolbarPopupPos(Edit3DAction *action) const; + template<typename T, typename = typename std::enable_if<std::is_base_of<AbstractProperty , T>::value>::type> + void maybeStoreCurrentSceneEnvironment(const QList<T> &propertyList); + QPointer<Edit3DWidget> m_edit3DWidget; QVector<Edit3DAction *> m_leftActions; QVector<Edit3DAction *> m_rightActions; @@ -148,6 +159,7 @@ private: std::unique_ptr<Edit3DAction> m_orientationModeAction; std::unique_ptr<Edit3DAction> m_editLightAction; std::unique_ptr<Edit3DAction> m_showGridAction; + std::unique_ptr<Edit3DAction> m_showLookAtAction; std::unique_ptr<Edit3DAction> m_showSelectionBoxAction; std::unique_ptr<Edit3DAction> m_showIconGizmoAction; std::unique_ptr<Edit3DAction> m_showCameraFrustumAction; @@ -187,11 +199,12 @@ private: int m_activeSplit = 0; QList<SplitToolState> m_splitToolStates; - QList<Edit3DAction *> m_flyModeDisabledActions; ModelNode m_contextMenuPendingNode; + ModelNode m_pickView3dNode; double m_previousCameraSpeed = -1.; double m_previousCameraMultiplier = -1.; + QString m_currProjectPath; friend class Edit3DAction; }; diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index 07102ae893..f6bbf8d794 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -2,37 +2,41 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "edit3dwidget.h" -#include "designdocument.h" -#include "designericons.h" + #include "edit3dactions.h" #include "edit3dcanvas.h" #include "edit3dtoolbarmenu.h" #include "edit3dview.h" -#include "externaldependenciesinterface.h" -#include "materialutils.h" -#include "metainfo.h" -#include "modelnodeoperations.h" -#include "nodeabstractproperty.h" -#include "nodehints.h" -#include "qmldesignerconstants.h" -#include "qmldesignerplugin.h" -#include "qmleditormenu.h" -#include "qmlvisualnode.h" -#include "viewmanager.h" -#include <utils3d.h> #include <auxiliarydataproperties.h> #include <designeractionmanager.h> +#include <designdocument.h> +#include <designericons.h> #include <designermcumanager.h> +#include <externaldependenciesinterface.h> +#include <generatedcomponentutils.h> #include <import.h> -#include <model/modelutils.h> +#include <materialutils.h> +#include <metainfo.h> +#include <modelnodeoperations.h> +#include <nodeabstractproperty.h> +#include <nodehints.h> #include <nodeinstanceview.h> +#include <qmldesignerconstants.h> +#include <qmldesignerplugin.h> +#include <qmleditormenu.h> +#include <qmlvisualnode.h> #include <seekerslider.h> +#include <toolbox.h> +#include <viewmanager.h> +#include <utils3d.h> #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/command.h> #include <coreplugin/icore.h> -#include <toolbox.h> + +#include <model/modelutils.h> + #include <utils/asset.h> #include <utils/qtcassert.h> #include <utils/utilsicons.h> @@ -359,6 +363,14 @@ void Edit3DWidget::createContextMenu() resetAction->setToolTip(tr("Reset all shading options for all viewports.")); m_contextMenu->addSeparator(); + + m_addToContentLibAction = m_contextMenu->addAction( + contextIcon(DesignerIcons::CreateIcon), // TODO: placeholder icon + tr("Add to Content Library"), [&] { + view()->emitCustomNotification("add_3d_to_content_lib", {m_contextMenuTarget}); + }); + + m_contextMenu->addSeparator(); } bool Edit3DWidget::isPasteAvailable() const @@ -402,7 +414,7 @@ void Edit3DWidget::showOnboardingLabel() " in the" " <b>Assets</b>" " view."); - text = labelText.arg(Utils::creatorTheme()->color(Utils::Theme::TextColorLink).name()); + text = labelText.arg(Utils::creatorColor(Utils::Theme::TextColorLink).name()); } else { text = tr("3D view is not supported in Qt5 projects."); } @@ -608,14 +620,18 @@ void Edit3DWidget::showBackgroundColorMenu(bool show, const QPoint &pos) void Edit3DWidget::showContextMenu(const QPoint &pos, const ModelNode &modelNode, const QVector3D &pos3d) { + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + m_contextMenuTarget = modelNode; m_contextMenuPos3d = pos3d; const bool isModel = modelNode.metaInfo().isQtQuick3DModel(); + const bool isNode = modelNode.metaInfo().isQtQuick3DNode(); const bool allowAlign = view()->edit3DAction(View3DActionType::AlignCamerasToView)->action()->isEnabled(); const bool isSingleComponent = view()->hasSingleSelectedModelNode() && modelNode.isComponent(); const bool anyNodeSelected = view()->hasSelectedModelNodes(); const bool selectionExcludingRoot = anyNodeSelected && !view()->rootModelNode().isSelected(); + const bool isInBundle = modelNode.type().startsWith(compUtils.componentBundlesTypePrefix().toLatin1()); if (m_createSubMenu) m_createSubMenu->setEnabled(!isSceneLocked()); @@ -633,6 +649,7 @@ void Edit3DWidget::showContextMenu(const QPoint &pos, const ModelNode &modelNode m_toggleGroupAction->setEnabled(true); m_bakeLightsAction->setVisible(view()->bakeLightsAction()->action()->isVisible()); m_bakeLightsAction->setEnabled(view()->bakeLightsAction()->action()->isEnabled()); + m_addToContentLibAction->setEnabled(isNode && !isInBundle); if (m_view) { int idx = m_view->activeSplit(); @@ -685,7 +702,7 @@ void Edit3DWidget::dragEnterEvent(QDragEnterEvent *dragEnterEvent) } else if (actionManager.externalDragHasSupportedAssets(dragEnterEvent->mimeData()) || dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_MATERIAL) || dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_MATERIAL) - || dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_EFFECT) + || dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_ITEM) || dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_TEXTURE)) { if (Utils3D::active3DSceneNode(m_view).isValid()) dragEnterEvent->acceptProposedAction(); @@ -694,7 +711,7 @@ void Edit3DWidget::dragEnterEvent(QDragEnterEvent *dragEnterEvent) if (!data.isEmpty()) { QDataStream stream(data); stream >> m_draggedEntry; - if (NodeHints::fromItemLibraryEntry(m_draggedEntry).canBeDroppedInView3D()) + if (NodeHints::fromItemLibraryEntry(m_draggedEntry, view()->model()).canBeDroppedInView3D()) dragEnterEvent->acceptProposedAction(); } } @@ -730,8 +747,8 @@ void Edit3DWidget::dropEvent(QDropEvent *dropEvent) return; } - // handle dropping bundle effects - if (dropEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_EFFECT)) { + // handle dropping bundle items + if (dropEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_ITEM)) { m_view->dropBundleEffect(pos); m_view->model()->endDrag(); return; @@ -766,9 +783,14 @@ 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()); + Utils::PathString import3dTypePrefix = QmlDesignerPlugin::instance() + ->documentManager() + .generatedComponentUtils() + .import3dTypePrefix(); + auto moduleId = model->module(import3dTypePrefix, Storage::ModuleKind::QmlLibrary); + auto metaInfo = model->metaInfo(moduleId, fileName.toUtf8()); if (auto entries = metaInfo.itemLibrariesEntries(); entries.size()) { - auto entry = ItemLibraryEntry{entries.front(), *model->projectStorage()}; + auto entry = ItemLibraryEntry{entries.front()}; QmlVisualNode::createQml3DNode(view(), entry, m_canvas->activeScene(), {}, false); } } @@ -780,7 +802,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<ItemLibraryEntry> 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/edit3d/edit3dwidget.h b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h index 211b044d41..97c0469668 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h @@ -100,6 +100,7 @@ private: QPointer<QAction> m_selectParentAction; QPointer<QAction> m_toggleGroupAction; QPointer<QAction> m_wireFrameAction; + QPointer<QAction> m_addToContentLibAction; QHash<int, QPointer<QAction>> m_matOverrideActions; QPointer<QMenu> m_createSubMenu; ModelNode m_contextMenuTarget; diff --git a/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp index 26c5fe0eb2..8890ea8964 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 729e6ce7d1..ae401f60f9 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 diff --git a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp index 0b7d199b50..a6494811b6 100644 --- a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp +++ b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp @@ -206,9 +206,16 @@ static ItemLibraryEntry itemLibraryEntryFromMimeData(const QMimeData *mimeData) return itemLibraryEntry; } -static bool canBeDropped(const QMimeData *mimeData) +static bool canBeDropped(const QMimeData *mimeData, Model *model) { - return NodeHints::fromItemLibraryEntry(itemLibraryEntryFromMimeData(mimeData)).canBeDroppedInFormEditor(); +#ifdef QDS_USE_PROJECTSTORAGE + auto itemLibraryEntry = itemLibraryEntryFromMimeData(mimeData); + NodeMetaInfo metaInfo{itemLibraryEntry.typeId(), model->projectStorage()}; + return metaInfo.canBeDroppedInFormEditor() == FlagIs::True; +#else + return NodeHints::fromItemLibraryEntry(itemLibraryEntryFromMimeData(mimeData), model) + .canBeDroppedInFormEditor(); +#endif } static bool hasItemLibraryInfo(const QMimeData *mimeData) @@ -218,7 +225,7 @@ static bool hasItemLibraryInfo(const QMimeData *mimeData) void DragTool::dropEvent(const QList<QGraphicsItem *> &itemList, QGraphicsSceneDragDropEvent *event) { - if (canBeDropped(event->mimeData())) { + if (canBeDropped(event->mimeData(), view()->model())) { event->accept(); end(generateUseSnapping(event->modifiers())); @@ -290,7 +297,7 @@ void DragTool::dropEvent(const QList<QGraphicsItem *> &itemList, QGraphicsSceneD void DragTool::dragEnterEvent(const QList<QGraphicsItem *> &/*itemList*/, QGraphicsSceneDragDropEvent *event) { - if (canBeDropped(event->mimeData())) { + if (canBeDropped(event->mimeData(), view()->model())) { m_blockMove = false; if (hasItemLibraryInfo(event->mimeData())) { @@ -306,7 +313,7 @@ void DragTool::dragEnterEvent(const QList<QGraphicsItem *> &/*itemList*/, QGraph void DragTool::dragLeaveEvent(const QList<QGraphicsItem *> &/*itemList*/, QGraphicsSceneDragDropEvent *event) { - if (canBeDropped(event->mimeData())) { + if (canBeDropped(event->mimeData(), view()->model())) { event->accept(); m_moveManipulator.end(); @@ -363,10 +370,8 @@ void DragTool::dragMoveEvent(const QList<QGraphicsItem *> &itemList, QGraphicsSc ->data(Constants::MIME_TYPE_ASSETS)).split(','); QString assetType = AssetsLibraryWidget::getAssetTypeAndData(assetPaths[0]).first; - if (!m_blockMove - && !m_isAborted - && canBeDropped(event->mimeData()) - && assetType != Constants::MIME_TYPE_ASSET_EFFECT) { + if (!m_blockMove && !m_isAborted && canBeDropped(event->mimeData(), view()->model()) + && assetType != Constants::MIME_TYPE_ASSET_EFFECT) { event->accept(); if (!m_dragNodes.isEmpty()) { if (targetContainerItem) { diff --git a/src/plugins/qmldesigner/components/formeditor/dragtool.h b/src/plugins/qmldesigner/components/formeditor/dragtool.h index 1cd2c9f487..c1d5626d28 100644 --- a/src/plugins/qmldesigner/components/formeditor/dragtool.h +++ b/src/plugins/qmldesigner/components/formeditor/dragtool.h @@ -7,7 +7,6 @@ #include "selectionindicator.h" #include <QObject> -#include <QScopedPointer> #include <QPointer> namespace QmlDesigner { diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorannotationicon.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorannotationicon.cpp index 35a48a6b6d..dd239dd966 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorannotationicon.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditorannotationicon.cpp @@ -302,9 +302,9 @@ QGraphicsItem *FormEditorAnnotationIcon::createCommentBubble(QRectF rect, const const QString &author, const QString &text, const QString &date, QGraphicsItem *parent) { - static QColor textColor = Utils::creatorTheme()->color(Utils::Theme::DStextColor); - static QColor backgroundColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_BackgroundColorDarker); - static QColor frameColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_BackgroundColor); + static QColor textColor = Utils::creatorColor(Utils::Theme::DStextColor); + static QColor backgroundColor = Utils::creatorColor(Utils::Theme::QmlDesigner_BackgroundColorDarker); + static QColor frameColor = Utils::creatorColor(Utils::Theme::QmlDesigner_BackgroundColor); QFont font; font.setBold(true); @@ -405,9 +405,9 @@ QGraphicsItem *FormEditorAnnotationIcon::createCommentBubble(QRectF rect, const QGraphicsItem *FormEditorAnnotationIcon::createTitleBubble(const QRectF &rect, const QString &text, QGraphicsItem *parent) { - static QColor textColor = Utils::creatorTheme()->color(Utils::Theme::DStextColor); - static QColor backgroundColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_BackgroundColorDarker); - static QColor frameColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_BackgroundColor); + static QColor textColor = Utils::creatorColor(Utils::Theme::DStextColor); + static QColor backgroundColor = Utils::creatorColor(Utils::Theme::QmlDesigner_BackgroundColorDarker); + static QColor frameColor = Utils::creatorColor(Utils::Theme::QmlDesigner_BackgroundColor); QFont font; font.setBold(true); diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp index 9549ce9dd4..d835274a97 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp @@ -3,7 +3,6 @@ #include "formeditorgraphicsview.h" #include "backgroundaction.h" -#include "formeditoritem.h" #include "formeditorwidget.h" #include "navigation2d.h" @@ -76,11 +75,11 @@ bool FormEditorGraphicsView::eventFilter(QObject *watched, QEvent *event) auto mouseEvent = static_cast<QMouseEvent *>(event); if (!m_panningStartPosition.isNull()) { horizontalScrollBar()->setValue(horizontalScrollBar()->value() - - (mouseEvent->x() - m_panningStartPosition.x())); + (mouseEvent->position().x() - m_panningStartPosition.x())); verticalScrollBar()->setValue(verticalScrollBar()->value() - - (mouseEvent->y() - m_panningStartPosition.y())); + (mouseEvent->position().y() - m_panningStartPosition.y())); } - m_panningStartPosition = mouseEvent->pos(); + m_panningStartPosition = mouseEvent->position(); event->accept(); return true; } @@ -215,7 +214,7 @@ void FormEditorGraphicsView::drawBackground(QPainter *painter, const QRectF &rec painter->fillRect(rectangle.intersected(rootItemRect()), backgroundBrush()); } - QPen pen(Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_FormEditorSelectionColor)); + QPen pen(Utils::creatorColor(Utils::Theme::QmlDesigner_FormEditorSelectionColor)); pen.setStyle(Qt::DotLine); pen.setWidth(1); diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.h b/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.h index 60e02582cd..e1e61a8f1e 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.h +++ b/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.h @@ -46,7 +46,7 @@ private: void stopPanning(QEvent *event); Panning m_isPanning = Panning::NotStarted; - QPoint m_panningStartPosition; + QPointF m_panningStartPosition; QRectF m_rootItemRect; QImage m_backgroundImage; }; diff --git a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp index 12da85e2c4..b3df6b8fe7 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp @@ -48,23 +48,19 @@ void drawIcon(QPainter *painter, int iconSize, const QColor &penColor) { - static QFontDatabase a; - const QString fontName = "qtds_propertyIconFont.ttf"; - Q_ASSERT(a.hasFamily(fontName)); + QTC_ASSERT(QFontDatabase::hasFamily(fontName), return); - if (a.hasFamily(fontName)) { - QFont font(fontName); - font.setPixelSize(fontSize); + QFont font(fontName); + font.setPixelSize(fontSize); - painter->save(); - painter->setPen(penColor); - painter->setFont(font); - painter->drawText(QRectF(x, y, iconSize, iconSize), iconSymbol); + painter->save(); + painter->setPen(penColor); + painter->setFont(font); + painter->drawText(QRectF(x, y, iconSize, iconSize), iconSymbol); - painter->restore(); - } + painter->restore(); } FormEditorScene *FormEditorItem::scene() const { @@ -309,7 +305,7 @@ void FormEditorItem::paintBoundingRect(QPainter *painter) const pen.setJoinStyle(Qt::MiterJoin); const QColor frameColor(0xaa, 0xaa, 0xaa); - static const QColor selectionColor = Utils::creatorTheme()->color( + static const QColor selectionColor = Utils::creatorColor( Utils::Theme::QmlDesigner_FormEditorSelectionColor); if (scene()->showBoundingRects()) { diff --git a/src/plugins/qmldesigner/components/formeditor/formeditortoolbutton.cpp b/src/plugins/qmldesigner/components/formeditor/formeditortoolbutton.cpp index 555d0d90e3..f3aa96ada4 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditortoolbutton.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditortoolbutton.cpp @@ -42,7 +42,7 @@ void FormEditorToolButton::paint(QPainter *painter, const QStyleOptionGraphicsIt QRectF adjustedRect(size().width() - toolButtonSize, size().height() - toolButtonSize, toolButtonSize, toolButtonSize); painter->setPen(Qt::NoPen); - static QColor selectionColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_FormEditorSelectionColor); + static QColor selectionColor = Utils::creatorColor(Utils::Theme::QmlDesigner_FormEditorSelectionColor); if (m_state == Hovered) painter->setBrush(selectionColor.lighter(110)); diff --git a/src/plugins/qmldesigner/components/formeditor/selectionindicator.cpp b/src/plugins/qmldesigner/components/formeditor/selectionindicator.cpp index 0f5b5f4438..45d26f831e 100644 --- a/src/plugins/qmldesigner/components/formeditor/selectionindicator.cpp +++ b/src/plugins/qmldesigner/components/formeditor/selectionindicator.cpp @@ -78,7 +78,7 @@ void SelectionIndicator::setItems(const QList<FormEditorItem*> &itemList) { clear(); - static QColor selectionColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_FormEditorSelectionColor); + static QColor selectionColor = Utils::creatorColor(Utils::Theme::QmlDesigner_FormEditorSelectionColor); for (FormEditorItem *item : itemList) { if (!item->qmlItemNode().isValid()) @@ -119,7 +119,7 @@ void SelectionIndicator::setItems(const QList<FormEditorItem*> &itemList) m_annotationItem = nullptr; } - static QColor textColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_FormEditorForegroundColor); + static QColor textColor = Utils::creatorColor(Utils::Theme::QmlDesigner_FormEditorForegroundColor); textItem->setDefaultTextColor(textColor); QPolygonF labelPolygon = boundingRectInLayerItemSpaceForItem(selectedItem, m_layerItem.data()); diff --git a/src/plugins/qmldesigner/components/integration/designdocument.cpp b/src/plugins/qmldesigner/components/integration/designdocument.cpp index c0bebbb82b..aa2dfd3b28 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<ModelResourceManagement>())) #else : m_documentModel( @@ -149,7 +150,10 @@ bool DesignDocument::loadInFileComponent(const ModelNode &componentNode) if (!componentNode.isRootNode()) { //change to subcomponent model - changeToInFileComponentModel(createComponentTextModifier(m_documentTextModifier.data(), rewriterView(), componentText, componentNode)); + changeToInFileComponentModel(createComponentTextModifier(m_documentTextModifier.get(), + rewriterView(), + componentText, + componentNode)); } return true; @@ -280,11 +284,10 @@ void DesignDocument::moveNodesToPosition(const QList<ModelNode> &nodes, const st parentProperty.reparentHere(pastedNode); QmlVisualNode visualNode(pastedNode); - if (!firstVisualNode.has_value() && visualNode.isValid()){ + if (!firstVisualNode && visualNode) { firstVisualNode = visualNode; - translationVect = (position.has_value() && firstVisualNode.has_value()) - ? position.value() - firstVisualNode->position() - : QVector3D(); + translationVect = (position && firstVisualNode) ? *position - firstVisualNode->position() + : QVector3D(); } visualNode.translate(translationVect); } @@ -376,9 +379,12 @@ void DesignDocument::loadDocument(QPlainTextEdit *edit) m_documentTextModifier.reset(new BaseTextEditModifier(qobject_cast<TextEditor::TextEditorWidget *>(plainTextEdit()))); - connect(m_documentTextModifier.data(), &TextModifier::textChanged, this, &DesignDocument::updateQrcFiles); + connect(m_documentTextModifier.get(), + &TextModifier::textChanged, + this, + &DesignDocument::updateQrcFiles); - m_rewriterView->setTextModifier(m_documentTextModifier.data()); + m_rewriterView->setTextModifier(m_documentTextModifier.get()); m_inFileComponentTextModifier.reset(); @@ -398,7 +404,7 @@ void DesignDocument::changeToDocumentModel() if (edit) edit->document()->clearUndoRedoStacks(); - m_rewriterView->setTextModifier(m_documentTextModifier.data()); + m_rewriterView->setTextModifier(m_documentTextModifier.get()); m_inFileComponentModel.reset(); m_inFileComponentTextModifier.reset(); @@ -431,7 +437,7 @@ bool DesignDocument::hasProject() const void DesignDocument::setModified() { - if (!m_documentTextModifier.isNull()) + if (m_documentTextModifier) m_documentTextModifier->textDocument()->setModified(true); } @@ -447,7 +453,7 @@ void DesignDocument::changeToInFileComponentModel(ComponentTextModifier *textMod m_inFileComponentModel = createInFileComponentModel(); - m_rewriterView->setTextModifier(m_inFileComponentTextModifier.data()); + m_rewriterView->setTextModifier(m_inFileComponentTextModifier.get()); viewManager().attachRewriterView(); viewManager().attachViewsExceptRewriterAndComponetView(); @@ -674,7 +680,7 @@ void DesignDocument::selectAll() RewriterView *DesignDocument::rewriterView() const { - return m_rewriterView.data(); + return m_rewriterView.get(); } void DesignDocument::setEditor(Core::IEditor *editor) diff --git a/src/plugins/qmldesigner/components/integration/designdocument.h b/src/plugins/qmldesigner/components/integration/designdocument.h index 0d75141205..1f67ff4b30 100644 --- a/src/plugins/qmldesigner/components/integration/designdocument.h +++ b/src/plugins/qmldesigner/components/integration/designdocument.h @@ -16,9 +16,10 @@ #include <QObject> #include <QString> - #include <QStackedWidget> +#include <memory> + QT_BEGIN_NAMESPACE class QPlainTextEdit; QT_END_NAMESPACE @@ -41,7 +42,8 @@ class QMLDESIGNERCOMPONENTS_EXPORT DesignDocument : public QObject Q_OBJECT public: - DesignDocument(ProjectStorageDependencies projectStorageDependencies, + DesignDocument(const QUrl &filePath, + ProjectStorageDependencies projectStorageDependencies, ExternalDependenciesInterface &externalDependencies); ~DesignDocument() override; @@ -142,12 +144,12 @@ private: // variables ModelPointer m_documentModel; ModelPointer m_inFileComponentModel; QPointer<Core::IEditor> m_textEditor; - QScopedPointer<BaseTextEditModifier> m_documentTextModifier; - QScopedPointer<ComponentTextModifier> m_inFileComponentTextModifier; + std::unique_ptr<BaseTextEditModifier> m_documentTextModifier; + std::unique_ptr<ComponentTextModifier> m_inFileComponentTextModifier; #ifndef QDS_USE_PROJECTSTORAGE - QScopedPointer<SubComponentManager> m_subComponentManager; + std::unique_ptr<SubComponentManager> m_subComponentManager; #endif - QScopedPointer<RewriterView> m_rewriterView; + std::unique_ptr<RewriterView> m_rewriterView; bool m_documentLoaded; ProjectExplorer::Target *m_currentTarget; ProjectStorageDependencies m_projectStorageDependencies; diff --git a/src/plugins/qmldesigner/components/integration/designdocumentview.cpp b/src/plugins/qmldesigner/components/integration/designdocumentview.cpp index 6ef95bf4c4..d97b9ff06f 100644 --- a/src/plugins/qmldesigner/components/integration/designdocumentview.cpp +++ b/src/plugins/qmldesigner/components/integration/designdocumentview.cpp @@ -23,6 +23,8 @@ #include <utils/algorithm.h> #include <utils/qtcassert.h> +#include <memory> + namespace QmlDesigner { DesignDocumentView::DesignDocumentView(ExternalDependenciesInterface &externalDependencies) @@ -116,14 +118,14 @@ QString DesignDocumentView::toText() const textEdit.setPlainText(imports + QStringLiteral("Item {\n}\n")); NotIndentingTextEditModifier modifier(&textEdit); - QScopedPointer<RewriterView> rewriterView( - new RewriterView(externalDependencies(), RewriterView::Amend)); + std::unique_ptr<RewriterView> rewriterView = std::make_unique<RewriterView>(externalDependencies(), + RewriterView::Amend); rewriterView->setCheckSemanticErrors(false); rewriterView->setPossibleImportsEnabled(false); rewriterView->setTextModifier(&modifier); - outputModel->setRewriterView(rewriterView.data()); + outputModel->setRewriterView(rewriterView.get()); - ModelMerger merger(rewriterView.data()); + ModelMerger merger(rewriterView.get()); merger.replaceModel(rootModelNode()); diff --git a/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.cpp b/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.cpp new file mode 100644 index 0000000000..608bf42eb3 --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.cpp @@ -0,0 +1,82 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "import3dcanvas.h" + +#include <QImage> +#include <QLinearGradient> +#include <QMouseEvent> +#include <QPainter> + +namespace QmlDesigner { + +static QImage createGradientImage(int width, int height) { + QImage image(width, height, QImage::Format_ARGB32_Premultiplied); + + QLinearGradient gradient(0, 0, 0, height); + gradient.setColorAt(0, QColor(0x999999)); + gradient.setColorAt(1, QColor(0x222222)); + + QPainter painter(&image); + painter.fillRect(0, 0, width, height, gradient); + + return image; +} + +Import3dCanvas::Import3dCanvas(QWidget *parent) + : QWidget(parent) +{ +} + +void Import3dCanvas::updateRenderImage(const QImage &img) +{ + m_image = img; + update(); +} + +void Import3dCanvas::paintEvent([[maybe_unused]] QPaintEvent *e) +{ + QWidget::paintEvent(e); + + QPainter painter(this); + + if (m_image.isNull()) { + QImage image = createGradientImage(width(), height()); + painter.drawImage(rect(), image, QRect(0, 0, image.width(), image.height())); + } else { + painter.drawImage(rect(), m_image, QRect(0, 0, m_image.width(), m_image.height())); + } +} + +void Import3dCanvas::resizeEvent(QResizeEvent *) +{ + emit requestImageUpdate(); +} + +void Import3dCanvas::mousePressEvent(QMouseEvent *e) +{ + if (e->buttons() == Qt::LeftButton) + m_dragPos = e->position(); + else + m_dragPos = {}; +} + +void Import3dCanvas::mouseReleaseEvent(QMouseEvent *) +{ + m_dragPos = {}; +} + +void Import3dCanvas::mouseMoveEvent(QMouseEvent *e) +{ + if (m_dragPos.isNull()) + return; + + const QPointF curPos = e->position(); + const QPointF delta = curPos - m_dragPos; + + m_dragPos = curPos; + + emit requestRotation(delta); +} + +} diff --git a/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.h b/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.h new file mode 100644 index 0000000000..72fb19acff --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.h @@ -0,0 +1,37 @@ +// 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 <QEvent> +#include <QImage> +#include <QPointF> +#include <QWidget> + +namespace QmlDesigner { + +class Import3dCanvas : public QWidget +{ + Q_OBJECT + +public: + Import3dCanvas(QWidget *parent); + + void updateRenderImage(const QImage &img); + +signals: + void requestImageUpdate(); + void requestRotation(const QPointF &delta); + +protected: + void paintEvent(QPaintEvent *e) override; + void resizeEvent(QResizeEvent *e) override; + void mousePressEvent(QMouseEvent *e) override; + void mouseReleaseEvent(QMouseEvent *e) override; + void mouseMoveEvent(QMouseEvent *e) override; + +private: + QImage m_image; + QPointF m_dragPos; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.cpp b/src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.cpp new file mode 100644 index 0000000000..4c455e3c6d --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.cpp @@ -0,0 +1,47 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "import3dconnectionmanager.h" + +#include <imagecontainer.h> +#include <puppettocreatorcommand.h> + +#include <QImage> + +namespace QmlDesigner { + +Import3dConnectionManager::Import3dConnectionManager() +{ + connections().clear(); // Remove default interactive puppets + connections().emplace_back("Import 3D", "import3dmode"); +} + +void Import3dConnectionManager::setPreviewImageCallback(ImageCallback callback) +{ + m_previewImageCallback = std::move(callback); +} + +void Import3dConnectionManager::dispatchCommand(const QVariant &command, + ConnectionManagerInterface::Connection &connection) +{ + static const int commandType = QMetaType::type("PuppetToCreatorCommand"); + + if (command.typeId() == commandType) { + auto cmd = command.value<PuppetToCreatorCommand>(); + switch (cmd.type()) { + case PuppetToCreatorCommand::Import3DPreviewImage: { + ImageContainer container = qvariant_cast<ImageContainer>(cmd.data()); + QImage image = container.image(); + if (!image.isNull()) + m_previewImageCallback(image); + break; + } + default: + break; + } + } else { + InteractiveConnectionManager::dispatchCommand(command, connection); + } +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.h b/src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.h new file mode 100644 index 0000000000..458d612ad2 --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.h @@ -0,0 +1,28 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "interactiveconnectionmanager.h" + +QT_FORWARD_DECLARE_CLASS(QImage) + +namespace QmlDesigner { + +class Import3dConnectionManager : public InteractiveConnectionManager +{ +public: + using ImageCallback = std::function<void(const QImage &)>; + + Import3dConnectionManager(); + + void setPreviewImageCallback(ImageCallback callback); + +protected: + void dispatchCommand(const QVariant &command, Connection &connection) override; + +private: + ImageCallback m_previewImageCallback; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp index 29fff4b359..271410233b 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp @@ -4,11 +4,16 @@ #include "itemlibraryassetimportdialog.h" #include "ui_itemlibraryassetimportdialog.h" +#include "import3dcanvas.h" +#include "import3dconnectionmanager.h" + #include <model.h> #include <model/modelutils.h> +#include <nodeinstanceview.h> #include <nodemetainfo.h> #include <qmldesignerconstants.h> #include <qmldesignerplugin.h> +#include <rewriterview.h> #include <variantproperty.h> #include <theme.h> @@ -73,9 +78,10 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog( const QStringList &importFiles, const QString &defaulTargetDirectory, const QVariantMap &supportedExts, const QVariantMap &supportedOpts, const QJsonObject &defaultOpts, const QSet<QString> &preselectedFilesForOverwrite, - QWidget *parent) + AbstractView *view, QWidget *parent) : QDialog(parent) , ui(new Ui::ItemLibraryAssetImportDialog) + , m_view(view) , m_importer(this) , m_preselectedFilesForOverwrite(preselectedFilesForOverwrite) { @@ -106,17 +112,15 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog( if (m_quick3DFiles.size() != importFiles.size()) addWarning("Cannot import 3D and other assets simultaneously. Skipping non-3D assets."); - ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Import")); - connect(ui->buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, - this, &ItemLibraryAssetImportDialog::onImport); + connect(ui->importButton, &QPushButton::clicked, this, &ItemLibraryAssetImportDialog::onImport); - ui->buttonBox->button(QDialogButtonBox::Ok)->setDefault(true); + ui->importButton->setDefault(true); ui->advancedSettingsButton->setStyleSheet( "QPushButton#advancedSettingsButton {background-color: transparent}"); ui->advancedSettingsButton->setStyleSheet( QString("QPushButton { border: none; color :%1 }").arg( - Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_HighlightColor).name())); + Utils::creatorColor(Utils::Theme::QmlDesigner_HighlightColor).name())); QStringList importPaths; auto doc = QmlDesignerPlugin::instance()->currentDesignDocument(); @@ -130,43 +134,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<QJsonObject> groups; @@ -244,10 +213,14 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog( ui->tabWidget->setCurrentIndex(0); } - connect(ui->buttonBox->button(QDialogButtonBox::Close), &QPushButton::clicked, + connect(ui->closeButton, &QPushButton::clicked, this, &ItemLibraryAssetImportDialog::onClose); connect(ui->tabWidget, &QTabWidget::currentChanged, this, &ItemLibraryAssetImportDialog::updateUi); + connect(canvas(), &Import3dCanvas::requestImageUpdate, + this, &ItemLibraryAssetImportDialog::onRequestImageUpdate); + connect(canvas(), &Import3dCanvas::requestRotation, + this, &ItemLibraryAssetImportDialog::onRequestRotation); connect(&m_importer, &ItemLibraryAssetImporter::errorReported, this, &ItemLibraryAssetImportDialog::addError); @@ -261,23 +234,35 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog( this, &ItemLibraryAssetImportDialog::onImportFinished); connect(&m_importer, &ItemLibraryAssetImporter::progressChanged, this, &ItemLibraryAssetImportDialog::setImportProgress); - - addInfo(tr("Select import options and press \"Import\" to import the following files:")); - for (const auto &file : std::as_const(m_quick3DFiles)) - addInfo(file); + connect(&m_importer, &ItemLibraryAssetImporter::importReadyForPreview, + this, &ItemLibraryAssetImportDialog::onImportReadyForPreview); connect(ui->advancedSettingsButton, &QPushButton::clicked, this, &ItemLibraryAssetImportDialog::toggleAdvanced); QTimer::singleShot(0, this, &ItemLibraryAssetImportDialog::updateUi); + + if (m_quick3DFiles.size() != 1) { + addInfo(tr("Select import options and press \"Import\" to import the following files:")); + } else { + addInfo(tr("Importing:")); + QTimer::singleShot(0, this, &ItemLibraryAssetImportDialog::onImport); + } + + for (const auto &file : std::as_const(m_quick3DFiles)) + addInfo(file); + + updateImportButtonState(); } ItemLibraryAssetImportDialog::~ItemLibraryAssetImportDialog() { + cleanupPreviewPuppet(); delete ui; } -void ItemLibraryAssetImportDialog::updateImport(const ModelNode &updateNode, +void ItemLibraryAssetImportDialog::updateImport(AbstractView *view, + const ModelNode &updateNode, const QVariantMap &supportedExts, const QVariantMap &supportedOpts) { @@ -294,11 +279,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(); @@ -363,7 +351,8 @@ void ItemLibraryAssetImportDialog::updateImport(const ModelNode &updateNode, {sourceInfo.absoluteFilePath()}, node.model()->fileUrl().toLocalFile(), supportedExts, supportedOpts, options, - preselectedFiles, Core::ICore::dialogParent()); + preselectedFiles, view, + Core::ICore::dialogParent()); importDlg->show(); } else { @@ -512,6 +501,7 @@ QGridLayout *ItemLibraryAssetImportDialog::createOptionsGrid( QJsonValue value(optCheck->isChecked()); optObj.insert("value", value); m_importOptions[optionsIndex].insert(optKey, optObj); + updateImportButtonState(); }); } else { // Simple options also exist in advanced, so don't connect simple controls directly @@ -519,13 +509,17 @@ QGridLayout *ItemLibraryAssetImportDialog::createOptionsGrid( auto *advCheck = qobject_cast<QCheckBox *>( m_labelToControlWidgetMaps[optionsIndex].value(optKey)); if (advCheck) { - QObject::connect(optCheck, &QCheckBox::toggled, this, [optCheck, advCheck]() { - if (advCheck->isChecked() != optCheck->isChecked()) + QObject::connect(optCheck, &QCheckBox::toggled, this, [this, optCheck, advCheck]() { + if (advCheck->isChecked() != optCheck->isChecked()) { advCheck->setChecked(optCheck->isChecked()); + updateImportButtonState(); + } }); - QObject::connect(advCheck, &QCheckBox::toggled, this, [optCheck, advCheck]() { - if (advCheck->isChecked() != optCheck->isChecked()) + QObject::connect(advCheck, &QCheckBox::toggled, this, [this, optCheck, advCheck]() { + if (advCheck->isChecked() != optCheck->isChecked()) { optCheck->setChecked(advCheck->isChecked()); + updateImportButtonState(); + } }); } } @@ -561,6 +555,7 @@ QGridLayout *ItemLibraryAssetImportDialog::createOptionsGrid( QJsonValue value(optSpin->value()); optObj.insert("value", value); m_importOptions[optionsIndex].insert(optKey, optObj); + updateImportButtonState(); }); } else { auto *advSpin = qobject_cast<QDoubleSpinBox *>( @@ -568,14 +563,18 @@ QGridLayout *ItemLibraryAssetImportDialog::createOptionsGrid( if (advSpin) { // Connect corresponding advanced control QObject::connect(optSpin, &QDoubleSpinBox::valueChanged, - this, [optSpin, advSpin] { - if (advSpin->value() != optSpin->value()) + this, [this, optSpin, advSpin] { + if (advSpin->value() != optSpin->value()) { advSpin->setValue(optSpin->value()); + updateImportButtonState(); + } }); QObject::connect(advSpin, &QDoubleSpinBox::valueChanged, - this, [optSpin, advSpin] { - if (advSpin->value() != optSpin->value()) + this, [this, optSpin, advSpin] { + if (advSpin->value() != optSpin->value()) { optSpin->setValue(advSpin->value()); + updateImportButtonState(); + } }); } } @@ -733,8 +732,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(); } } @@ -858,6 +859,145 @@ bool ItemLibraryAssetImportDialog::isHiddenOption(const QString &id) return hiddenOptions.contains(id); } +void ItemLibraryAssetImportDialog::startPreview() +{ + cleanupPreviewPuppet(); + + // Preview is done via custom QML file added into the temporary folder of the preview + QString previewQml = +R"( +import QtQuick +import QtQuick3D + +Rectangle { + id: root + width: %1 + height: %2 + + property alias sceneNode: sceneNode + property alias view3d: view3d + property string extents + property string sceneModelName: "%3" + + gradient: Gradient { + GradientStop { position: 1.0; color: "#222222" } + GradientStop { position: 0.0; color: "#999999" } + } + + View3D { + id: view3d + anchors.fill: parent + camera: viewCamera + + environment: SceneEnvironment { + antialiasingMode: SceneEnvironment.MSAA + antialiasingQuality: SceneEnvironment.VeryHigh + } + + PerspectiveCamera { + id: viewCamera + x: 600 + y: 600 + z: 600 + eulerRotation.x: -45 + eulerRotation.y: -45 + clipFar: 100000 + clipNear: 10 + } + + DirectionalLight { + rotation: viewCamera.rotation + } + + Node { + id: sceneNode + } + } + + Text { + anchors.bottom: parent.bottom + anchors.left: parent.left + color: "white" + text: root.extents + font.pixelSize: 14 + } +} +)"; + + QSize size = canvas()->size(); + previewQml = previewQml.arg(size.width()).arg(size.height()).arg(m_previewCompName); + + m_previewFile.writeFileContents(previewQml.toUtf8()); + + if (!m_previewFile.exists()) { + addWarning("Failed to write preview file."); + return; + } + + m_connectionManager = new Import3dConnectionManager; + m_rewriterView = new RewriterView{m_view->externalDependencies(), RewriterView::Amend}; + m_nodeInstanceView = new NodeInstanceView{*m_connectionManager, m_view->externalDependencies()}; + +#ifdef QDS_USE_PROJECTSTORAGE + m_model = m_view->model()->createModel("Item"); +#else + m_model = QmlDesigner::Model::create("QtQuick/Item", 2, 1); + m_model->setFileUrl(m_previewFile.toUrl()); +#endif + + auto textDocument = std::make_unique<QTextDocument>(previewQml); + auto modifier = std::make_unique<NotIndentingTextEditModifier>(textDocument.get(), + QTextCursor{textDocument.get()}); + m_rewriterView->setTextModifier(modifier.get()); + m_model->setRewriterView(m_rewriterView); + + if (!m_rewriterView->errors().isEmpty()) { + addWarning("Preview scene creation failed."); + cleanupPreviewPuppet(); + return; + } + + m_nodeInstanceView->setTarget(m_view->nodeInstanceView()->target()); + + auto previewImageCallback = [this](const QImage &image) { + canvas()->updateRenderImage(image); + }; + + auto crashCallback = [&] { + addWarning("Preview process crashed."); + cleanupPreviewPuppet(); + }; + + m_connectionManager->setPreviewImageCallback(std::move(previewImageCallback)); + m_nodeInstanceView->setCrashCallback(std::move(crashCallback)); + + m_model->setNodeInstanceView(m_nodeInstanceView); +} + +void ItemLibraryAssetImportDialog::cleanupPreviewPuppet() +{ + if (m_model) { + m_model->setNodeInstanceView({}); + m_model->setRewriterView({}); + m_model.reset(); + } + + if (m_nodeInstanceView) + m_nodeInstanceView->setCrashCallback({}); + + if (m_connectionManager) + m_connectionManager->setPreviewImageCallback({}); + + delete m_rewriterView; + delete m_nodeInstanceView; + delete m_connectionManager; +} + +Import3dCanvas *ItemLibraryAssetImportDialog::canvas() +{ + return ui->import3dcanvas; +} + void ItemLibraryAssetImportDialog::resizeEvent(QResizeEvent *event) { m_dialogHeight = event->size().height(); @@ -866,8 +1006,13 @@ void ItemLibraryAssetImportDialog::resizeEvent(QResizeEvent *event) void ItemLibraryAssetImportDialog::setCloseButtonState(bool importing) { - ui->buttonBox->button(QDialogButtonBox::Close)->setEnabled(true); - ui->buttonBox->button(QDialogButtonBox::Close)->setText(importing ? tr("Cancel") : tr("Close")); + ui->closeButton->setEnabled(true); + ui->closeButton->setText(importing ? tr("Cancel") : tr("Close")); +} + +void ItemLibraryAssetImportDialog::updateImportButtonState() +{ + ui->importButton->setText(m_previewOptions == m_importOptions ? tr("Accept") : tr("Import")); } void ItemLibraryAssetImportDialog::addError(const QString &error, const QString &srcPath) @@ -889,14 +1034,25 @@ void ItemLibraryAssetImportDialog::addInfo(const QString &info, const QString &s void ItemLibraryAssetImportDialog::onImport() { - ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + ui->importButton->setEnabled(false); + + if (!m_previewCompName.isEmpty() && m_previewOptions == m_importOptions) { + cleanupPreviewPuppet(); + m_importer.finalizeQuick3DImport(); + return; + } + setCloseButtonState(true); ui->progressBar->setValue(0); if (!m_quick3DFiles.isEmpty()) { - m_importer.importQuick3D(m_quick3DFiles, m_quick3DImportPath, - m_importOptions, m_extToImportOptionsMap, - m_preselectedFilesForOverwrite); + if (!m_previewCompName.isEmpty()) { + m_importer.reImportQuick3D(m_previewCompName, m_importOptions); + } else { + m_importer.importQuick3D(m_quick3DFiles, m_quick3DImportPath, + m_importOptions, m_extToImportOptionsMap, + m_preselectedFilesForOverwrite); + } } } @@ -910,10 +1066,37 @@ void ItemLibraryAssetImportDialog::setImportProgress(int value, const QString &t ui->progressBar->setValue(value); } +void ItemLibraryAssetImportDialog::onImportReadyForPreview(const QString &path, const QString &compName) +{ + addInfo(tr("Import is ready for preview.")); + if (m_previewCompName.isEmpty()) + addInfo(tr("Click \"Accept\" to finish the import or adjust options and click \"Import\" to import again.")); + + m_previewFile = Utils::FilePath::fromString(path).pathAppended(m_importer.previewFileName()); + m_previewCompName = compName; + m_previewOptions = m_importOptions; + QTimer::singleShot(0, this, &ItemLibraryAssetImportDialog::startPreview); + + ui->importButton->setEnabled(true); + updateImportButtonState(); +} + +void ItemLibraryAssetImportDialog::onRequestImageUpdate() +{ + if (m_nodeInstanceView) + m_nodeInstanceView->view3DAction(View3DActionType::Import3dUpdatePreviewImage, canvas()->size()); +} + +void ItemLibraryAssetImportDialog::onRequestRotation(const QPointF &delta) +{ + if (m_nodeInstanceView) + m_nodeInstanceView->view3DAction(View3DActionType::Import3dRotatePreviewModel, delta); +} + void ItemLibraryAssetImportDialog::onImportNearlyFinished() { // Canceling import is no longer doable - ui->buttonBox->button(QDialogButtonBox::Close)->setEnabled(false); + ui->closeButton->setEnabled(false); } void ItemLibraryAssetImportDialog::onImportFinished() @@ -923,19 +1106,28 @@ void ItemLibraryAssetImportDialog::onImportFinished() QString interruptStr = tr("Import interrupted."); addError(interruptStr); setImportProgress(0, interruptStr); + if (m_explicitClose) + QTimer::singleShot(1000, this, &ItemLibraryAssetImportDialog::doClose); } else { QString doneStr = tr("Import done."); addInfo(doneStr); setImportProgress(100, doneStr); if (m_closeOnFinish) { // Add small delay to allow user to visually confirm import finishing - QTimer::singleShot(1000, this, &ItemLibraryAssetImportDialog::onClose); + QTimer::singleShot(1000, this, &ItemLibraryAssetImportDialog::doClose); } } } void ItemLibraryAssetImportDialog::onClose() { + ui->importButton->setEnabled(false); + m_explicitClose = true; + doClose(); +} + +void ItemLibraryAssetImportDialog::doClose() +{ if (m_importer.isImporting()) { addInfo(tr("Canceling import.")); m_importer.cancelImport(); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h index c5da478232..e7c4933056 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h @@ -3,14 +3,19 @@ #pragma once #include "itemlibraryassetimporter.h" -#include "modelnode.h" + +#include <modelnode.h> + +#include <utils/filepath.h> #include <QDialog> #include <QJsonObject> +#include <QPointer> #include <QSet> QT_BEGIN_NAMESPACE class QGridLayout; +class QPushButton; QT_END_NAMESPACE namespace Utils { @@ -19,6 +24,10 @@ class OutputFormatter; namespace QmlDesigner { class ItemLibraryAssetImporter; +class Import3dCanvas; +class Import3dConnectionManager; +class NodeInstanceView; +class RewriterView; namespace Ui { class ItemLibraryAssetImportDialog; @@ -35,10 +44,12 @@ public: const QVariantMap &supportedOpts, const QJsonObject &defaultOpts, const QSet<QString> &preselectedFilesForOverwrite, + AbstractView *view, QWidget *parent = nullptr); ~ItemLibraryAssetImportDialog(); - static void updateImport(const ModelNode &updateNode, + static void updateImport(AbstractView *view, + const ModelNode &updateNode, const QVariantMap &supportedExts, const QVariantMap &supportedOpts); @@ -52,12 +63,17 @@ private slots: private: void setCloseButtonState(bool importing); + void updateImportButtonState(); void onImport(); void setImportProgress(int value, const QString &text); + void onImportReadyForPreview(const QString &path, const QString &compName); + void onRequestImageUpdate(); + void onRequestRotation(const QPointF &delta); void onImportNearlyFinished(); void onImportFinished(); void onClose(); + void doClose(); void toggleAdvanced(); void createTab(const QString &tabLabel, int optionsIndex, const QJsonObject &groups); @@ -69,8 +85,19 @@ private: bool isSimpleOption(const QString &id); bool isHiddenOption(const QString &id); + void startPreview(); + void cleanupPreviewPuppet(); + Import3dCanvas *canvas(); + Ui::ItemLibraryAssetImportDialog *ui = nullptr; Utils::OutputFormatter *m_outputFormatter = nullptr; + QPointer<Import3dConnectionManager> m_connectionManager; + QPointer<NodeInstanceView> m_nodeInstanceView; + QPointer<RewriterView> m_rewriterView; + QPointer<AbstractView> m_view; + ModelPointer m_model; + Utils::FilePath m_previewFile; + QString m_previewCompName; struct OptionsData { @@ -83,6 +110,7 @@ private: QString m_quick3DImportPath; ItemLibraryAssetImporter m_importer; QVector<QJsonObject> m_importOptions; + QVector<QJsonObject> m_previewOptions; QHash<QString, int> m_extToImportOptionsMap; QSet<QString> m_preselectedFilesForOverwrite; bool m_closeOnFinish = true; @@ -91,5 +119,6 @@ private: OptionsData m_advancedData; bool m_advancedMode = false; int m_dialogHeight = 350; + bool m_explicitClose = false; }; } diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.ui b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.ui index 081bc36a3d..e0b9d925fc 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.ui +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.ui @@ -6,15 +6,15 @@ <rect> <x>0</x> <y>0</y> - <width>630</width> + <width>1100</width> <height>350</height> </rect> </property> <property name="windowTitle"> <string>Asset Import</string> </property> - <layout class="QFormLayout" name="formLayout"> - <item row="0" column="0" colspan="2"> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> <layout class="QVBoxLayout" name="verticalLayout"> <item> <widget class="QTabWidget" name="tabWidget"> @@ -24,6 +24,12 @@ <verstretch>2</verstretch> </sizepolicy> </property> + <property name="minimumSize"> + <size> + <width>550</width> + <height>0</height> + </size> + </property> <property name="currentIndex"> <number>0</number> </property> @@ -98,6 +104,12 @@ <verstretch>0</verstretch> </sizepolicy> </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> <property name="text"> <string/> </property> @@ -111,16 +123,64 @@ </widget> </item> <item> - <widget class="QDialogButtonBox" name="buttonBox"> - <property name="standardButtons"> - <set>QDialogButtonBox::Close|QDialogButtonBox::Ok</set> - </property> - </widget> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <spacer name="horizontalSpacer_3"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="closeButton"> + <property name="text"> + <string>Close</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="importButton"> + <property name="text"> + <string>Import</string> + </property> + </widget> + </item> + </layout> </item> </layout> </item> + <item> + <widget class="Import3dCanvas" name="import3dcanvas" native="true"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>300</width> + <height>300</height> + </size> + </property> + </widget> + </item> </layout> </widget> + <customwidgets> + <customwidget> + <class>Import3dCanvas</class> + <extends>QWidget</extends> + <header>import3dcanvas.h</header> + <container>1</container> + </customwidget> + </customwidgets> <resources/> <connections/> </ui> diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp index 48958ceec9..010d00a970 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" @@ -20,6 +21,7 @@ #include <utils/algorithm.h> #include <utils/async.h> +#include <utils/filepath.h> #include <utils/qtcassert.h> #include <QApplication> @@ -57,8 +59,6 @@ void ItemLibraryAssetImporter::importQuick3D(const QStringList &inputFiles, const QHash<QString, int> &extToImportOptionsMap, const QSet<QString> &preselectedFilesForOverwrite) { - if (m_isImporting) - cancelImport(); reset(); m_isImporting = true; @@ -91,6 +91,53 @@ void ItemLibraryAssetImporter::importQuick3D(const QStringList &inputFiles, } } +void ItemLibraryAssetImporter::reImportQuick3D(const QString &assetName, + const QVector<QJsonObject> &options) +{ + if (!assetName.isEmpty() && !m_parseData.contains(assetName)) { + addError(tr("Attempted to reimport non-existing asset: %1").arg(assetName)); + return; + } + + ParseData &pd = m_parseData[assetName]; + // Change outDir just in case reimport generates different files + QDir oldDir = pd.outDir; + QString assetFolder = generateAssetFolderName(pd.assetName); + pd.outDir.cdUp(); + pd.outDir.mkpath(assetFolder); + + if (!pd.outDir.cd(assetFolder)) { + addError(tr("Could not access temporary asset directory: \"%1\".") + .arg(pd.outDir.filePath(assetFolder))); + return; + } + + if (oldDir.absolutePath().contains(tempDirNameBase())) + oldDir.removeRecursively(); + + m_isImporting = false; + m_cancelled = false; + + m_puppetProcess.reset(); + m_requiredImports.clear(); + m_currentImportId = 0; + m_puppetQueue.clear(); + + for (ParseData &pd : m_parseData) + pd.importId = -1; + + pd.options = options[pd.optionsIndex]; + pd.importId = 1; + + m_importFiles.remove(assetName); + + m_importIdToAssetNameMap.clear(); + m_importIdToAssetNameMap[pd.importId] = assetName; + + m_puppetQueue.append(pd.importId); + startNextImportProcess(); +} + bool ItemLibraryAssetImporter::isImporting() const { return m_isImporting; @@ -103,19 +150,19 @@ void ItemLibraryAssetImporter::cancelImport() notifyFinished(); } -void ItemLibraryAssetImporter::addError(const QString &errMsg, const QString &srcPath) const +void ItemLibraryAssetImporter::addError(const QString &errMsg, const QString &srcPath) { qCDebug(importerLog) << "Error: "<< errMsg << srcPath; emit errorReported(errMsg, srcPath); } -void ItemLibraryAssetImporter::addWarning(const QString &warningMsg, const QString &srcPath) const +void ItemLibraryAssetImporter::addWarning(const QString &warningMsg, const QString &srcPath) { qCDebug(importerLog) << "Warning: " << warningMsg << srcPath; emit warningReported(warningMsg, srcPath); } -void ItemLibraryAssetImporter::addInfo(const QString &infoMsg, const QString &srcPath) const +void ItemLibraryAssetImporter::addInfo(const QString &infoMsg, const QString &srcPath) { qCDebug(importerLog) << "Info: " << infoMsg << srcPath; emit infoReported(infoMsg, srcPath); @@ -126,8 +173,8 @@ void ItemLibraryAssetImporter::importProcessFinished([[maybe_unused]] int exitCo { m_puppetProcess.reset(); - if (m_parseData.contains(m_currentImportId)) { - const ParseData &pd = m_parseData[m_currentImportId]; + if (m_importIdToAssetNameMap.contains(m_currentImportId)) { + const ParseData &pd = m_parseData[m_importIdToAssetNameMap[m_currentImportId]]; QString errStr; if (exitStatus == QProcess::ExitStatus::CrashExit) { errStr = tr("Import process crashed."); @@ -150,11 +197,12 @@ void ItemLibraryAssetImporter::importProcessFinished([[maybe_unused]] int exitCo addError(tr("Asset import process failed: \"%1\".") .arg(pd.sourceInfo.absoluteFilePath())); addError(errStr); - m_parseData.remove(m_currentImportId); + m_parseData.remove(m_importIdToAssetNameMap[m_currentImportId]); + m_importIdToAssetNameMap.remove(m_currentImportId); } } - int finishedCount = m_parseData.size() - m_puppetQueue.size(); + int finishedCount = m_importIdToAssetNameMap.size() - m_puppetQueue.size(); if (!m_puppetQueue.isEmpty()) startNextImportProcess(); @@ -162,7 +210,7 @@ void ItemLibraryAssetImporter::importProcessFinished([[maybe_unused]] int exitCo notifyProgress(100); QTimer::singleShot(0, this, &ItemLibraryAssetImporter::postImport); } else { - notifyProgress(int(100. * (double(finishedCount) / double(m_parseData.size())))); + notifyProgress(int(100. * (double(finishedCount) / double(m_importIdToAssetNameMap.size())))); } } @@ -178,7 +226,7 @@ void ItemLibraryAssetImporter::reset() m_cancelled = false; delete m_tempDir; - m_tempDir = new QTemporaryDir; + m_tempDir = new QTemporaryDir(QDir::tempPath() + tempDirNameBase()); m_importFiles.clear(); m_overwrittenImports.clear(); m_puppetProcess.reset(); @@ -186,6 +234,7 @@ void ItemLibraryAssetImporter::reset() m_requiredImports.clear(); m_currentImportId = 0; m_puppetQueue.clear(); + m_importIdToAssetNameMap.clear(); } void ItemLibraryAssetImporter::parseFiles(const QStringList &filePaths, @@ -207,9 +256,11 @@ void ItemLibraryAssetImporter::parseFiles(const QStringList &filePaths, int index = extToImportOptionsMap.value(QFileInfo(file).suffix()); ParseData pd; pd.options = options[index]; + pd.optionsIndex = index; if (preParseQuick3DAsset(file, pd, preselectedFilesForOverwrite)) { pd.importId = ++m_importIdCounter; - m_parseData.insert(pd.importId, pd); + m_importIdToAssetNameMap[pd.importId] = pd.assetName; + m_parseData.insert(pd.assetName, pd); } notifyProgress(qRound(++count * quota), progressTitle); } @@ -238,7 +289,7 @@ bool ItemLibraryAssetImporter::preParseQuick3DAsset(const QString &file, ParseDa pd.targetDirPath = pd.targetDir.filePath(pd.assetName); - if (pd.outDir.exists(pd.assetName)) { + if (m_parseData.contains(pd.assetName)) { addWarning(tr("Skipped import of duplicate asset: \"%1\".").arg(pd.assetName)); return false; } @@ -287,11 +338,12 @@ bool ItemLibraryAssetImporter::preParseQuick3DAsset(const QString &file, ParseDa } } - pd.outDir.mkpath(pd.assetName); + QString assetFolder = generateAssetFolderName(pd.assetName); + pd.outDir.mkpath(assetFolder); - if (!pd.outDir.cd(pd.assetName)) { + if (!pd.outDir.cd(assetFolder)) { addError(tr("Could not access temporary asset directory: \"%1\".") - .arg(pd.outDir.filePath(pd.assetName))); + .arg(pd.outDir.filePath(assetFolder))); return false; } return true; @@ -329,12 +381,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()); @@ -421,7 +476,7 @@ void ItemLibraryAssetImporter::postParseQuick3DAsset(ParseData &pd) // Copy the original asset into a subdirectory assetFiles.insert(sourcePath, sourceSceneTargetFilePath(pd)); - m_importFiles.insert(assetFiles); + m_importFiles.insert(pd.assetName, assetFiles); } void ItemLibraryAssetImporter::copyImportedFiles() @@ -496,6 +551,12 @@ void ItemLibraryAssetImporter::keepUiAlive() const QApplication::processEvents(); } +QString ItemLibraryAssetImporter::generateAssetFolderName(const QString &assetName) const +{ + static int counter = 0; + return assetName + "_QDS_" + QString::number(counter++); +} + ItemLibraryAssetImporter::OverwriteResult ItemLibraryAssetImporter::confirmAssetOverwrite(const QString &assetName) { const QString title = tr("Overwrite Existing Asset?"); @@ -530,7 +591,8 @@ void ItemLibraryAssetImporter::startNextImportProcess() if (model && view) { bool done = false; while (!m_puppetQueue.isEmpty() && !done) { - const ParseData pd = m_parseData.value(m_puppetQueue.takeLast()); + const ParseData pd = m_parseData.value( + m_importIdToAssetNameMap.value(m_puppetQueue.takeLast())); QStringList puppetArgs; QJsonDocument optDoc(pd.options); @@ -553,7 +615,8 @@ void ItemLibraryAssetImporter::startNextImportProcess() } else { addError(tr("Failed to start import 3D asset process."), pd.sourceInfo.absoluteFilePath()); - m_parseData.remove(pd.importId); + const QString assetName = m_importIdToAssetNameMap.take(pd.importId); + m_parseData.remove(assetName); m_puppetProcess.reset(); } } @@ -569,8 +632,16 @@ void ItemLibraryAssetImporter::postImport() postParseQuick3DAsset(pd); } - if (!isCancelled()) - finalizeQuick3DImport(); + if (!isCancelled()) { + // TODO: Currently we only support import preview for single imports + if (m_parseData.size() != 1) { + finalizeQuick3DImport(); + } else { + const ParseData &pd = m_parseData[m_parseData.keys().first()]; + const QString importedComponentName = pd.assetName; + emit importReadyForPreview(pd.outDir.absolutePath(), importedComponentName); + } + } } void ItemLibraryAssetImporter::finalizeQuick3DImport() diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h index 8ad7b5a2de..abe9690951 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h @@ -2,8 +2,6 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #pragma once -#include "import.h" - #include <qprocessuniqueptr.h> #include <QSet> @@ -35,20 +33,28 @@ public: const QHash<QString, int> &extToImportOptionsMap, const QSet<QString> &preselectedFilesForOverwrite); + void reImportQuick3D(const QString &assetName, const QVector<QJsonObject> &options); + bool isImporting() const; void cancelImport(); bool isCancelled() const; - void addError(const QString &errMsg, const QString &srcPath = {}) const; - void addWarning(const QString &warningMsg, const QString &srcPath = {}) const; - void addInfo(const QString &infoMsg, const QString &srcPath = {}) const; + void addError(const QString &errMsg, const QString &srcPath = {}); + void addWarning(const QString &warningMsg, const QString &srcPath = {}); + void addInfo(const QString &infoMsg, const QString &srcPath = {}); + + QString previewFileName() const { return "QDSImport3dPreviewScene.qml"; } + QString tempDirNameBase() const { return "/qds3dimport"; } + + void finalizeQuick3DImport(); signals: - void errorReported(const QString &, const QString &) const; - void warningReported(const QString &, const QString &) const; - void infoReported(const QString &, const QString &) const; - void progressChanged(int value, const QString &text) const; - void importNearlyFinished() const; + void errorReported(const QString &, const QString &); + void warningReported(const QString &, const QString &); + void infoReported(const QString &, const QString &); + void progressChanged(int value, const QString &text); + void importReadyForPreview(const QString &path, const QString &compName); + void importNearlyFinished(); void importFinished(); private slots: @@ -63,7 +69,8 @@ private: QFileInfo sourceInfo; QString assetName; QString originalAssetName; - int importId; + int importId = -1; + int optionsIndex = -1; }; void notifyFinished(); @@ -79,6 +86,7 @@ private: void notifyProgress(int value, const QString &text); void notifyProgress(int value); void keepUiAlive() const; + QString generateAssetFolderName(const QString &assetName) const; enum class OverwriteResult { Skip, @@ -89,10 +97,9 @@ private: OverwriteResult confirmAssetOverwrite(const QString &assetName); void startNextImportProcess(); void postImport(); - void finalizeQuick3DImport(); QString sourceSceneTargetFilePath(const ParseData &pd); - QSet<QHash<QString, QString>> m_importFiles; + QHash<QString, QHash<QString, QString>> m_importFiles; // Key: asset name QHash<QString, QStringList> m_overwrittenImports; bool m_isImporting = false; bool m_cancelled = false; @@ -101,7 +108,8 @@ private: QProcessUniquePointer m_puppetProcess; int m_importIdCounter = 0; int m_currentImportId = 0; - QHash<int, ParseData> m_parseData; + QHash<int, QString> m_importIdToAssetNameMap; + QHash<QString, ParseData> m_parseData; // Key: asset name QString m_progressTitle; QStringList m_requiredImports; QList<int> m_puppetQueue; diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp index dbeacc7595..8ef9512e26 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; @@ -312,46 +313,54 @@ void ItemLibraryModel::update([[maybe_unused]] ItemLibraryInfo *itemLibraryInfo, beginResetModel(); clearSections(); + const QString projectName = DocumentManager::currentProjectName(); + + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + QStringList excludedImports { - QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1) + ".MaterialBundle", - QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1) + ".EffectBundle" + projectName, + compUtils.materialsBundleType(), + compUtils.effectsBundleType(), + compUtils.userMaterialsBundleType(), + compUtils.user3DBundleType(), + compUtils.userEffectsBundleType() }; // create import sections - const QString projectName = DocumentManager::currentProjectName(); const Imports usedImports = model->usedImports(); QHash<QString, ItemLibraryImport *> importHash; for (const Import &import : model->imports()) { - if (import.url() != projectName) { - if (excludedImports.contains(import.url()) || import.url().startsWith("Effects.")) - continue; - bool addNew = true; - bool isQuick3DAsset = import.url().startsWith("Quick3DAssets."); - QString importUrl = import.url(); - if (isQuick3DAsset) - importUrl = ItemLibraryImport::quick3DAssetsTitle(); - else if (import.isFileImport()) - importUrl = import.toString(true, true).remove("\""); - - ItemLibraryImport *oldImport = importHash.value(importUrl); - if (oldImport && oldImport->sectionType() == ItemLibraryImport::SectionType::Quick3DAssets - && isQuick3DAsset) { - addNew = false; // add only 1 Quick3DAssets import section - } else if (oldImport && oldImport->importEntry().url() == import.url()) { - // Retain the higher version if multiples exist - if (oldImport->importEntry().toVersion() >= import.toVersion() || import.hasVersion()) - addNew = false; - else - delete oldImport; - } + if (excludedImports.contains(import.url()) + || import.url().startsWith(compUtils.composedEffectsTypePrefix())) { + continue; + } - if (addNew) { - auto sectionType = isQuick3DAsset ? ItemLibraryImport::SectionType::Quick3DAssets - : ItemLibraryImport::SectionType::Default; - ItemLibraryImport *itemLibImport = new ItemLibraryImport(import, this, sectionType); - itemLibImport->setImportUsed(usedImports.contains(import)); - importHash.insert(importUrl, itemLibImport); - } + bool addNew = true; + bool isQuick3DAsset = import.url().startsWith(compUtils.import3dTypePrefix()); + QString importUrl = import.url(); + if (isQuick3DAsset) + importUrl = ItemLibraryImport::quick3DAssetsTitle(); + else if (import.isFileImport()) + importUrl = import.toString(true, true).remove("\""); + + ItemLibraryImport *oldImport = importHash.value(importUrl); + if (oldImport && oldImport->sectionType() == ItemLibraryImport::SectionType::Quick3DAssets + && isQuick3DAsset) { + addNew = false; // add only 1 Quick3DAssets import section + } else if (oldImport && oldImport->importEntry().url() == import.url()) { + // Retain the higher version if multiples exist + if (oldImport->importEntry().toVersion() >= import.toVersion() || import.hasVersion()) + addNew = false; + else + delete oldImport; + } + + if (addNew) { + auto sectionType = isQuick3DAsset ? ItemLibraryImport::SectionType::Quick3DAssets + : ItemLibraryImport::SectionType::Default; + ItemLibraryImport *itemLibImport = new ItemLibraryImport(import, this, sectionType); + itemLibImport->setImportUsed(usedImports.contains(import)); + importHash.insert(importUrl, itemLibImport); } } @@ -367,7 +376,7 @@ void ItemLibraryModel::update([[maybe_unused]] ItemLibraryInfo *itemLibraryInfo, NodeMetaInfo metaInfo; if constexpr (useProjectStorage()) - metaInfo = entry.metaInfo(); + metaInfo = NodeMetaInfo{entry.typeId(), model->projectStorage()}; else metaInfo = model->metaInfo(entry.typeName()); @@ -379,7 +388,8 @@ void ItemLibraryModel::update([[maybe_unused]] ItemLibraryInfo *itemLibraryInfo, || metaInfo.majorVersion() < 0); #endif bool isItem = valid && metaInfo.isQtQuickItem(); - bool forceVisibility = valid && NodeHints::fromItemLibraryEntry(entry).visibleInLibrary(); + bool forceVisibility = valid + && NodeHints::fromItemLibraryEntry(entry, model).visibleInLibrary(); if (m_flowMode) { isItem = metaInfo.isFlowViewItem(); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h index 212ddf8e04..b04ea492d2 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/itemlibraryview.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp index feff523b9c..e4f4ffcd9c 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp @@ -164,7 +164,7 @@ void ItemLibraryView::updateImport3DSupport(const QVariantMap &supportMap) auto importDlg = new ItemLibraryAssetImportDialog(fileNames, defaultDir, m_importableExtensions3DMap, m_importOptions3DMap, {}, {}, - Core::ICore::dialogParent()); + this, Core::ICore::dialogParent()); int result = importDlg->exec(); return result == QDialog::Accepted ? AddFilesResult::succeeded() : AddFilesResult::cancelled(); @@ -198,7 +198,7 @@ void ItemLibraryView::customNotification(const AbstractView *view, const QString const QList<ModelNode> &nodeList, const QList<QVariant> &data) { if (identifier == "UpdateImported3DAsset" && nodeList.size() > 0) { - ItemLibraryAssetImportDialog::updateImport(nodeList[0], + ItemLibraryAssetImportDialog::updateImport(this, nodeList[0], m_importableExtensions3DMap, m_importOptions3DMap); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp index b710a8226f..ffefc43178 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<ItemLibraryModel>()) + , m_addModuleModel(std::make_unique<ItemLibraryAddImportModel>()) + , m_itemsWidget(Utils::makeUniqueObjectPtr<StudioQuickWidget>()) , 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)}, @@ -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/components/itemlibrary/itemlibrarywidget.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h index b56532b218..2940db7a73 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h @@ -10,9 +10,10 @@ #include <studioquickwidget.h> -#include <utils/fancylineedit.h> -#include <utils/dropsupport.h> #include <previewtooltip/previewtooltipbackend.h> +#include <utils/dropsupport.h> +#include <utils/fancylineedit.h> +#include <utils/uniqueobjectptr.h> #include <QFileIconProvider> #include <QFrame> @@ -104,10 +105,10 @@ private: #ifndef QDS_USE_PROJECTSTORAGE QPointer<ItemLibraryInfo> m_itemLibraryInfo; #endif - QPointer<ItemLibraryModel> m_itemLibraryModel; - QPointer<ItemLibraryAddImportModel> m_addModuleModel; + std::unique_ptr<ItemLibraryModel> m_itemLibraryModel; + std::unique_ptr<ItemLibraryAddImportModel> m_addModuleModel; - QScopedPointer<StudioQuickWidget> m_itemsWidget; + Utils::UniqueObjectPtr<StudioQuickWidget> m_itemsWidget; std::unique_ptr<PreviewTooltipBackend> m_previewTooltipBackend; QShortcut *m_qmlSourceUpdateShortcut; diff --git a/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.cpp b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.cpp index b6009edc77..f97b6f74ac 100644 --- a/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.cpp +++ b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.cpp @@ -29,10 +29,10 @@ public: QVariant maybeConvertToNumber(const QVariant &value) { - if (value.typeId() == QVariant::Bool) + if (value.typeId() == QMetaType::Bool) return value; - if (value.typeId() == QVariant::String) { + if (value.typeId() == QMetaType::QString) { const QString text = value.toString(); if (text == "true") return QVariant(true); diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp index d36e78512b..b74f310741 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp @@ -366,6 +366,9 @@ void MaterialBrowserModel::selectMaterial(int idx, bool force) if (idx != m_selectedIndex || force) { m_selectedIndex = idx; emit selectedIndexChanged(idx); + + m_selectedMaterialIsComponent = selectedMaterial().isComponent(); + emit selectedMaterialIsComponentChanged(); } } @@ -434,22 +437,20 @@ void MaterialBrowserModel::copyMaterialProperties(int idx, const QString §io QJsonObject propsSpecObj = m_propertyGroupsObj.value(m_copiedMaterialType).toObject(); if (propsSpecObj.contains(section)) { // should always be true const QJsonArray propNames = propsSpecObj.value(section).toArray(); - // auto == QJsonValueConstRef after 04dc959d49e5e3 / Qt 6.4, QJsonValueRef before - for (const auto &propName : propNames) + for (const QJsonValueConstRef &propName : propNames) copiedProps.append(propName.toString().toLatin1()); if (section == "Base") { // add QtQuick3D.Material base props as well QJsonObject propsMatObj = m_propertyGroupsObj.value("Material").toObject(); const QJsonArray propNames = propsMatObj.value("Base").toArray(); - // auto == QJsonValueConstRef after 04dc959d49e5e3 / Qt 6.4, QJsonValueRef before - for (const auto &propName : propNames) + for (const QJsonValueConstRef &propName : propNames) copiedProps.append(propName.toString().toLatin1()); } } } m_copiedMaterialProps.clear(); - for (const auto &propName : copiedProps) { + for (const PropertyName &propName : copiedProps) { PropertyCopyData data; data.name = propName; data.isValid = m_allPropsCopied || validProps.contains(propName); diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h index 337dce0550..3b6b64ec86 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h @@ -3,7 +3,7 @@ #pragma once -#include "modelnode.h" +#include <modelnode.h> #include <QAbstractListModel> #include <QJsonObject> @@ -20,6 +20,7 @@ class MaterialBrowserModel : public QAbstractListModel Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged) + Q_PROPERTY(bool selectedMaterialIsComponent MEMBER m_selectedMaterialIsComponent NOTIFY selectedMaterialIsComponentChanged) Q_PROPERTY(bool hasQuick3DImport READ hasQuick3DImport WRITE setHasQuick3DImport NOTIFY hasQuick3DImportChanged) Q_PROPERTY(bool hasModelSelection READ hasModelSelection WRITE setHasModelSelection NOTIFY hasModelSelectionChanged) Q_PROPERTY(bool hasMaterialLibrary READ hasMaterialLibrary WRITE setHasMaterialLibrary NOTIFY hasMaterialLibraryChanged) @@ -110,6 +111,7 @@ signals: const QList<QmlDesigner::MaterialBrowserModel::PropertyCopyData> &props, bool all); void isQt6ProjectChanged(); + void selectedMaterialIsComponentChanged(); private: bool isValidIndex(int idx) const; @@ -132,6 +134,7 @@ private: bool m_hasMaterialLibrary = false; bool m_allPropsCopied = true; bool m_isQt6Project = false; + bool m_selectedMaterialIsComponent = false; QString m_copiedMaterialType; QPointer<MaterialBrowserView> m_view; diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp index ec95f3e5d3..918956a04c 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/components/materialbrowser/materialbrowserview.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp index 8bd5761728..7d90dffffc 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp @@ -446,8 +446,13 @@ void QmlDesigner::MaterialBrowserView::loadPropertyGroups() if (!m_hasQuick3DImport || m_propertyGroupsLoaded || !model()) return; +#ifdef QDS_USE_PROJECTSTORAGE + // TODO + QString matPropsPath; +#else QString matPropsPath = model()->metaInfo("QtQuick3D.Material").importDirectoryPath() + "/designer/propertyGroups.json"; +#endif m_propertyGroupsLoaded = m_widget->materialBrowserModel()->loadPropertyGroups(matPropsPath); } diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp index 47a1e8d293..8723611be0 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(); @@ -143,7 +150,7 @@ MaterialBrowserWidget::MaterialBrowserWidget(AsynchronousImageCache &imageCache, : m_materialBrowserView(view) , m_materialBrowserModel(new MaterialBrowserModel(view, this)) , m_materialBrowserTexturesModel(new MaterialBrowserTexturesModel(view, this)) - , m_quickWidget(new StudioQuickWidget(this)) + , m_quickWidget(Utils::makeUniqueObjectPtr<StudioQuickWidget>(this)) , m_previewImageProvider(new PreviewImageProvider()) { QImage defaultImage; @@ -172,7 +179,7 @@ MaterialBrowserWidget::MaterialBrowserWidget(AsynchronousImageCache &imageCache, auto layout = new QVBoxLayout(this); layout->setContentsMargins({}); layout->setSpacing(0); - layout->addWidget(m_quickWidget.data()); + layout->addWidget(m_quickWidget.get()); updateSearch(); @@ -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 @@ -397,7 +411,7 @@ void MaterialBrowserWidget::setIsDragging(bool val) StudioQuickWidget *MaterialBrowserWidget::quickWidget() const { - return m_quickWidget.data(); + return m_quickWidget.get(); } void MaterialBrowserWidget::clearPreviewCache() diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h index bfe7ace34d..6506283f85 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h @@ -7,6 +7,8 @@ #include <coreplugin/icontext.h> +#include <utils/uniqueobjectptr.h> + #include <QFrame> QT_BEGIN_NAMESPACE @@ -60,6 +62,7 @@ public: Q_INVOKABLE void acceptAssetsDropOnMaterial(int matIndex, const QList<QUrl> &urls); Q_INVOKABLE void acceptTextureDropOnMaterial(int matIndex, const QString &texId); Q_INVOKABLE void focusMaterialSection(bool focusMatSec); + Q_INVOKABLE void addMaterialToContentLibrary(); StudioQuickWidget *quickWidget() const; @@ -83,7 +86,7 @@ private: QPointer<MaterialBrowserView> m_materialBrowserView; QPointer<MaterialBrowserModel> m_materialBrowserModel; QPointer<MaterialBrowserTexturesModel> m_materialBrowserTexturesModel; - QScopedPointer<StudioQuickWidget> m_quickWidget; + Utils::UniqueObjectPtr<StudioQuickWidget> m_quickWidget; QShortcut *m_qmlSourceUpdateShortcut = nullptr; PreviewImageProvider *m_previewImageProvider = nullptr; diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.cpp index 664006fb7a..f583498db7 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.cpp @@ -46,9 +46,9 @@ QString MaterialEditorContextObject::convertColorToString(const QVariant &color) { QString colorString; QColor theColor; - if (color.canConvert(QVariant::Color)) { + if (color.canConvert(QMetaType(QMetaType::QColor))) { theColor = color.value<QColor>(); - } else if (color.canConvert(QVariant::Vector3D)) { + } else if (color.canConvert(QMetaType(QMetaType::QVector3D))) { auto vec = color.value<QVector3D>(); theColor = QColor::fromRgbF(vec.x(), vec.y(), vec.z()); } diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp index ecc460ae51..0e508f8f36 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp @@ -80,8 +80,8 @@ public: MaterialEditorQmlBackend::MaterialEditorQmlBackend(MaterialEditorView *materialEditor) : m_quickWidget(Utils::makeUniqueObjectPtr<QQuickWidget>()) - , m_materialEditorTransaction(new MaterialEditorTransaction(materialEditor)) - , m_contextObject(new MaterialEditorContextObject(m_quickWidget.get())) + , m_materialEditorTransaction(std::make_unique<MaterialEditorTransaction>(materialEditor)) + , m_contextObject(std::make_unique<MaterialEditorContextObject>(m_quickWidget.get())) , m_materialEditorImageProvider(new MaterialEditorImageProvider()) { m_quickWidget->setObjectName(Constants::OBJECT_NAME_MATERIAL_EDITOR); @@ -90,7 +90,7 @@ MaterialEditorQmlBackend::MaterialEditorQmlBackend(MaterialEditorView *materialE m_quickWidget->engine()->addImageProvider("materialEditor", m_materialEditorImageProvider); m_contextObject->setBackendValues(&m_backendValuesPropertyMap); m_contextObject->setModel(materialEditor->model()); - context()->setContextObject(m_contextObject.data()); + context()->setContextObject(m_contextObject.get()); QObject::connect(&m_backendValuesPropertyMap, &DesignerPropertyMap::valueChanged, materialEditor, &MaterialEditorView::changeValue); @@ -193,7 +193,7 @@ QQmlContext *MaterialEditorQmlBackend::context() const MaterialEditorContextObject *MaterialEditorQmlBackend::contextObject() const { - return m_contextObject.data(); + return m_contextObject.get(); } QQuickWidget *MaterialEditorQmlBackend::widget() const @@ -224,7 +224,7 @@ DesignerPropertyMap &MaterialEditorQmlBackend::backendValuesPropertyMap() MaterialEditorTransaction *MaterialEditorQmlBackend::materialEditorTransaction() const { - return m_materialEditorTransaction.data(); + return m_materialEditorTransaction.get(); } PropertyEditorValue *MaterialEditorQmlBackend::propertyValueForName(const QString &propertyName) @@ -267,12 +267,9 @@ void MaterialEditorQmlBackend::setup(const QmlObjectNode &selectedMaterialNode, // anchors m_backendAnchorBinding.setup(selectedMaterialNode.modelNode()); - context()->setContextProperties( - QVector<QQmlContext::PropertyPair>{ - {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)}, - {{"transaction"}, QVariant::fromValue(m_materialEditorTransaction.data())} - } - ); + context()->setContextProperties(QVector<QQmlContext::PropertyPair>{ + {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)}, + {{"transaction"}, QVariant::fromValue(m_materialEditorTransaction.get())}}); contextObject()->setSpecificsUrl(qmlSpecificsFile); contextObject()->setStateName(stateName); diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.h b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.h index 6f9d6014e9..9fd5fc2399 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.h +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.h @@ -11,6 +11,8 @@ #include <nodemetainfo.h> +#include <memory> + class PropertyEditorValue; QT_BEGIN_NAMESPACE @@ -60,12 +62,15 @@ private: MaterialEditorView *materialEditor); PropertyName auxNamePostFix(const PropertyName &propertyName); + // to avoid a crash while destructing DesignerPropertyMap in the QQmlData + // this needs be destructed after m_quickWidget->engine() is destructed + DesignerPropertyMap m_backendValuesPropertyMap; + Utils::UniqueObjectPtr<QQuickWidget> m_quickWidget = nullptr; QmlAnchorBindingProxy m_backendAnchorBinding; QmlModelNodeProxy m_backendModelNode; - DesignerPropertyMap m_backendValuesPropertyMap; - QScopedPointer<MaterialEditorTransaction> m_materialEditorTransaction; - QScopedPointer<MaterialEditorContextObject> m_contextObject; + std::unique_ptr<MaterialEditorTransaction> m_materialEditorTransaction; + std::unique_ptr<MaterialEditorContextObject> m_contextObject; QPointer<MaterialEditorImageProvider> m_materialEditorImageProvider; }; diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp index e083310cdb..21114267cb 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp @@ -24,7 +24,6 @@ #include "qmldesignerplugin.h" #include "qmltimeline.h" #include "variantproperty.h" -#include <itemlibraryentry.h> #include <utils3d.h> #include <coreplugin/icore.h> @@ -63,10 +62,6 @@ MaterialEditorView::MaterialEditorView(ExternalDependenciesInterface &externalDe } }); - m_typeUpdateTimer.setSingleShot(true); - m_typeUpdateTimer.setInterval(500); - connect(&m_typeUpdateTimer, &QTimer::timeout, this, &MaterialEditorView::updatePossibleTypes); - QmlDesignerPlugin::trackWidgetFocusTime(m_stackedWidget, Constants::EVENT_MATERIALEDITOR_TIME); MaterialEditorDynamicPropertiesProxyModel::registerDeclarativeType(); @@ -333,8 +328,10 @@ void MaterialEditorView::resetView() setupQmlBackend(); - if (m_qmlBackEnd) + if (m_qmlBackEnd) { m_qmlBackEnd->emitSelectionChanged(); + updatePossibleTypes(); + } QTimer::singleShot(0, this, &MaterialEditorView::requestPreviewRender); @@ -605,7 +602,6 @@ void MaterialEditorView::setupQmlBackend() else m_dynamicPropertiesModel->reset(); - delayedTypeUpdate(); initPreviewData(); m_stackedWidget->setCurrentWidget(m_qmlBackEnd->widget()); @@ -690,21 +686,6 @@ void MaterialEditorView::initPreviewData() } } -void MaterialEditorView::delayedTypeUpdate() -{ - m_typeUpdateTimer.start(); -} - -[[maybe_unused]] static Import entryToImport(const ItemLibraryEntry &entry) -{ - if (entry.majorVersion() == -1 && entry.minorVersion() == -1) - return Import::createFileImport(entry.requiredImport()); - - return Import::createLibraryImport(entry.requiredImport(), - QString::number(entry.majorVersion()) + QLatin1Char('.') + - QString::number(entry.minorVersion())); -} - void MaterialEditorView::updatePossibleTypes() { QTC_ASSERT(model(), return); @@ -712,49 +693,21 @@ void MaterialEditorView::updatePossibleTypes() if (!m_qmlBackEnd) return; -#ifdef QDS_USE_PROJECTSTORAGE - auto heirs = model()->qtQuick3DMaterialMetaInfo().heirs(); - heirs.push_back(model()->qtQuick3DMaterialMetaInfo()); - auto entries = Utils::transform<ItemLibraryEntries>(heirs, [&](const auto &heir) { - return toItemLibraryEntries(heir.itemLibrariesEntries(), *model()->projectStorage()); - }); + static const QStringList basicTypes { + "CustomMaterial", + "DefaultMaterial", + "PrincipledMaterial", + "SpecularGlossyMaterial" + }; - // I am unsure about the code intention here -#else // Ensure basic types are always first - QStringList nonQuick3dTypes; - QStringList allTypes; - - const QList<ItemLibraryEntry> itemLibEntries = m_itemLibraryInfo->entries(); - for (const ItemLibraryEntry &entry : itemLibEntries) { - NodeMetaInfo metaInfo = model()->metaInfo(entry.typeName()); - bool valid = metaInfo.isValid() - && (metaInfo.majorVersion() >= entry.majorVersion() - || metaInfo.majorVersion() < 0); - if (valid && metaInfo.isQtQuick3DMaterial()) { - bool addImport = entry.requiredImport().isEmpty(); - if (!addImport) { - Import import = entryToImport(entry); - addImport = model()->hasImport(import, true, true); - } - if (addImport) { - const QList<QByteArray> typeSplit = entry.typeName().split('.'); - const QString typeName = QString::fromLatin1(typeSplit.last()); - if (typeSplit.size() == 2 && typeSplit.first() == "QtQuick3D") { - if (!allTypes.contains(typeName)) - allTypes.append(typeName); - } else if (!nonQuick3dTypes.contains(typeName)) { - nonQuick3dTypes.append(typeName); - } - } - } - } + const QString matType = m_selectedMaterial.simplifiedTypeName(); - allTypes.sort(); - nonQuick3dTypes.sort(); - allTypes.append(nonQuick3dTypes); + if (basicTypes.contains(matType)) { + m_qmlBackEnd->contextObject()->setPossibleTypes(basicTypes); + return; + } - m_qmlBackEnd->contextObject()->setPossibleTypes(allTypes); -#endif + m_qmlBackEnd->contextObject()->setPossibleTypes({matType}); } void MaterialEditorView::modelAttached(Model *model) @@ -774,20 +727,6 @@ void MaterialEditorView::modelAttached(Model *model) m_ensureMatLibTimer.start(500); } -#ifndef QDS_USE_PROJECTSTORAGE - if (m_itemLibraryInfo.data() != model->metaInfo().itemLibraryInfo()) { - if (m_itemLibraryInfo) { - disconnect(m_itemLibraryInfo.data(), &ItemLibraryInfo::entriesChanged, - this, &MaterialEditorView::delayedTypeUpdate); - } - m_itemLibraryInfo = model->metaInfo().itemLibraryInfo(); - if (m_itemLibraryInfo) { - connect(m_itemLibraryInfo.data(), &ItemLibraryInfo::entriesChanged, - this, &MaterialEditorView::delayedTypeUpdate); - } - } -#endif - if (!m_setupCompleted) { reloadQml(); m_setupCompleted = true; @@ -801,7 +740,8 @@ void MaterialEditorView::modelAboutToBeDetached(Model *model) { AbstractView::modelAboutToBeDetached(model); m_dynamicPropertiesModel->reset(); - m_qmlBackEnd->materialEditorTransaction()->end(); + if (auto transaction = m_qmlBackEnd->materialEditorTransaction()) + transaction->end(); m_qmlBackEnd->contextObject()->setHasMaterialLibrary(false); m_selectedMaterial = {}; } @@ -938,7 +878,8 @@ void MaterialEditorView::selectedNodesChanged(const QList<ModelNode> &selectedNo m_selectedModels.append(node); } - m_qmlBackEnd->contextObject()->setHasModelSelection(!m_selectedModels.isEmpty()); + if (m_qmlBackEnd) + m_qmlBackEnd->contextObject()->setHasModelSelection(!m_selectedModels.isEmpty()); } void MaterialEditorView::currentStateChanged(const ModelNode &node) @@ -1020,7 +961,7 @@ void MaterialEditorView::renameMaterial(ModelNode &material, const QString &newN return; executeInTransaction(__FUNCTION__, [&] { - material.setIdWithRefactoring(model()->generateIdFromName(newName, "material")); + material.setIdWithRefactoring(model()->generateNewId(newName, "material")); VariantProperty objNameProp = material.variantProperty("objectName"); objNameProp.setValue(newName); @@ -1057,7 +998,7 @@ void MaterialEditorView::duplicateMaterial(const ModelNode &material) QString newName = sourceMat.modelNode().variantProperty("objectName").value().toString() + " copy"; VariantProperty objNameProp = duplicateMatNode.variantProperty("objectName"); objNameProp.setValue(newName); - duplicateMatNode.setIdWithoutRefactoring(model()->generateIdFromName(newName, "material")); + duplicateMatNode.setIdWithoutRefactoring(model()->generateNewId(newName, "material")); // sync properties. Only the base state is duplicated. const QList<AbstractProperty> props = material.properties(); diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h index c201742bd5..11bea46063 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h @@ -109,12 +109,10 @@ private: bool noValidSelection() const; void initPreviewData(); - void delayedTypeUpdate(); void updatePossibleTypes(); ModelNode m_selectedMaterial; QTimer m_ensureMatLibTimer; - QTimer m_typeUpdateTimer; QShortcut *m_updateShortcut = nullptr; int m_timerId = 0; QStackedWidget *m_stackedWidget = nullptr; diff --git a/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp b/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp index 58b4c42749..fee3218af0 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 <qmldesignerplugin.h> + namespace QmlDesigner { // This will filter and return possible properties that the given type can be bound to @@ -100,7 +102,9 @@ ChooseFromPropertyListFilter::ChooseFromPropertyListFilter(const NodeMetaInfo &i #ifdef QDS_USE_PROJECTSTORAGE // TODO add the types here or use the module #else - } else if (insertInfo.typeName().startsWith("ComponentBundles.MaterialBundle")) { + } else if (insertInfo.typeName().startsWith( + QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().materialsBundleType().toUtf8())) { if (parentInfo.isQtQuick3DModel()) propertyList.append("materials"); #endif diff --git a/src/plugins/qmldesigner/components/navigator/iconcheckboxitemdelegate.cpp b/src/plugins/qmldesigner/components/navigator/iconcheckboxitemdelegate.cpp index 5b36bee7f9..09cf5945e8 100644 --- a/src/plugins/qmldesigner/components/navigator/iconcheckboxitemdelegate.cpp +++ b/src/plugins/qmldesigner/components/navigator/iconcheckboxitemdelegate.cpp @@ -79,15 +79,12 @@ void IconCheckboxItemDelegate::paint(QPainter *painter, if (rowIsPropertyRole(modelIndex.model(), modelIndex) || getModelNode(modelIndex).isRootNode()) return; // Do not paint icons for property rows or root node - QWindow *window = dynamic_cast<QWidget*>(painter->device())->window()->windowHandle(); - QTC_ASSERT(window, return); - const QSize iconSize(16, 16); QPoint iconPosition(styleOption.rect.left() + (styleOption.rect.width() - iconSize.width()) / 2, styleOption.rect.top() + 2 + delegateMargin); const QIcon::State state = isChecked(modelIndex) ? QIcon::State::On : QIcon::State::Off; - const QPixmap iconPixmap = m_icon.pixmap(window, iconSize, mode, state); + const QPixmap iconPixmap = m_icon.pixmap(iconSize, painter->device()->devicePixelRatio(), mode, state); // Shift the lock icon (last column) slightly to the left due to vertical scrollbar width if (modelIndex.column() == NavigatorTreeModel::ColumnType::Lock) diff --git a/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp b/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp index aacaf6dc0d..223e04fd96 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 <model/modelutils.h> +#include <dialogutils.h> #include <modelnodecontextmenu.h> #include <theme.h> #include <qmldesignerconstants.h> @@ -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/navigator/navigatorsearchwidget.cpp b/src/plugins/qmldesigner/components/navigator/navigatorsearchwidget.cpp index 24d4377ef2..e7fe0ebb77 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorsearchwidget.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatorsearchwidget.cpp @@ -97,10 +97,10 @@ void LineEdit::paintEvent(QPaintEvent *event) QPalette p(palette()); p.setColor(QPalette::Active, QPalette::PlaceholderText, - Utils::creatorTheme()->color(Utils::Theme::DSplaceholderTextColor)); + Utils::creatorColor(Utils::Theme::DSplaceholderTextColor)); p.setColor(QPalette::Inactive, QPalette::PlaceholderText, - Utils::creatorTheme()->color(Utils::Theme::DSplaceholderTextColor)); + Utils::creatorColor(Utils::Theme::DSplaceholderTextColor)); setPalette(p); } QLineEdit::paintEvent(event); diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp index d305753ead..c5fa30fc7d 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp @@ -455,7 +455,7 @@ QStringList NavigatorTreeModel::mimeTypes() const Constants::MIME_TYPE_MATERIAL, Constants::MIME_TYPE_BUNDLE_TEXTURE, Constants::MIME_TYPE_BUNDLE_MATERIAL, - Constants::MIME_TYPE_BUNDLE_EFFECT, + Constants::MIME_TYPE_BUNDLE_ITEM, Constants::MIME_TYPE_ASSETS}); return types; @@ -570,9 +570,9 @@ bool NavigatorTreeModel::dropMimeData(const QMimeData *mimeData, } else if (mimeData->hasFormat(Constants::MIME_TYPE_BUNDLE_MATERIAL)) { if (targetNode.isValid()) m_view->emitCustomNotification("drop_bundle_material", {targetNode}); // To ContentLibraryView - } else if (mimeData->hasFormat(Constants::MIME_TYPE_BUNDLE_EFFECT)) { + } else if (mimeData->hasFormat(Constants::MIME_TYPE_BUNDLE_ITEM)) { if (targetNode.isValid()) - m_view->emitCustomNotification("drop_bundle_effect", {targetNode}); // To ContentLibraryView + m_view->emitCustomNotification("drop_bundle_item", {targetNode}); // To ContentLibraryView } else if (mimeData->hasFormat(Constants::MIME_TYPE_ASSETS)) { const QStringList assetsPaths = QString::fromUtf8(mimeData->data(Constants::MIME_TYPE_ASSETS)).split(','); NodeAbstractProperty targetProperty; @@ -705,7 +705,7 @@ void NavigatorTreeModel::handleItemLibraryItemDrop(const QMimeData *mimeData, in const ItemLibraryEntry itemLibraryEntry = createItemLibraryEntryFromMimeData(mimeData->data(Constants::MIME_TYPE_ITEM_LIBRARY_INFO)); - const NodeHints hints = NodeHints::fromItemLibraryEntry(itemLibraryEntry); + const NodeHints hints = NodeHints::fromItemLibraryEntry(itemLibraryEntry, m_view->model()); const QString targetPropertyName = hints.forceNonDefaultProperty(); diff --git a/src/plugins/qmldesigner/components/navigator/previewtooltip.cpp b/src/plugins/qmldesigner/components/navigator/previewtooltip.cpp index f99b964f2d..e5e42697e2 100644 --- a/src/plugins/qmldesigner/components/navigator/previewtooltip.cpp +++ b/src/plugins/qmldesigner/components/navigator/previewtooltip.cpp @@ -21,7 +21,8 @@ PreviewToolTip::PreviewToolTip(QWidget *parent) m_ui->idLabel->setElideMode(Qt::ElideLeft); m_ui->typeLabel->setElideMode(Qt::ElideLeft); m_ui->infoLabel->setElideMode(Qt::ElideLeft); - setStyleSheet(QString("QWidget { background-color: %1 }").arg(Utils::creatorTheme()->color(Utils::Theme::BackgroundColorNormal).name())); + setStyleSheet(QString("QWidget { background-color: %1 }") + .arg(Utils::creatorColor(Utils::Theme::BackgroundColorNormal).name())); m_ui->imageLabel->setStyleSheet("background-color: rgba(0, 0, 0, 0)"); static QPixmap checkers; diff --git a/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.cpp b/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.cpp index 248a9ec429..4034254420 100644 --- a/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.cpp +++ b/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.cpp @@ -20,7 +20,8 @@ PreviewImageTooltip::PreviewImageTooltip(QWidget *parent) m_ui->nameLabel->setElideMode(Qt::ElideLeft); m_ui->pathLabel->setElideMode(Qt::ElideLeft); m_ui->infoLabel->setElideMode(Qt::ElideLeft); - setStyleSheet(QString("QWidget { background-color: %1 }").arg(Utils::creatorTheme()->color(Utils::Theme::BackgroundColorNormal).name())); + setStyleSheet(QString("QWidget { background-color: %1 }") + .arg(Utils::creatorColor(Utils::Theme::BackgroundColorNormal).name())); } PreviewImageTooltip::~PreviewImageTooltip() = default; diff --git a/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp b/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp index 16328ae532..b6d5e2ae47 100644 --- a/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp +++ b/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp @@ -42,7 +42,18 @@ void PreviewTooltipBackend::showTooltip() } }); }, - [](auto) {}, + [&](ImageCache::AbortReason abortReason) { + if (abortReason == ImageCache::AbortReason::Abort) { + qWarning() << QLatin1String("PreviewTooltipBackend::showTooltip(): preview generation " + "failed for path %1, reason: Abort").arg(m_path); + } else if (abortReason == ImageCache::AbortReason::Failed) { + qWarning() << QLatin1String("PreviewTooltipBackend::showTooltip(): preview generation " + "failed for path %1, reason: Failed").arg(m_path); + } else if (abortReason == ImageCache::AbortReason::NoEntry) { + qWarning() << QLatin1String("PreviewTooltipBackend::showTooltip(): preview generation " + "failed for path %1, reason: NoEntry").arg(m_path); + } + }, Utils::PathString{m_extraId}, m_auxiliaryData); diff --git a/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp b/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp index 45f89ae339..4898366619 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) @@ -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); @@ -270,6 +274,8 @@ PropertyEditorValue *DynamicPropertyRow::createProxyBackendValue() auto *newValue = new PropertyEditorValue(this); m_proxyBackendValues.append(newValue); + QQmlEngine::setObjectOwnership(newValue, QJSEngine::CppOwnership); + return newValue; } diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp index 591ce5a57f..1a49ce0c39 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp @@ -87,9 +87,9 @@ QString PropertyEditorContextObject::convertColorToString(const QVariant &color) { QString colorString; QColor theColor; - if (color.canConvert(QVariant::Color)) { + if (color.canConvert(QMetaType(QMetaType::QColor))) { theColor = color.value<QColor>(); - } else if (color.canConvert(QVariant::Vector3D)) { + } else if (color.canConvert(QMetaType(QMetaType::QVector3D))) { auto vec = color.value<QVector3D>(); theColor = QColor::fromRgbF(vec.x(), vec.y(), vec.z()); } @@ -586,8 +586,7 @@ int PropertyEditorContextObject::devicePixelRatio() QStringList PropertyEditorContextObject::styleNamesForFamily(const QString &family) { - const QFontDatabase dataBase; - return dataBase.styles(family); + return QFontDatabase::styles(family); } QStringList PropertyEditorContextObject::allStatesForId(const QString &id) diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp index 7f1ab00bb9..c397d445b1 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp @@ -82,17 +82,18 @@ namespace QmlDesigner { PropertyEditorQmlBackend::PropertyEditorQmlBackend(PropertyEditorView *propertyEditor, AsynchronousImageCache &imageCache) - : m_view(new Quick2PropertyEditorView(imageCache)) - , m_propertyEditorTransaction(new PropertyEditorTransaction(propertyEditor)) - , m_dummyPropertyEditorValue(new PropertyEditorValue()) - , m_contextObject(new PropertyEditorContextObject(m_view)) + : m_view(Utils::makeUniqueObjectPtr<Quick2PropertyEditorView>(imageCache)) + , m_propertyEditorTransaction(std::make_unique<PropertyEditorTransaction>(propertyEditor)) + , m_dummyPropertyEditorValue(std::make_unique<PropertyEditorValue>()) + , m_contextObject(std::make_unique<PropertyEditorContextObject>(m_view.get())) { m_view->engine()->setOutputWarningsToStandardError(QmlDesignerPlugin::instance() ->settings().value(DesignerSettingsKey::SHOW_PROPERTYEDITOR_WARNINGS).toBool()); m_view->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); m_dummyPropertyEditorValue->setValue(QLatin1String("#000000")); - context()->setContextProperty(QLatin1String("dummyBackendValue"), m_dummyPropertyEditorValue.data()); + context()->setContextProperty(QLatin1String("dummyBackendValue"), + m_dummyPropertyEditorValue.get()); m_contextObject->setBackendValues(&m_backendValuesPropertyMap); m_contextObject->setModel(propertyEditor->model()); m_contextObject->insertInQmlContext(context()); @@ -284,6 +285,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, @@ -381,12 +403,12 @@ QQmlContext *PropertyEditorQmlBackend::context() PropertyEditorContextObject *PropertyEditorQmlBackend::contextObject() { - return m_contextObject.data(); + return m_contextObject.get(); } QQuickWidget *PropertyEditorQmlBackend::widget() { - return m_view; + return m_view.get(); } void PropertyEditorQmlBackend::setSource(const QUrl &url) @@ -411,7 +433,7 @@ DesignerPropertyMap &PropertyEditorQmlBackend::backendValuesPropertyMap() { } PropertyEditorTransaction *PropertyEditorQmlBackend::propertyEditorTransaction() { - return m_propertyEditorTransaction.data(); + return m_propertyEditorTransaction.get(); } PropertyEditorValue *PropertyEditorQmlBackend::propertyValueForName(const QString &propertyName) @@ -474,12 +496,9 @@ void PropertyEditorQmlBackend::setup(const QmlObjectNode &qmlObjectNode, const Q // anchors m_backendAnchorBinding.setup(qmlObjectNode.modelNode()); - context()->setContextProperties( - QVector<QQmlContext::PropertyPair>{ - {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)}, - {{"transaction"}, QVariant::fromValue(m_propertyEditorTransaction.data())} - } - ); + context()->setContextProperties(QVector<QQmlContext::PropertyPair>{ + {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)}, + {{"transaction"}, QVariant::fromValue(m_propertyEditorTransaction.get())}}); contextObject()->setHasMultiSelection( !qmlObjectNode.view()->singleSelectedModelNode().isValid()); @@ -571,13 +590,10 @@ void PropertyEditorQmlBackend::initialSetup(const TypeName &typeName, const QUrl QObject::connect(valueObject, &PropertyEditorValue::valueChanged, &backendValuesPropertyMap(), &DesignerPropertyMap::valueChanged); m_backendValuesPropertyMap.insert(QLatin1String("id"), QVariant::fromValue(valueObject)); - context()->setContextProperties( - QVector<QQmlContext::PropertyPair>{ - {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)}, - {{"modelNodeBackend"}, QVariant::fromValue(&m_backendModelNode)}, - {{"transaction"}, QVariant::fromValue(m_propertyEditorTransaction.data())} - } - ); + context()->setContextProperties(QVector<QQmlContext::PropertyPair>{ + {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)}, + {{"modelNodeBackend"}, QVariant::fromValue(&m_backendModelNode)}, + {{"transaction"}, QVariant::fromValue(m_propertyEditorTransaction.get())}}); contextObject()->setSpecificsUrl(qmlSpecificsFile); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h index b677258488..1c9b808ac3 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h @@ -3,17 +3,21 @@ #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" +#include <utils/uniqueobjectptr.h> + #include <nodemetainfo.h> #include <QQmlPropertyMap> +#include <memory> + class PropertyEditorValue; namespace QmlDesigner { @@ -71,7 +75,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); @@ -92,13 +104,16 @@ private: static TypeName fixTypeNameForPanes(const TypeName &typeName); private: - Quick2PropertyEditorView *m_view; + // to avoid a crash while destructing DesignerPropertyMap in the QQmlData + // this needs be destructed after m_quickWidget->engine() is destructed + DesignerPropertyMap m_backendValuesPropertyMap; + + Utils::UniqueObjectPtr<Quick2PropertyEditorView> m_view = nullptr; QmlAnchorBindingProxy m_backendAnchorBinding; QmlModelNodeProxy m_backendModelNode; - DesignerPropertyMap m_backendValuesPropertyMap; - QScopedPointer<PropertyEditorTransaction> m_propertyEditorTransaction; - QScopedPointer<PropertyEditorValue> m_dummyPropertyEditorValue; - QScopedPointer<PropertyEditorContextObject> m_contextObject; + std::unique_ptr<PropertyEditorTransaction> m_propertyEditorTransaction; + std::unique_ptr<PropertyEditorValue> m_dummyPropertyEditorValue; + std::unique_ptr<PropertyEditorContextObject> m_contextObject; }; } //QmlDesigner diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp index 663ebafb65..27319c15f5 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp @@ -10,16 +10,18 @@ #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 <enumeration.h> #include <utils/qtcassert.h> #include <QRegularExpression> -#include <QScopedPointer> #include <QUrl> namespace QmlDesigner { @@ -57,10 +59,10 @@ static bool cleverColorCompare(const QVariant &value1, const QVariant &value2) return c1.name() == c2.name() && c1.alpha() == c2.alpha(); } - if (value1.typeId() == QVariant::String && value2.typeId() == QVariant::Color) + if (value1.typeId() == QMetaType::QString && value2.typeId() == QVariant::Color) return cleverColorCompare(QVariant(QColor(value1.toString())), value2); - if (value1.typeId() == QVariant::Color && value2.typeId() == QVariant::String) + if (value1.typeId() == QVariant::Color && value2.typeId() == QMetaType::QString) return cleverColorCompare(value1, QVariant(QColor(value2.toString()))); return false; @@ -153,7 +155,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 +183,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 +337,7 @@ void PropertyEditorValue::resetValue() m_expression = QString(); emit valueChanged(nameAsQString(), QVariant()); emit expressionChanged({}); + emit expressionChangedQml(); } } @@ -425,6 +429,34 @@ QStringList PropertyEditorValue::getExpressionAsList() const return generateStringList(expression()); } +QVector<double> 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<double> 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 +539,32 @@ 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(); +} + +void PropertyEditorValue::insertKeyframe() +{ + if (!m_modelNode.isValid()) + return; + + /*If we add more code here we have to forward the property editor view */ + AbstractView *view = m_modelNode.view(); + + QmlTimeline timeline = view->currentTimeline(); + + QTC_ASSERT(timeline.isValid(), return ); + QTC_ASSERT(m_modelNode.isValid(), return ); + + view->executeInTransaction("PropertyEditorContextObject::insertKeyframe", + [&] { timeline.insertKeyframe(m_modelNode, name()); }); +} + QStringList PropertyEditorValue::generateStringList(const QString &string) const { QString copy = string; @@ -687,4 +745,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<PropertyEditorValue*>(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<PropertyEditorValue *>( + 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>(); + 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<PropertyEditorValue *>( + 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<PropertyEditorValue *>( + 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<PropertyEditorValue *>( + 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 59236c4fe2..c4b09f6b5a 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 @@ -126,12 +163,17 @@ public: bool isIdList() const; Q_INVOKABLE QStringList getExpressionAsList() const; + Q_INVOKABLE QVector<double> 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); + + Q_INVOKABLE void insertKeyframe(); + public slots: void resetValue(); void setEnumeration(const QString &scope, const QString &name); @@ -143,6 +185,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 +212,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 diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp index 1ff098f4ea..e0d5759617 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp @@ -38,7 +38,6 @@ #include <QFileSystemWatcher> #include <QQuickItem> #include <QScopeGuard> -#include <QScopedPointer> #include <QShortcut> #include <QTimer> @@ -256,66 +255,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 +282,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 +297,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 +316,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<QWidget*>("propertyEditorFrame"); + auto frame = m_qmlBackEndForCurrentType->widget()->findChild<QWidget *>("propertyEditorFrame"); if (frame) frame->resize(m_stackedWidget->size()); } @@ -747,7 +781,11 @@ void PropertyEditorView::propertiesRemoved(const QList<AbstractProperty> &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 +843,11 @@ void PropertyEditorView::variantPropertiesChanged(const QList<VariantProperty>& 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 +872,11 @@ void PropertyEditorView::bindingPropertiesChanged(const QList<BindingProperty> & 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 +998,9 @@ void PropertyEditorView::instancePropertyChanged(const QList<QPair<ModelNode, Pr { if (!m_selectedNode.isValid()) return; + + QTC_ASSERT(m_qmlBackEndForCurrentType, return ); + m_locked = true; using ModelNodePropertyPair = QPair<ModelNode, PropertyName>; @@ -960,7 +1009,11 @@ void PropertyEditorView::instancePropertyChanged(const QList<QPair<ModelNode, Pr const QmlObjectNode qmlObjectNode(modelNode); const PropertyName propertyName = propertyPair.second; - if (qmlObjectNode.isValid() && m_qmlBackEndForCurrentType && modelNode == m_selectedNode && qmlObjectNode.currentState().isValid()) { + m_qmlBackEndForCurrentType->handleInstancePropertyChangedInModelNodeProxy(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 +1022,6 @@ void PropertyEditorView::instancePropertyChanged(const QList<QPair<ModelNode, Pr setValue(modelNode, property.name(), qmlObjectNode.modelValue(property.name())); } } - } m_locked = false; diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h index cc9b522839..ef2b71f558 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h @@ -84,6 +84,16 @@ public: void refreshMetaInfos(const TypeIds &deletedTypeIds) override; + static void setExpressionOnObjectNode(const QmlObjectNode &objectNode, + const PropertyName &name, + const QString &expression); + + static void generateAliasForProperty(const ModelNode &modelNode, + const QString &propertyName); + + static void removeAliasForProperty(const ModelNode &modelNode, + const QString &propertyName); + protected: void timerEvent(QTimerEvent *event) override; void setupPane(const TypeName &typeName); diff --git a/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp b/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp index 96ec5f92e7..1cff0e55e6 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp @@ -4,6 +4,15 @@ #include "abstractview.h" #include "qmlmodelnodeproxy.h" +#include <nodemetainfo.h> + +#include <nodeabstractproperty.h> +#include <nodelistproperty.h> +#include <variantproperty.h> + +#include <utils/qtcassert.h> +#include <utils/algorithm.h> + #include <QtQml> namespace QmlDesigner { @@ -17,6 +26,8 @@ void QmlModelNodeProxy::setup(const QmlObjectNode &objectNode) { m_qmlObjectNode = objectNode; + m_subselection.clear(); + emit modelNodeChanged(); } @@ -75,4 +86,229 @@ QString QmlModelNodeProxy::simplifiedTypeName() const return m_qmlObjectNode.simplifiedTypeName(); } +static QList<int> toInternalIdList(const QList<ModelNode> &nodes) +{ + return Utils::transform(nodes, [](const ModelNode &node) { return node.internalId(); }); +} + +QList<int> 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<int> 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<PropertyEditorSubSelectionWrapper> wrapper( + new PropertyEditorSubSelectionWrapper(node)); + m_subselection.append(wrapper); + + QJSEngine::setObjectOwnership(wrapper.data(), QJSEngine::CppOwnership); + + return wrapper.data(); +} + +void QmlModelNodeProxy::createModelNode(int internalIdParent, + const QString &property, + const QString &typeName, + const QString &requiredImport) +{ + auto parentModelNode = m_qmlObjectNode.modelNode(); + + QTC_ASSERT(parentModelNode.isValid(), return ); + + AbstractView *view = parentModelNode.view(); + + 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(typeName.toUtf8()); +#else + NodeMetaInfo metaInfo = parentModelNode.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) +{ + ModelNode modelNode = m_qmlObjectNode.modelNode(); + + QTC_ASSERT(modelNode.isValid(), return ); + + if (internalIdParent >= 0) + modelNode = m_qmlObjectNode.view()->modelNodeForInternalId(internalIdParent); + + QTC_ASSERT(modelNode.isValid(), return ); + AbstractView *view = m_qmlObjectNode.view(); + view->executeInTransaction("QmlModelNodeProxy::moveNode", [&] { + modelNode.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 && 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 && 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 && 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 && item->isRelevantModelNode(property.parentModelNode())) { + QmlObjectNode objectNode(item->modelNode()); + item->resetValue(property.name()); + item->setValueFromModel(property.name(), objectNode.instanceValue(property.name())); + } + } +} + +QList<int> QmlModelNodeProxy::allChildren(const ModelNode &modelNode) const +{ + return toInternalIdList(modelNode.directSubModelNodes()); +} + +QList<int> 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 4740b01fbd..d8a49d7e10 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 <qmlitemnode.h> #include <QObject> @@ -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<int> allChildren(int internalId = -1) const; + Q_INVOKABLE QList<int> 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<int> allChildren(const ModelNode &modelNode) const; + QList<int> allChildrenOfType(const ModelNode &modelNode, const QString &typeName) const; + PropertyEditorSubSelectionWrapper *findWrapper(int internalId) const; + QmlObjectNode m_qmlObjectNode; + QList<QSharedPointer<PropertyEditorSubSelectionWrapper>> m_subselection; }; } //QmlDesigner diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorstatusbar.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorstatusbar.cpp index a8981ff2f0..3f6f6769fb 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorstatusbar.cpp +++ b/src/plugins/qmldesigner/components/texteditor/texteditorstatusbar.cpp @@ -15,7 +15,8 @@ TextEditorStatusBar::TextEditorStatusBar(QWidget *parent) : QToolBar(parent), m_ addWidget(m_label); /* We have to set another .css, since the central widget has already a style sheet */ - m_label->setStyleSheet(QString("QLabel { color :%1 }").arg(Utils::creatorTheme()->color(Utils::Theme::TextColorError).name())); + m_label->setStyleSheet(QString("QLabel { color :%1 }") + .arg(Utils::creatorColor(Utils::Theme::TextColorError).name())); } void TextEditorStatusBar::clearText() diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp index d400251648..2e52b3358b 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp +++ b/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp @@ -96,7 +96,8 @@ void TextEditorView::modelAboutToBeDetached(Model *model) { AbstractView::modelAboutToBeDetached(model); - m_widget->setTextEditor(nullptr); + if (m_widget) + m_widget->setTextEditor(nullptr); // in case the user closed it explicit we do not want to do anything with the editor if (Core::ModeManager::currentModeId() == Core::Constants::MODE_DESIGN) { diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp index 97ef6c4b68..127780f918 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp +++ b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp @@ -265,7 +265,7 @@ void TextEditorWidget::dragEnterEvent(QDragEnterEvent *dragEnterEvent) void TextEditorWidget::dragMoveEvent(QDragMoveEvent *dragMoveEvent) { - QTextCursor cursor = m_textEditor->editorWidget()->cursorForPosition(dragMoveEvent->pos()); + QTextCursor cursor = m_textEditor->editorWidget()->cursorForPosition(dragMoveEvent->position().toPoint()); const int cursorPosition = cursor.position(); RewriterView *rewriterView = m_textEditorView->model()->rewriterView(); @@ -279,7 +279,7 @@ void TextEditorWidget::dragMoveEvent(QDragMoveEvent *dragMoveEvent) void TextEditorWidget::dropEvent(QDropEvent *dropEvent) { - QTextCursor cursor = m_textEditor->editorWidget()->cursorForPosition(dropEvent->pos()); + QTextCursor cursor = m_textEditor->editorWidget()->cursorForPosition(dropEvent->position().toPoint()); const int cursorPosition = cursor.position(); RewriterView *rewriterView = m_textEditorView->model()->rewriterView(); diff --git a/src/plugins/qmldesigner/components/texttool/textedititemwidget.cpp b/src/plugins/qmldesigner/components/texttool/textedititemwidget.cpp index b9c5868bd8..dc57cf30c8 100644 --- a/src/plugins/qmldesigner/components/texttool/textedititemwidget.cpp +++ b/src/plugins/qmldesigner/components/texttool/textedititemwidget.cpp @@ -38,26 +38,26 @@ void TextEditItemWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem QLineEdit* TextEditItemWidget::lineEdit() const { - if (m_lineEdit.isNull()) { - m_lineEdit.reset(new QLineEdit); + if (!m_lineEdit) { + m_lineEdit = std::make_unique<QLineEdit>(); m_lineEdit->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); QPalette palette = m_lineEdit->palette(); - static QColor selectionColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_FormEditorSelectionColor); + static QColor selectionColor = Utils::creatorColor(Utils::Theme::QmlDesigner_FormEditorSelectionColor); palette.setColor(QPalette::Highlight, selectionColor); palette.setColor(QPalette::HighlightedText, Qt::white); palette.setColor(QPalette::Base, Qt::white); palette.setColor(QPalette::Text, Qt::black); m_lineEdit->setPalette(palette); } - return m_lineEdit.data(); + return m_lineEdit.get(); } QTextEdit* TextEditItemWidget::textEdit() const { - if (m_textEdit.isNull()) { - m_textEdit.reset(new QTextEdit); + if (!m_textEdit) { + m_textEdit = std::make_unique<QTextEdit>(); QPalette palette = m_textEdit->palette(); - static QColor selectionColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_FormEditorSelectionColor); + static QColor selectionColor = Utils::creatorColor(Utils::Theme::QmlDesigner_FormEditorSelectionColor); palette.setColor(QPalette::Highlight, selectionColor); palette.setColor(QPalette::HighlightedText, Qt::white); palette.setColor(QPalette::Base, Qt::white); @@ -65,7 +65,7 @@ QTextEdit* TextEditItemWidget::textEdit() const m_textEdit->setPalette(palette); } - return m_textEdit.data(); + return m_textEdit.get(); } void TextEditItemWidget::activateTextEdit(const QSize &maximumSize) @@ -83,19 +83,19 @@ void TextEditItemWidget::activateLineEdit() QString TextEditItemWidget::text() const { - if (widget() == m_lineEdit.data()) + if (widget() == m_lineEdit.get()) return m_lineEdit->text(); - else if (widget() == m_textEdit.data()) + else if (widget() == m_textEdit.get()) return m_textEdit->toPlainText(); return QString(); } void TextEditItemWidget::updateText(const QString &text) { - if (widget() == m_lineEdit.data()) { + if (widget() == m_lineEdit.get()) { m_lineEdit->setText(text); m_lineEdit->selectAll(); - } else if (widget() == m_textEdit.data()) { + } else if (widget() == m_textEdit.get()) { m_textEdit->setText(text); m_textEdit->selectAll(); } diff --git a/src/plugins/qmldesigner/components/texttool/textedititemwidget.h b/src/plugins/qmldesigner/components/texttool/textedititemwidget.h index 8aa6c409f9..f975f1a3da 100644 --- a/src/plugins/qmldesigner/components/texttool/textedititemwidget.h +++ b/src/plugins/qmldesigner/components/texttool/textedititemwidget.h @@ -3,7 +3,8 @@ #pragma once #include <QGraphicsProxyWidget> -#include <QScopedPointer> + +#include <memory> QT_BEGIN_NAMESPACE class QTextEdit; @@ -32,7 +33,7 @@ protected: QString text() const; private: - mutable QScopedPointer<QLineEdit> m_lineEdit; - mutable QScopedPointer<QTextEdit> m_textEdit; + mutable std::unique_ptr<QLineEdit> m_lineEdit; + mutable std::unique_ptr<QTextEdit> m_textEdit; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorcontextobject.cpp b/src/plugins/qmldesigner/components/textureeditor/textureeditorcontextobject.cpp index 73e784846b..30f276a48d 100644 --- a/src/plugins/qmldesigner/components/textureeditor/textureeditorcontextobject.cpp +++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorcontextobject.cpp @@ -47,9 +47,9 @@ QString TextureEditorContextObject::convertColorToString(const QVariant &color) { QString colorString; QColor theColor; - if (color.canConvert(QVariant::Color)) { + if (color.canConvert(QMetaType(QMetaType::QColor))) { theColor = color.value<QColor>(); - } else if (color.canConvert(QVariant::Vector3D)) { + } else if (color.canConvert(QMetaType(QMetaType::QVector3D))) { auto vec = color.value<QVector3D>(); theColor = QColor::fromRgbF(vec.x(), vec.y(), vec.z()); } diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp index 80cf5693d2..415d943723 100644 --- a/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp @@ -42,21 +42,22 @@ static QObject *variantToQObject(const QVariant &value) namespace QmlDesigner { -TextureEditorQmlBackend::TextureEditorQmlBackend(TextureEditorView *textureEditor, AsynchronousImageCache &imageCache) - : m_view(new QQuickWidget) - , m_textureEditorTransaction(new TextureEditorTransaction(textureEditor)) - , m_contextObject(new TextureEditorContextObject(m_view->rootContext())) +TextureEditorQmlBackend::TextureEditorQmlBackend(TextureEditorView *textureEditor, + AsynchronousImageCache &imageCache) + : m_quickWidget(Utils::makeUniqueObjectPtr<QQuickWidget>()) + , m_textureEditorTransaction(std::make_unique<TextureEditorTransaction>(textureEditor)) + , m_contextObject(std::make_unique<TextureEditorContextObject>(m_quickWidget->rootContext())) { QImage defaultImage; defaultImage.load(Utils::StyleHelper::dpiSpecificImageFile(":/textureeditor/images/texture_default.png")); m_textureEditorImageProvider = new AssetImageProvider(imageCache, defaultImage); - m_view->setResizeMode(QQuickWidget::SizeRootObjectToView); - m_view->setObjectName(Constants::OBJECT_NAME_TEXTURE_EDITOR); - m_view->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); - m_view->engine()->addImageProvider("qmldesigner_thumbnails", m_textureEditorImageProvider); + m_quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); + m_quickWidget->setObjectName(Constants::OBJECT_NAME_TEXTURE_EDITOR); + m_quickWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); + m_quickWidget->engine()->addImageProvider("qmldesigner_thumbnails", m_textureEditorImageProvider); m_contextObject->setBackendValues(&m_backendValuesPropertyMap); m_contextObject->setModel(textureEditor->model()); - context()->setContextObject(m_contextObject.data()); + context()->setContextObject(m_contextObject.get()); QObject::connect(&m_backendValuesPropertyMap, &DesignerPropertyMap::valueChanged, textureEditor, &TextureEditorView::changeValue); @@ -154,22 +155,22 @@ void TextureEditorQmlBackend::setValue(const QmlObjectNode &, const PropertyName QQmlContext *TextureEditorQmlBackend::context() const { - return m_view->rootContext(); + return m_quickWidget->rootContext(); } TextureEditorContextObject *TextureEditorQmlBackend::contextObject() const { - return m_contextObject.data(); + return m_contextObject.get(); } QQuickWidget *TextureEditorQmlBackend::widget() const { - return m_view; + return m_quickWidget.get(); } void TextureEditorQmlBackend::setSource(const QUrl &url) { - m_view->setSource(url); + m_quickWidget->setSource(url); } QmlAnchorBindingProxy &TextureEditorQmlBackend::backendAnchorBinding() @@ -184,7 +185,7 @@ DesignerPropertyMap &TextureEditorQmlBackend::backendValuesPropertyMap() TextureEditorTransaction *TextureEditorQmlBackend::textureEditorTransaction() const { - return m_textureEditorTransaction.data(); + return m_textureEditorTransaction.get(); } PropertyEditorValue *TextureEditorQmlBackend::propertyValueForName(const QString &propertyName) @@ -227,12 +228,9 @@ void TextureEditorQmlBackend::setup(const QmlObjectNode &selectedTextureNode, co // anchors m_backendAnchorBinding.setup(selectedTextureNode.modelNode()); - context()->setContextProperties( - QVector<QQmlContext::PropertyPair>{ - {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)}, - {{"transaction"}, QVariant::fromValue(m_textureEditorTransaction.data())} - } - ); + context()->setContextProperties(QVector<QQmlContext::PropertyPair>{ + {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)}, + {{"transaction"}, QVariant::fromValue(m_textureEditorTransaction.get())}}); contextObject()->setSpecificsUrl(qmlSpecificsFile); contextObject()->setStateName(stateName); diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.h b/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.h index b6d3fddf22..f69c46a569 100644 --- a/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.h +++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.h @@ -7,8 +7,12 @@ #include "qmlanchorbindingproxy.h" #include "qmlmodelnodeproxy.h" +#include <utils/uniqueobjectptr.h> + #include <nodemetainfo.h> +#include <memory> + class PropertyEditorValue; QT_BEGIN_NAMESPACE @@ -58,12 +62,15 @@ private: TextureEditorView *textureEditor); PropertyName auxNamePostFix(const PropertyName &propertyName); - QQuickWidget *m_view = nullptr; + // to avoid a crash while destructing DesignerPropertyMap in the QQmlData + // this needs be destructed after m_quickWidget->engine() is destructed + DesignerPropertyMap m_backendValuesPropertyMap; + + Utils::UniqueObjectPtr<QQuickWidget> m_quickWidget; QmlAnchorBindingProxy m_backendAnchorBinding; QmlModelNodeProxy m_backendModelNode; - DesignerPropertyMap m_backendValuesPropertyMap; - QScopedPointer<TextureEditorTransaction> m_textureEditorTransaction; - QScopedPointer<TextureEditorContextObject> m_contextObject; + std::unique_ptr<TextureEditorTransaction> m_textureEditorTransaction; + std::unique_ptr<TextureEditorContextObject> m_contextObject; AssetImageProvider *m_textureEditorImageProvider = nullptr; }; diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp index 5de3730c97..a637431c4d 100644 --- a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp +++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp @@ -43,7 +43,6 @@ #include <QFileInfo> #include <QQuickWidget> #include <QQuickItem> -#include <QScopedPointer> #include <QStackedWidget> #include <QShortcut> #include <QTimer> @@ -698,7 +697,8 @@ void TextureEditorView::selectedNodesChanged(const QList<ModelNode> &selectedNod m_selectedModel = selectedNodeList.at(0); bool hasValidSelection = QmlObjectNode(m_selectedModel).hasBindingProperty("materials"); - m_qmlBackEnd->contextObject()->setHasSingleModelSelection(hasValidSelection); + if (m_qmlBackEnd) + m_qmlBackEnd->contextObject()->setHasSingleModelSelection(hasValidSelection); } void TextureEditorView::currentStateChanged(const ModelNode &node) diff --git a/src/plugins/qmldesigner/components/timelineeditor/setframevaluedialog.cpp b/src/plugins/qmldesigner/components/timelineeditor/setframevaluedialog.cpp index 2d8998404a..f289583cc3 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<int>::min(), std::numeric_limits<int>::max()); + m_frameControl->setRange(std::numeric_limits<int>::lowest(), std::numeric_limits<int>::max()); m_frameControl->setValue(static_cast<int>(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<QColor>()); 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<int>::min(), std::numeric_limits<int>::max()); + widget->setRange(std::numeric_limits<int>::lowest(), std::numeric_limits<int>::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<float>::min(), std::numeric_limits<float>::max()); + widget->setRange(std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max()); widget->setAlignment(Qt::AlignRight); widget->setValue(value.toFloat()); m_valueGetter = [widget]() { return static_cast<float>(widget->value()); }; @@ -132,7 +131,7 @@ QWidget* SetFrameValueDialog::createValueControl(const QVariant& value) default: { auto* widget = new QDoubleSpinBox; - widget->setRange(std::numeric_limits<double>::min(), std::numeric_limits<double>::max()); + widget->setRange(std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max()); widget->setAlignment(Qt::AlignRight); widget->setValue(value.toDouble()); m_valueGetter = [widget]() { return widget->value(); }; diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.cpp index 83551378ee..56e22dab1b 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.cpp @@ -13,6 +13,7 @@ #include <variantproperty.h> #include <qmlitemnode.h> #include <qmlobjectnode.h> +#include <dialogutils.h> #include <coreplugin/messagebox.h> @@ -96,10 +97,10 @@ TimelineAnimationForm::TimelineAnimationForm(QWidget *parent) using namespace Layouting; Grid { Span(4, mainL), br, - empty(), br, + empty, br, idL, Span(2, m_idLineEdit), Span(2, Row{ runningL, m_running }), br, - empty(), startFrameL, m_startFrame, endFrameL, m_endFrame, durationL, m_duration, br, - empty(), continuousL, m_continuous, loopsL, m_loops, pingPongL, m_pingPong, str, br, + empty, startFrameL, m_startFrame, endFrameL, m_endFrame, durationL, m_duration, br, + empty, continuousL, m_continuous, loopsL, m_loops, pingPongL, m_pingPong, str, br, tr("Transition to state:"), transitionToStateL, m_transitionToState, br, }.attachTo(this); @@ -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 b3d408dc0d..2d1e70cd77 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelineform.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelineform.cpp @@ -10,6 +10,7 @@ #include <nodemetainfo.h> #include <rewritertransaction.h> #include <variantproperty.h> +#include <dialogutils.h> #include <coreplugin/messagebox.h> @@ -77,8 +78,8 @@ TimelineForm::TimelineForm(QWidget *parent) Grid { Span(2, mainL), br, idL, m_idLineEdit, br, - empty(), Row { startFrameL, m_startFrame, st(), endFrameL, m_endFrame }, str, br, - empty(), Row { m_expressionBinding, m_animation, st() }, br, + empty, Row { startFrameL, m_startFrame, st, endFrameL, m_endFrame }, str, br, + empty, Row { m_expressionBinding, m_animation, st }, br, expressionBindingL, m_expressionBindingLineEdit, br, }.attachTo(this); @@ -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/timelineeditor/timelinepropertyitem.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.cpp index 698ef0f03b..04cc8ebebc 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.cpp @@ -35,7 +35,6 @@ #include <QLineEdit> #include <QMenu> #include <QPainter> -#include <QScopedPointer> #include <algorithm> @@ -108,14 +107,14 @@ static void editValue(const ModelNode &frameNode, const std::pair<qreal, qreal> int userType = value.typeId(); QVariant newValue = dialog->value(); - if (newValue.canConvert(userType)) { + if (newValue.canConvert(QMetaType(userType))) { QVariant newValueConverted = newValue; - bool converted = newValueConverted.convert(userType); + bool converted = newValueConverted.convert(QMetaType(userType)); if (!converted) { // convert() fails for int to double, so we try this combination newValueConverted = newValue; - converted = newValueConverted.convert(QMetaType::Double); + converted = newValueConverted.convert(QMetaType(QMetaType::Double)); } if (converted) diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinesettingsmodel.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinesettingsmodel.cpp index 39c1f01ce6..9b85599ead 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinesettingsmodel.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinesettingsmodel.cpp @@ -68,7 +68,7 @@ TimelineEditorDelegate::TimelineEditorDelegate(QWidget *parent) if (factory == nullptr) { factory = new QItemEditorFactory; QItemEditorCreatorBase *creator = new QItemEditorCreator<QComboBox>("currentText"); - factory->registerEditor(QVariant::String, creator); + factory->registerEditor(QMetaType::QString, creator); } setItemEditorFactory(factory); diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp index 7905df68e9..8288e69316 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/timelineeditor/timelinewidget.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp index 591926d3f5..592cf0dff9 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp @@ -221,10 +221,10 @@ TimelineWidget::TimelineWidget(TimelineView *view) { QPalette timelinePalette; - timelinePalette.setColor(QPalette::Text, Utils::creatorTheme()->color( + timelinePalette.setColor(QPalette::Text, Utils::creatorColor( Utils::Theme::DStextColor)); timelinePalette.setColor(QPalette::WindowText, timelinePalette.color(QPalette::Text)); - timelinePalette.setColor(QPalette::Window, Utils::creatorTheme()->color( + timelinePalette.setColor(QPalette::Window, Utils::creatorColor( Utils::Theme::QmlDesigner_BackgroundColorDarkAlternate)); onboardingTopLabel->setPalette(timelinePalette); diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp index e9df928c96..cb84183f92 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp +++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp @@ -21,6 +21,9 @@ #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/icore.h> #include <coreplugin/modemanager.h> + +#include <texteditor/textdocument.h> + #include <projectexplorer/kitmanager.h> #include <projectexplorer/project.h> #include <projectexplorer/projectexplorer.h> @@ -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<TextEditor::TextDocument *>( + 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 5d0b0e712a..02bdae1717 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(); diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp index 104127bd49..9f9e888823 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; } diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitionform.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitionform.cpp index 1770ba63fc..dcbb0b23b3 100644 --- a/src/plugins/qmldesigner/components/transitioneditor/transitionform.cpp +++ b/src/plugins/qmldesigner/components/transitioneditor/transitionform.cpp @@ -13,6 +13,7 @@ #include <rewritertransaction.h> #include <variantproperty.h> #include <qmlitemnode.h> +#include <dialogutils.h> #include <coreplugin/messagebox.h> @@ -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/componentsplugin/components.metainfo b/src/plugins/qmldesigner/componentsplugin/components.metainfo index 8a1e365266..2e070aa322 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.") } } } diff --git a/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp b/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp new file mode 100644 index 0000000000..da40c4d387 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp @@ -0,0 +1,291 @@ +// 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 <qmldesignerconstants.h> + +namespace QmlDesigner { + +bool couldBeProjectModule(const Utils::FilePath &path, const QString &projectName) +{ + if (!path.exists()) + return false; + + Utils::FilePath qmlDirPath = path.pathAppended("qmldir"); + if (qmlDirPath.exists()) { + Utils::expected_str<QByteArray> qmldirContents = qmlDirPath.fileContents(); + if (!qmldirContents.has_value()) + return false; + + const QString expectedLine = QLatin1String("module %1").arg(projectName); + QByteArray fileContents = *qmldirContents; + QTextStream stream(fileContents); + while (!stream.atEnd()) { + QString lineData = stream.readLine().trimmed(); + if (lineData.startsWith(u"module ")) + return lineData == expectedLine; + } + } + if (path.endsWith(projectName)) + return true; + + return false; +} + +GeneratedComponentUtils::GeneratedComponentUtils(ExternalDependenciesInterface &externalDependencies) + : m_externalDependencies(externalDependencies) +{ +} + +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 {}; + + if (basePath.endsWith(Constants::GENERATED_COMPONENTS_FOLDER)) + return basePath.resolvePath(QLatin1String(Constants::COMPONENT_BUNDLES_TYPE)); + + return basePath.resolvePath(QLatin1String(Constants::OLD_COMPONENT_BUNDLES_TYPE)); +} + +Utils::FilePath GeneratedComponentUtils::import3dBasePath() const +{ + 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)); +} + +Utils::FilePath GeneratedComponentUtils::materialBundlePath() const +{ + Utils::FilePath basePath = componentBundlesBasePath(); + + if (basePath.isEmpty()) + return {}; + + if (basePath.endsWith(Constants::OLD_COMPONENT_BUNDLES_TYPE)) + return basePath.resolvePath(QLatin1String(Constants::OLD_COMPONENT_BUNDLES_MATERIAL_BUNDLE_TYPE)); + + return basePath.resolvePath(QLatin1String(Constants::COMPONENT_BUNDLES_MATERIAL_BUNDLE_TYPE)); +} + +Utils::FilePath GeneratedComponentUtils::effectBundlePath() const +{ + Utils::FilePath basePath = componentBundlesBasePath(); + + if (basePath.isEmpty()) + return {}; + + if (basePath.endsWith(Constants::OLD_COMPONENT_BUNDLES_TYPE)) + return basePath.resolvePath(QLatin1String(Constants::OLD_COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE)); + + return basePath.resolvePath(QLatin1String(Constants::COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE)); +} + +Utils::FilePath GeneratedComponentUtils::projectModulePath(bool generateIfNotExists) const +{ + using Utils::FilePath; + FilePath projectPath = FilePath::fromString(m_externalDependencies.currentProjectDirPath()); + + if (projectPath.isEmpty()) + return {}; + + const QString projectName = m_externalDependencies.projectName(); + + FilePath newImportDirectory = projectPath.pathAppended(projectName); + if (couldBeProjectModule(newImportDirectory, projectName)) + return newImportDirectory; + + FilePath oldImportDirectory = projectPath.resolvePath(QLatin1String("imports/") + projectName); + if (couldBeProjectModule(oldImportDirectory, projectName)) + return oldImportDirectory; + + for (const QString &path : m_externalDependencies.projectModulePaths()) { + FilePath dir = FilePath::fromString(path); + if (couldBeProjectModule(dir, projectName)) + return dir; + } + + if (generateIfNotExists) + newImportDirectory.createDir(); + + return newImportDirectory; +} + +bool GeneratedComponentUtils::isImport3dPath(const QString &path) const +{ + return path.contains('/' + QLatin1String(Constants::OLD_QUICK_3D_ASSETS_FOLDER)) + || path.contains(QLatin1String(Constants::GENERATED_COMPONENTS_FOLDER) + '/' + + 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::GENERATED_COMPONENTS_FOLDER) + '/' + + QLatin1String(Constants::COMPOSED_EFFECTS_TYPE)); +} + +bool GeneratedComponentUtils::isBundlePath(const QString &path) const +{ + return path.contains(componentBundlesTypePrefix().replace('.', '/')); +} + +bool GeneratedComponentUtils::isGeneratedPath(const QString &path) const +{ + return path.startsWith(generatedComponentsPath().toFSPathString()); +} + + +QString GeneratedComponentUtils::generatedComponentTypePrefix() const +{ + Utils::FilePath basePath = generatedComponentsPath(); + 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::import3dTypePath() const +{ + QString prefix = import3dTypePrefix(); + prefix.replace('.', '/'); + return prefix; +} + +QString GeneratedComponentUtils::componentBundlesTypePrefix() const +{ + QString basePrefix = generatedComponentTypePrefix(); + + if (basePrefix.endsWith(Constants::GENERATED_COMPONENTS_FOLDER)) + return basePrefix + '.' + QLatin1String(Constants::COMPONENT_BUNDLES_TYPE); + + return Constants::OLD_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; +} + +QString GeneratedComponentUtils::materialsBundleId() const +{ + bool isNewImportDir = generatedComponentTypePrefix().endsWith(Constants::GENERATED_COMPONENTS_FOLDER); + + return QLatin1String(isNewImportDir ? Constants::COMPONENT_BUNDLES_MATERIAL_BUNDLE_TYPE + : Constants::OLD_COMPONENT_BUNDLES_MATERIAL_BUNDLE_TYPE); +} + +QString GeneratedComponentUtils::effectsBundleId() const +{ + bool isNewImportDir = generatedComponentTypePrefix().endsWith(Constants::GENERATED_COMPONENTS_FOLDER); + + return QLatin1String(isNewImportDir ? Constants::COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE + : Constants::OLD_COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE); +} + +QString GeneratedComponentUtils::userMaterialsBundleId() const +{ + return QLatin1String(Constants::COMPONENT_BUNDLES_USER_MATERIAL_BUNDLE_TYPE); +} + +QString GeneratedComponentUtils::userEffectsBundleId() const +{ + return QLatin1String(Constants::COMPONENT_BUNDLES_USER_EFFECT_BUNDLE_TYPE); +} + +QString GeneratedComponentUtils::user3DBundleId() const +{ + return QLatin1String(Constants::COMPONENT_BUNDLES_USER_3D_BUNDLE_TYPE); +} + +QString GeneratedComponentUtils::materialsBundleType() const +{ + return componentBundlesTypePrefix() + '.' + materialsBundleId(); +} + +QString GeneratedComponentUtils::effectsBundleType() const +{ + return componentBundlesTypePrefix() + '.' + effectsBundleId(); +} + +QString GeneratedComponentUtils::userMaterialsBundleType() const +{ + return componentBundlesTypePrefix() + '.' + userMaterialsBundleId(); +} + +QString GeneratedComponentUtils::userEffectsBundleType() const +{ + return componentBundlesTypePrefix() + '.' + userEffectsBundleId(); +} + +QString GeneratedComponentUtils::user3DBundleType() const +{ + return componentBundlesTypePrefix() + '.' + user3DBundleId(); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/generatedcomponentutils.h b/src/plugins/qmldesigner/designercore/generatedcomponentutils.h new file mode 100644 index 0000000000..ceddb405a3 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/generatedcomponentutils.h @@ -0,0 +1,55 @@ +// 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 <externaldependenciesinterface.h> +#include <qmldesignercorelib_exports.h> + +#include <utils/filepath.h> + +#include <QString> + +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; + Utils::FilePath materialBundlePath() const; + Utils::FilePath effectBundlePath() const; + Utils::FilePath projectModulePath(bool generateIfNotExists = false) const; + + bool isImport3dPath(const QString &path) const; + bool isComposedEffectPath(const QString &path) const; + bool isBundlePath(const QString &path) const; + bool isGeneratedPath(const QString &path) const; + + QString generatedComponentTypePrefix() const; + QString import3dTypePrefix() const; + QString import3dTypePath() const; + QString componentBundlesTypePrefix() const; + QString composedEffectsTypePrefix() const; + + QString materialsBundleId() const; + QString effectsBundleId() const; + QString userMaterialsBundleId() const; + QString userEffectsBundleId() const; + QString user3DBundleId() const; + + QString materialsBundleType() const; + QString effectsBundleType() const; + QString userMaterialsBundleType() const; + QString userEffectsBundleType() const; + QString user3DBundleType() const; + +private: + ExternalDependenciesInterface &m_externalDependencies; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp index 955e676d3b..97148e664f 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( diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h index c2a912cc3a..fec68c2894 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 diff --git a/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h b/src/plugins/qmldesigner/designercore/imagecache/taskqueue.h index fac7e7d9bf..dc5ed3de23 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>(arguments)...); } @@ -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() { @@ -66,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; + return m_tasks.size() || m_finishing || m_sleeping; }); - if (timedOutWithoutEntriesOrFinishing || m_finishing) { + 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<Task> getTask(std::unique_lock<std::mutex> lock) @@ -94,29 +104,38 @@ private: return {std::move(task)}; } - template<typename TraceToken> - void ensureThreadIsRunning(TraceToken traceToken) + template<typename Lock, typename TraceToken> + void ensureThreadIsRunning(Lock &lock, TraceToken traceToken) { using namespace NanotraceHR::Literals; if (m_finishing || !m_sleeping) return; + 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) { diff --git a/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h b/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h index 71ddeb7dc1..9055f51b6a 100644 --- a/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h +++ b/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h @@ -28,6 +28,7 @@ public: virtual QString qmlPuppetFallbackDirectory() const = 0; virtual QString defaultPuppetToplevelBuildDirectory() const = 0; virtual QUrl projectUrl() const = 0; + virtual QString projectName() const = 0; virtual QString currentProjectDirPath() const = 0; virtual QUrl currentResourcePath() const = 0; virtual void parseItemLibraryDescriptions() = 0; diff --git a/src/plugins/qmldesigner/designercore/include/itemlibraryentry.h b/src/plugins/qmldesigner/designercore/include/itemlibraryentry.h index f88f9e35c6..2d0f2ef31e 100644 --- a/src/plugins/qmldesigner/designercore/include/itemlibraryentry.h +++ b/src/plugins/qmldesigner/designercore/include/itemlibraryentry.h @@ -42,13 +42,16 @@ class QMLDESIGNERCORE_EXPORT ItemLibraryEntry public: ItemLibraryEntry(); - explicit ItemLibraryEntry(const Storage::Info::ItemLibraryEntry &entry, - const ProjectStorageType &projectStorage); - ~ItemLibraryEntry() = default; + ItemLibraryEntry(const ItemLibraryEntry &) = default; + ItemLibraryEntry &operator=(const ItemLibraryEntry &) = default; + ItemLibraryEntry(ItemLibraryEntry &&) = default; + ItemLibraryEntry &operator=(ItemLibraryEntry &&) = default; + explicit ItemLibraryEntry(const Storage::Info::ItemLibraryEntry &entry); + ~ItemLibraryEntry(); QString name() const; TypeName typeName() const; - const NodeMetaInfo &metaInfo() const; + TypeId typeId() const; QIcon typeIcon() const; QString libraryEntryIconPath() const; int majorVersion() const; @@ -86,7 +89,7 @@ private: using ItemLibraryEntries = QList<ItemLibraryEntry>; QMLDESIGNERCORE_EXPORT QList<ItemLibraryEntry> toItemLibraryEntries( - const Storage::Info::ItemLibraryEntries &entries, const ProjectStorageType &projectStorage); + const Storage::Info::ItemLibraryEntries &entries); } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/include/model.h b/src/plugins/qmldesigner/designercore/include/model.h index b907e6c5d8..39b5cdaa81 100644 --- a/src/plugins/qmldesigner/designercore/include/model.h +++ b/src/plugins/qmldesigner/designercore/include/model.h @@ -137,7 +137,7 @@ public: ModelPointer createModel(const TypeName &typeName, std::unique_ptr<ModelResourceManagementInterface> resourceManagement = {}); - QUrl fileUrl() const; + const QUrl &fileUrl() const; SourceId fileUrlSourceId() const; void setFileUrl(const QUrl &url); @@ -147,7 +147,7 @@ public: void setMetaInfo(const MetaInfo &metaInfo); #endif - Module module(Utils::SmallStringView moduleName); + Module module(Utils::SmallStringView moduleName, Storage::ModuleKind moduleKind); NodeMetaInfo metaInfo(const TypeName &typeName, int majorVersion = -1, int minorVersion = -1) const; NodeMetaInfo metaInfo(Module module, Utils::SmallStringView typeName, @@ -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; @@ -218,14 +219,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); @@ -251,10 +255,7 @@ public: bool hasId(const QString &id) const; bool hasImport(const QString &importUrl) const; - QString generateNewId(const QString &prefixName, - const QString &fallbackPrefix = "element", - std::optional<std::function<bool(const QString &)>> isDuplicate = {}) const; - QString generateIdFromName(const QString &name, const QString &fallbackId = "element") const; + QString generateNewId(const QString &prefixName, const QString &fallbackPrefix = "element") const; void startDrag(QMimeData *mimeData, const QPixmap &icon); void endDrag(); diff --git a/src/plugins/qmldesigner/designercore/include/modelfwd.h b/src/plugins/qmldesigner/designercore/include/modelfwd.h index 0a062289fd..91c533fe7b 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<Sqlite::Database>; +using ProjectStorageType = ProjectStorage; using PathCacheType = SourcePathCache<ProjectStorageType, NonLockingMutex>; #endif diff --git a/src/plugins/qmldesigner/designercore/include/nodehints.h b/src/plugins/qmldesigner/designercore/include/nodehints.h index 9e67c2d99b..99470db65f 100644 --- a/src/plugins/qmldesigner/designercore/include/nodehints.h +++ b/src/plugins/qmldesigner/designercore/include/nodehints.h @@ -3,9 +3,11 @@ #pragma once +#include "modelnode.h" +#include "nodemetainfo.h" + #include <QList> #include <QString> -#include "modelnode.h" #include "qmldesignercorelib_global.h" #include "invalidmetainfoexception.h" @@ -54,18 +56,19 @@ public: QHash<QString, QString> hints() const; static NodeHints fromModelNode(const ModelNode &modelNode); - static NodeHints fromItemLibraryEntry(const ItemLibraryEntry &entry); + static NodeHints fromItemLibraryEntry(const ItemLibraryEntry &entry, Model *model); private: explicit NodeHints(const ModelNode &modelNode); explicit NodeHints(const NodeMetaInfo &metaInfo); - explicit NodeHints(const ItemLibraryEntry &entry); + explicit NodeHints(const ItemLibraryEntry &entry, Model *model); const ModelNode &modelNode() const; bool isValid() const; Model *model() const; bool evaluateBooleanExpression(const QString &hintName, bool defaultValue, const ModelNode potentialParent = ModelNode()) const; ModelNode m_modelNode; + NodeMetaInfo m_metaInfo; QHash<QString, QString> m_hints; }; diff --git a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h index 53c755ddc8..fd3f2f9be8 100644 --- a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h +++ b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h @@ -27,9 +27,15 @@ 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.")]] +# define DEPRECATED_IMPORT_DIRECTORY_PATH [[deprecated("Use allExportedTypeNames().")]] +# define DEPRECATED_REQUIRED_IMPORT_STRING [[deprecated("Use allExportedTypeNames().")]] #else # define DEPRECATED_TYPENAME # define DEPRECATED_VERSION_NUMBER +# define DEPRECATED_COMPONENT_FILE_NAME +# define DEPRECATED_IMPORT_DIRECTORY_PATH +# define DEPRECATED_REQUIRED_IMPORT_STRING #endif namespace QmlDesigner { @@ -116,7 +122,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; @@ -167,6 +173,7 @@ public: bool isQtMultimediaSoundEffect() const; bool isQtObject() const; bool isQtQmlConnections() const; + bool isQtQmlModelsListElement() const; bool isQtQuick3DBakedLightmap() const; bool isQtQuick3DBuffer() const; bool isQtQuick3DCamera() const; @@ -176,9 +183,9 @@ public: bool isQtQuick3DInstanceList() const; bool isQtQuick3DInstanceListEntry() const; bool isQtQuick3DLight() const; - bool isQtQuickListElement() const; bool isQtQuickListModel() const; bool isQtQuickListView() const; + bool isQtQuickGridView() const; bool isQtQuick3DMaterial() const; bool isQtQuick3DModel() const; bool isQtQuick3DNode() const; @@ -235,8 +242,8 @@ public: bool usesCustomParser() const; bool isEnumeration() const; - QString importDirectoryPath() const; - QString requiredImportString() const; + DEPRECATED_IMPORT_DIRECTORY_PATH QString importDirectoryPath() const; + DEPRECATED_REQUIRED_IMPORT_STRING QString requiredImportString() const; friend bool operator==(const NodeMetaInfo &first, const NodeMetaInfo &second) { @@ -265,12 +272,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<const ProjectStorageType> m_projectStorage = {}; mutable std::optional<Storage::Info::Type> m_typeData; + mutable std::optional<PropertyDeclarationId> m_defaultPropertyId; std::shared_ptr<NodeMetaInfoPrivate> m_privateData; }; diff --git a/src/plugins/qmldesigner/designercore/include/projectstorageids.h b/src/plugins/qmldesigner/designercore/include/projectstorageids.h index bc66e0d2b2..7c2e8c4b25 100644 --- a/src/plugins/qmldesigner/designercore/include/projectstorageids.h +++ b/src/plugins/qmldesigner/designercore/include/projectstorageids.h @@ -7,6 +7,8 @@ #include <utils/span.h> +#include <QVarLengthArray> + namespace QmlDesigner { enum class BasicIdType { @@ -29,6 +31,8 @@ enum class BasicIdType { using TypeId = Sqlite::BasicId<BasicIdType::Type>; using TypeIds = std::vector<TypeId>; +template<std::size_t size> +using SmallTypeIds = QVarLengthArray<TypeId, size>; using PropertyDeclarationId = Sqlite::BasicId<BasicIdType::PropertyDeclaration>; using PropertyDeclarationIds = std::vector<PropertyDeclarationId>; @@ -44,9 +48,13 @@ using EnumerationDeclarationIds = std::vector<EnumerationDeclarationId>; using SourceContextId = Sqlite::BasicId<BasicIdType::SourceContext, int>; using SourceContextIds = std::vector<SourceContextId>; +template<std::size_t size> +using SmallSourceContextIds = QVarLengthArray<SourceContextId, size>; using SourceId = Sqlite::BasicId<BasicIdType::Source, int>; using SourceIds = std::vector<SourceId>; +template<std::size_t size> +using SmallSourceIds = QVarLengthArray<SourceId, size>; using ModuleId = Sqlite::BasicId<BasicIdType::Module, int>; using ModuleIds = std::vector<ModuleId>; diff --git a/src/plugins/qmldesigner/designercore/include/qmlitemnode.h b/src/plugins/qmldesigner/designercore/include/qmlitemnode.h index dde5515a5a..e11f201cdb 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/include/rewriterview.h b/src/plugins/qmldesigner/designercore/include/rewriterview.h index 23841accda..92c79c7863 100644 --- a/src/plugins/qmldesigner/designercore/include/rewriterview.h +++ b/src/plugins/qmldesigner/designercore/include/rewriterview.h @@ -8,11 +8,11 @@ #include "documentmessage.h" #include "rewritertransaction.h" -#include <QScopedPointer> #include <QTimer> #include <QUrl> #include <functional> +#include <memory> namespace QmlJS { class Document; @@ -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<QPair<QString, QString> > qrcMapping() const; @@ -202,9 +203,9 @@ private: //variables bool m_checkLinkErrors = true; DifferenceHandling m_differenceHandling; - QScopedPointer<Internal::ModelNodePositionStorage> m_positionStorage; - QScopedPointer<Internal::ModelToTextMerger> m_modelToTextMerger; - QScopedPointer<Internal::TextToModelMerger> m_textToModelMerger; + std::unique_ptr<Internal::ModelNodePositionStorage> m_positionStorage; + std::unique_ptr<Internal::ModelToTextMerger> m_modelToTextMerger; + std::unique_ptr<Internal::TextToModelMerger> m_textToModelMerger; QList<DocumentMessage> m_errors; QList<DocumentMessage> m_warnings; RewriterTransaction m_removeDefaultPropertyTransaction; diff --git a/src/plugins/qmldesigner/designercore/include/subcomponentmanager.h b/src/plugins/qmldesigner/designercore/include/subcomponentmanager.h index 7fa2348854..a42164d1bd 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 <generatedcomponentutils.h> # include <import.h> # include <QObject> @@ -62,6 +63,7 @@ private: // variables QDir m_filePathDir; QPointer<Model> m_model; ExternalDependenciesInterface &m_externalDependencies; + GeneratedComponentUtils m_componentUtils; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index 33a100f7b2..484f18e42b 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -64,6 +64,8 @@ #include <qmlitemnode.h> #include <rewriterview.h> +#include <projectstorage/projectstorage.h> + #include <utils/hdrimage.h> #include <coreplugin/messagemanager.h> @@ -89,7 +91,6 @@ #include <QMultiHash> #include <QPainter> #include <QPicture> -#include <QScopedPointer> #include <QTimerEvent> #include <QUrl> @@ -205,22 +206,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 +640,7 @@ void NodeInstanceView::auxiliaryDataChanged(const ModelNode &node, TypeName(), key.type}; m_nodeInstanceServer->changeAuxiliaryValues({{container}}); - }; + } break; case AuxiliaryDataType::NodeInstanceAuxiliary: @@ -656,7 +652,7 @@ void NodeInstanceView::auxiliaryDataChanged(const ModelNode &node, TypeName(), key.type}; m_nodeInstanceServer->changeAuxiliaryValues({{container}}); - }; + } break; case AuxiliaryDataType::NodeInstancePropertyOverwrite: @@ -991,6 +987,8 @@ QRectF NodeInstanceView::sceneRect() const return {}; } +namespace { + QList<ModelNode> filterNodesForSkipItems(const QList<ModelNode> &nodeList) { QList<ModelNode> filteredNodeList; @@ -1003,14 +1001,12 @@ QList<ModelNode> filterNodesForSkipItems(const QList<ModelNode> &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 +1020,43 @@ 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(); + using Storage::ModuleKind; + auto module = model->projectStorage()->module(exportedType.moduleId); + Utils::PathString typeName; + switch (module.kind) { + case ModuleKind::QmlLibrary: + typeName += module.name; + typeName += '/'; + break; + case ModuleKind::PathLibrary: + break; + case ModuleKind::CppLibrary: + break; + } + + typeName += exportedType.name; + + return typeName.toQByteArray(); + } + + return {}; +#else + return node.type(); +#endif +} + +} // namespace + CreateSceneCommand NodeInstanceView::createCreateSceneCommand() { QList<ModelNode> nodeList = allModelNodes(); @@ -1079,8 +1112,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), @@ -1182,6 +1216,13 @@ CreateSceneCommand NodeInstanceView::createCreateSceneCommand() if (stateNode.isValid() && stateNode.metaInfo().isQtQuickState()) stateInstanceId = stateNode.internalId(); + QHash<QString, QVariantMap> sceneStates = m_edit3DToolStates[model()->fileUrl()]; + QHash<QString, QVariantMap> projectStates = m_edit3DToolStates[ + QUrl::fromLocalFile(m_externalDependencies.currentProjectDirPath())]; + const QString ptsId = "@PTS"; + if (projectStates.contains(ptsId)) + sceneStates.insert(ptsId, projectStates[ptsId]); + return CreateSceneCommand(instanceContainerList, reparentContainerList, idContainerList, @@ -1192,7 +1233,7 @@ CreateSceneCommand NodeInstanceView::createCreateSceneCommand() mockupTypesVector, model()->fileUrl(), m_externalDependencies.currentResourcePath(), - m_edit3DToolStates[model()->fileUrl()], + sceneStates, lastUsedLanguage, m_captureImageMinimumSize, m_captureImageMaximumSize, @@ -1243,7 +1284,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), @@ -1710,7 +1751,12 @@ void NodeInstanceView::handlePuppetToCreatorCommand(const PuppetToCreatorCommand auto data = qvariant_cast<QVariantList>(command.data()); if (data.size() == 3) { QString qmlId = data[0].toString(); - m_edit3DToolStates[model()->fileUrl()][qmlId].insert(data[1].toString(), data[2]); + QUrl mainKey; + if (qmlId == "@PTS") // Project tool state + mainKey = QUrl::fromLocalFile(m_externalDependencies.currentProjectDirPath()); + else + mainKey = model()->fileUrl(); + m_edit3DToolStates[mainKey][qmlId].insert(data[1].toString(), data[2]); } } } else if (command.type() == PuppetToCreatorCommand::Render3DView) { @@ -1824,7 +1870,7 @@ QVariant NodeInstanceView::modelNodePreviewImageDataToVariant(const ModelNodePre placeHolder = {150, 150}; // Placeholder has transparency, but we don't want to show the checkerboard, so // paint in the correct background color - placeHolder.fill(Utils::creatorTheme()->color(Utils::Theme::BackgroundColorNormal)); + placeHolder.fill(Utils::creatorColor(Utils::Theme::BackgroundColorNormal)); QPainter painter(&placeHolder); painter.drawPixmap(0, 0, 150, 150, placeHolderSrc); } @@ -1850,7 +1896,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()) { @@ -1923,7 +1969,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); } } @@ -1958,7 +2004,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/itemlibraryentry.cpp b/src/plugins/qmldesigner/designercore/metainfo/itemlibraryentry.cpp index 806da7e7c4..2aec766002 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/itemlibraryentry.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/itemlibraryentry.cpp @@ -22,7 +22,7 @@ class ItemLibraryEntryData public: QString name; TypeName typeName; - NodeMetaInfo metaInfo; + TypeId typeId; QString category; int majorVersion{-1}; int minorVersion{-1}; @@ -64,12 +64,12 @@ ItemLibraryEntry::ItemLibraryEntry() : m_data(std::make_shared<Internal::ItemLibraryEntryData>()) {} -ItemLibraryEntry::ItemLibraryEntry(const Storage::Info::ItemLibraryEntry &entry, - const ProjectStorageType &projectStorage) +ItemLibraryEntry::ItemLibraryEntry(const Storage::Info::ItemLibraryEntry &entry) : ItemLibraryEntry{} { m_data->name = entry.name.toQString(); - m_data->metaInfo = {entry.typeId, &projectStorage}; + m_data->typeId = entry.typeId; + m_data->typeName = entry.typeName.toQByteArray(); m_data->category = entry.category.toQString(); if (entry.iconPath.size()) m_data->libraryEntryIconPath = entry.iconPath.toQString(); @@ -87,6 +87,8 @@ ItemLibraryEntry::ItemLibraryEntry(const Storage::Info::ItemLibraryEntry &entry, m_data->extraFilePaths.emplace_back(extraFilePath.toQString()); } +ItemLibraryEntry::~ItemLibraryEntry() = default; + QString ItemLibraryEntry::name() const { return m_data->name; @@ -97,9 +99,9 @@ TypeName ItemLibraryEntry::typeName() const return m_data->typeName; } -const NodeMetaInfo &ItemLibraryEntry::metaInfo() const +TypeId ItemLibraryEntry::typeId() const { - return m_data->metaInfo; + return m_data->typeId; } QString ItemLibraryEntry::qmlSource() const @@ -245,6 +247,7 @@ QDataStream &operator<<(QDataStream &stream, const ItemLibraryEntry &itemLibrary stream << itemLibraryEntry.m_data->qmlSource; stream << itemLibraryEntry.m_data->customComponentSource; stream << itemLibraryEntry.m_data->extraFilePaths; + stream << itemLibraryEntry.m_data->typeId.internalId(); return stream; } @@ -270,6 +273,9 @@ QDataStream &operator>>(QDataStream &stream, ItemLibraryEntry &itemLibraryEntry) stream >> itemLibraryEntry.m_data->qmlSource; stream >> itemLibraryEntry.m_data->customComponentSource; stream >> itemLibraryEntry.m_data->extraFilePaths; + TypeId::DatabaseType internalTypeId; + stream >> internalTypeId; + itemLibraryEntry.m_data->typeId = TypeId::create(internalTypeId); return stream; } @@ -295,11 +301,10 @@ QDebug operator<<(QDebug debug, const ItemLibraryEntry &itemLibraryEntry) return debug.space(); } -QList<ItemLibraryEntry> toItemLibraryEntries(const Storage::Info::ItemLibraryEntries &entries, - const ProjectStorageType &projectStorage) +QList<ItemLibraryEntry> toItemLibraryEntries(const Storage::Info::ItemLibraryEntries &entries) { return Utils::transform<QList<ItemLibraryEntry>>(entries, [&](const auto &entry) { - return ItemLibraryEntry{entry, projectStorage}; + return ItemLibraryEntry{entry}; }); } diff --git a/src/plugins/qmldesigner/designercore/metainfo/metainforeader.cpp b/src/plugins/qmldesigner/designercore/metainfo/metainforeader.cpp index af61ef6573..0addc6884d 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/metainforeader.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/metainforeader.cpp @@ -272,7 +272,7 @@ inline QString deEscape(const QString &value) inline QVariant deEscapeVariant(const QVariant &value) { - if (value.typeId() == QVariant::String) + if (value.typeId() == QMetaType::QString) return deEscape(value.toString()); return value; } diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp index 32e68a3cdc..1f9a3e42bd 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp @@ -106,14 +106,15 @@ QmlDesigner::NodeHints::NodeHints(const ModelNode &node) } NodeHints::NodeHints(const NodeMetaInfo &metaInfo) + : m_metaInfo{metaInfo} { for (const auto &[name, expression] : metaInfo.typeHints()) m_hints.insert(name.toQString(), expression.toQString()); } -NodeHints::NodeHints(const ItemLibraryEntry &entry) +NodeHints::NodeHints(const ItemLibraryEntry &entry, [[maybe_unused]] Model *model) #ifdef QDS_USE_PROJECTSTORAGE - : NodeHints{entry.metaInfo()} + : NodeHints{NodeMetaInfo{entry.typeId(), model->projectStorage()}} #endif { if constexpr (!useProjectStorage()) @@ -135,7 +136,7 @@ bool NodeHints::canBeContainerFor(const ModelNode &potenialChild) const if (!isValid()) return true; - auto flagIs = m_modelNode.metaInfo().canBeContainer(); + auto flagIs = m_metaInfo.canBeContainer(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -151,7 +152,7 @@ bool NodeHints::forceClip() const if (isSwipeView(modelNode())) return true; - auto flagIs = m_modelNode.metaInfo().forceClip(); + auto flagIs = m_metaInfo.forceClip(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -167,7 +168,7 @@ bool NodeHints::doesLayoutChildren() const if (isSwipeView(modelNode())) return true; - auto flagIs = m_modelNode.metaInfo().doesLayoutChildren(); + auto flagIs = m_metaInfo.doesLayoutChildren(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -177,7 +178,7 @@ bool NodeHints::doesLayoutChildren() const bool NodeHints::canBeDroppedInFormEditor() const { - auto flagIs = m_modelNode.metaInfo().canBeDroppedInFormEditor(); + auto flagIs = m_metaInfo.canBeDroppedInFormEditor(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -187,7 +188,7 @@ bool NodeHints::canBeDroppedInFormEditor() const bool NodeHints::canBeDroppedInNavigator() const { - auto flagIs = m_modelNode.metaInfo().canBeDroppedInNavigator(); + auto flagIs = m_metaInfo.canBeDroppedInNavigator(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -197,7 +198,7 @@ bool NodeHints::canBeDroppedInNavigator() const bool NodeHints::canBeDroppedInView3D() const { - auto flagIs = m_modelNode.metaInfo().canBeDroppedInView3D(); + auto flagIs = m_metaInfo.canBeDroppedInView3D(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -210,7 +211,7 @@ bool NodeHints::isMovable() const if (!isValid()) return true; - auto flagIs = m_modelNode.metaInfo().isMovable(); + auto flagIs = m_metaInfo.isMovable(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -223,7 +224,7 @@ bool NodeHints::isResizable() const if (!isValid()) return true; - auto flagIs = m_modelNode.metaInfo().isResizable(); + auto flagIs = m_metaInfo.isResizable(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -236,7 +237,7 @@ bool NodeHints::hasFormEditorItem() const if (!isValid()) return true; - auto flagIs = m_modelNode.metaInfo().hasFormEditorItem(); + auto flagIs = m_metaInfo.hasFormEditorItem(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -252,7 +253,7 @@ bool NodeHints::isStackedContainer() const if (isSwipeView(modelNode())) return true; - auto flagIs = m_modelNode.metaInfo().isStackedContainer(); + auto flagIs = m_metaInfo.isStackedContainer(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -299,7 +300,7 @@ bool NodeHints::takesOverRenderingOfChildren() const if (!isValid()) return false; - auto flagIs = m_modelNode.metaInfo().takesOverRenderingOfChildren(); + auto flagIs = m_metaInfo.takesOverRenderingOfChildren(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -312,7 +313,7 @@ bool NodeHints::visibleInNavigator() const if (!isValid()) return false; - auto flagIs = m_modelNode.metaInfo().visibleInNavigator(); + auto flagIs = m_metaInfo.visibleInNavigator(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -322,7 +323,7 @@ bool NodeHints::visibleInNavigator() const bool NodeHints::visibleInLibrary() const { - auto flagIs = m_modelNode.metaInfo().visibleInLibrary(); + auto flagIs = m_metaInfo.visibleInLibrary(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -391,9 +392,9 @@ NodeHints NodeHints::fromModelNode(const ModelNode &modelNode) return NodeHints(modelNode); } -NodeHints NodeHints::fromItemLibraryEntry(const ItemLibraryEntry &entry) +NodeHints NodeHints::fromItemLibraryEntry(const ItemLibraryEntry &entry, Model *model) { - return NodeHints(entry); + return NodeHints(entry, model); } const ModelNode &NodeHints::modelNode() const diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index 85f904666c..c656634272 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -53,6 +53,10 @@ NodeMetaInfo object will result in an InvalidMetaInfoException being thrown. namespace { +using Storage::ModuleKind; + +auto category = MetaInfoTracing::category; + struct TypeDescription { QString className; @@ -919,8 +923,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); } @@ -1490,15 +1497,20 @@ MetaInfoType NodeMetaInfo::type() const { if constexpr (useProjectStorage()) { if (isValid()) { - switch (typeData().traits.kind) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get type"_t, category(), keyValue("type id", m_typeId)}; + auto kind = typeData().traits.kind; + tracer.end(keyValue("type kind", kind)); + + switch (kind) { case Storage::TypeTraitsKind::Reference: return MetaInfoType::Reference; case Storage::TypeTraitsKind::Value: return MetaInfoType::Value; case Storage::TypeTraitsKind::Sequence: return MetaInfoType::Sequence; - default: - break; + case Storage::TypeTraitsKind::None: + return MetaInfoType::None; } } } @@ -1508,16 +1520,38 @@ MetaInfoType NodeMetaInfo::type() const bool NodeMetaInfo::isFileComponent() const { - if constexpr (useProjectStorage()) - return isValid() && typeData().traits.isFileComponent; - else + if constexpr (useProjectStorage()) { + if (!isValid()) + return {}; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is file component"_t, category(), keyValue("type id", m_typeId)}; + + auto isFileComponent = typeData().traits.isFileComponent; + + tracer.end(keyValue("is file component", isFileComponent)); + + return isFileComponent; + + } else { return isValid() && m_privateData->isFileComponent(); + } } bool NodeMetaInfo::isProjectComponent() const { if constexpr (useProjectStorage()) { - return isValid() && typeData().traits.isProjectComponent; + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is project component"_t, category(), keyValue("type id", m_typeId)}; + + auto isProjectComponent = typeData().traits.isProjectComponent; + + tracer.end(keyValue("is project component", isProjectComponent)); + + return isProjectComponent; } return false; @@ -1526,7 +1560,17 @@ bool NodeMetaInfo::isProjectComponent() const bool NodeMetaInfo::isInProjectModule() const { if constexpr (useProjectStorage()) { - return isValid() && typeData().traits.isInProjectModule; + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is project module"_t, category(), keyValue("type id", m_typeId)}; + + auto isInProjectModule = typeData().traits.isInProjectModule; + + tracer.end(keyValue("is project module", isInProjectModule)); + + return isInProjectModule; } return false; @@ -1535,10 +1579,17 @@ bool NodeMetaInfo::isInProjectModule() const FlagIs NodeMetaInfo::canBeContainer() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.canBeContainer; + if (!isValid()) + return FlagIs::False; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"can be container"_t, category(), keyValue("type id", m_typeId)}; + + auto canBeContainer = typeData().traits.canBeContainer; - return FlagIs::False; + tracer.end(keyValue("can be container", canBeContainer)); + + return canBeContainer; } return FlagIs::Set; @@ -1547,10 +1598,17 @@ FlagIs NodeMetaInfo::canBeContainer() const FlagIs NodeMetaInfo::forceClip() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.forceClip; + if (!isValid()) + return FlagIs::False; - return FlagIs::False; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"force clip"_t, category(), keyValue("type id", m_typeId)}; + + auto forceClip = typeData().traits.forceClip; + + tracer.end(keyValue("force clip", forceClip)); + + return forceClip; } return FlagIs::Set; @@ -1559,10 +1617,17 @@ FlagIs NodeMetaInfo::forceClip() const FlagIs NodeMetaInfo::doesLayoutChildren() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.doesLayoutChildren; + if (!isValid()) + return FlagIs::False; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"does layout children"_t, category(), keyValue("type id", m_typeId)}; - return FlagIs::False; + auto doesLayoutChildren = typeData().traits.doesLayoutChildren; + + tracer.end(keyValue("does layout children", doesLayoutChildren)); + + return doesLayoutChildren; } return FlagIs::Set; @@ -1571,10 +1636,19 @@ FlagIs NodeMetaInfo::doesLayoutChildren() const FlagIs NodeMetaInfo::canBeDroppedInFormEditor() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.canBeDroppedInFormEditor; + if (!isValid()) + return FlagIs::False; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"can be dropped in form editor"_t, + category(), + keyValue("type id", m_typeId)}; - return FlagIs::False; + auto canBeDroppedInFormEditor = typeData().traits.canBeDroppedInFormEditor; + + tracer.end(keyValue("can be dropped in form editor", canBeDroppedInFormEditor)); + + return canBeDroppedInFormEditor; } return FlagIs::Set; @@ -1583,10 +1657,19 @@ FlagIs NodeMetaInfo::canBeDroppedInFormEditor() const FlagIs NodeMetaInfo::canBeDroppedInNavigator() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.canBeDroppedInNavigator; + if (!isValid()) + return FlagIs::False; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"can be dropped in navigator"_t, + category(), + keyValue("type id", m_typeId)}; + + auto canBeDroppedInNavigator = typeData().traits.canBeDroppedInNavigator; - return FlagIs::False; + tracer.end(keyValue("can be dropped in navigator", canBeDroppedInNavigator)); + + return canBeDroppedInNavigator; } return FlagIs::Set; @@ -1595,10 +1678,19 @@ FlagIs NodeMetaInfo::canBeDroppedInNavigator() const FlagIs NodeMetaInfo::canBeDroppedInView3D() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.canBeDroppedInView3D; + if (!isValid()) + return FlagIs::False; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"can be dropped in view3d"_t, + category(), + keyValue("type id", m_typeId)}; + + auto canBeDroppedInView3D = typeData().traits.canBeDroppedInView3D; + + tracer.end(keyValue("can be dropped in view3d", canBeDroppedInView3D)); - return FlagIs::False; + return canBeDroppedInView3D; } return FlagIs::Set; @@ -1607,10 +1699,17 @@ FlagIs NodeMetaInfo::canBeDroppedInView3D() const FlagIs NodeMetaInfo::isMovable() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.isMovable; + if (!isValid()) + return FlagIs::False; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is movable"_t, category(), keyValue("type id", m_typeId)}; - return FlagIs::False; + auto isMovable = typeData().traits.isMovable; + + tracer.end(keyValue("is movable", isMovable)); + + return isMovable; } return FlagIs::Set; @@ -1619,10 +1718,17 @@ FlagIs NodeMetaInfo::isMovable() const FlagIs NodeMetaInfo::isResizable() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.isResizable; + if (!isValid()) + return FlagIs::False; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is resizable"_t, category(), keyValue("type id", m_typeId)}; + + auto isResizable = typeData().traits.isResizable; - return FlagIs::False; + tracer.end(keyValue("is resizable", isResizable)); + + return isResizable; } return FlagIs::Set; @@ -1631,10 +1737,17 @@ FlagIs NodeMetaInfo::isResizable() const FlagIs NodeMetaInfo::hasFormEditorItem() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.hasFormEditorItem; + if (!isValid()) + return FlagIs::False; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"has form editor item"_t, category(), keyValue("type id", m_typeId)}; + + auto hasFormEditorItem = typeData().traits.hasFormEditorItem; - return FlagIs::False; + tracer.end(keyValue("has form editor item", hasFormEditorItem)); + + return hasFormEditorItem; } return FlagIs::Set; @@ -1643,10 +1756,17 @@ FlagIs NodeMetaInfo::hasFormEditorItem() const FlagIs NodeMetaInfo::isStackedContainer() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.isStackedContainer; + if (!isValid()) + return FlagIs::False; - return FlagIs::False; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is stacked container"_t, category(), keyValue("type id", m_typeId)}; + + auto isStackedContainer = typeData().traits.isStackedContainer; + + tracer.end(keyValue("is stacked container", isStackedContainer)); + + return isStackedContainer; } return FlagIs::Set; @@ -1655,10 +1775,19 @@ FlagIs NodeMetaInfo::isStackedContainer() const FlagIs NodeMetaInfo::takesOverRenderingOfChildren() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.takesOverRenderingOfChildren; + if (!isValid()) + return FlagIs::False; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"takes over rendering of children"_t, + category(), + keyValue("type id", m_typeId)}; - return FlagIs::False; + auto takesOverRenderingOfChildren = typeData().traits.takesOverRenderingOfChildren; + + tracer.end(keyValue("takes over rendering of children", takesOverRenderingOfChildren)); + + return takesOverRenderingOfChildren; } return FlagIs::Set; @@ -1667,10 +1796,17 @@ FlagIs NodeMetaInfo::takesOverRenderingOfChildren() const FlagIs NodeMetaInfo::visibleInNavigator() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.visibleInNavigator; + if (!isValid()) + return FlagIs::False; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"visible in navigator"_t, category(), keyValue("type id", m_typeId)}; + + auto visibleInNavigator = typeData().traits.visibleInNavigator; - return FlagIs::False; + tracer.end(keyValue("visible in navigator", visibleInNavigator)); + + return visibleInNavigator; } return FlagIs::Set; @@ -1679,10 +1815,17 @@ FlagIs NodeMetaInfo::visibleInNavigator() const FlagIs NodeMetaInfo::visibleInLibrary() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.visibleInLibrary; + if (!isValid()) + return FlagIs::False; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"visible in library"_t, category(), keyValue("type id", m_typeId)}; + + auto visibleInLibrary = typeData().traits.visibleInLibrary; - return FlagIs::False; + tracer.end(keyValue("visible in library", visibleInLibrary)); + + return visibleInLibrary; } return FlagIs::Set; @@ -1694,6 +1837,12 @@ namespace { TypeId typeId, Utils::SmallStringView propertyName) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get combound property id"_t, + category(), + keyValue("type id", typeId), + keyValue("property name", propertyName)}; + auto begin = propertyName.begin(); const auto end = propertyName.end(); @@ -1709,11 +1858,17 @@ namespace { if (propertyId && found != end) { begin = std::next(found); - return projectStorage.propertyDeclarationId(propertyTypeId, {begin, end}); + auto id = projectStorage.propertyDeclarationId(propertyTypeId, {begin, end}); + + tracer.end(keyValue("property id", id)); + + return id; } } } + tracer.end(keyValue("property id", propertyId)); + return propertyId; } @@ -1721,10 +1876,24 @@ namespace { bool NodeMetaInfo::hasProperty(Utils::SmallStringView propertyName) const { - if constexpr (useProjectStorage()) - return isValid() && bool(propertyId(*m_projectStorage, m_typeId, propertyName)); - else + if constexpr (useProjectStorage()) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"has property"_t, + category(), + keyValue("type id", m_typeId), + keyValue("property name", propertyName)}; + + if (!isValid()) + return false; + + auto hasPropertyId = bool(propertyId(*m_projectStorage, m_typeId, propertyName)); + + tracer.end(keyValue("has property", hasPropertyId)); + + return hasPropertyId; + } else { return isValid() && m_privateData->properties().contains(QByteArrayView(propertyName)); + } } PropertyMetaInfos NodeMetaInfo::properties() const @@ -1733,12 +1902,14 @@ PropertyMetaInfos NodeMetaInfo::properties() const return {}; if constexpr (useProjectStorage()) { - if (isValid()) { - return Utils::transform<PropertyMetaInfos>( - m_projectStorage->propertyDeclarationIds(m_typeId), [&](auto id) { - return PropertyMetaInfo{id, m_projectStorage}; - }); - } + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get properties"_t, category(), keyValue("type id", m_typeId)}; + + return Utils::transform<PropertyMetaInfos>( + m_projectStorage->propertyDeclarationIds(m_typeId), [&](auto id) { + return PropertyMetaInfo{id, m_projectStorage}; + }); + } else { const auto &properties = m_privateData->properties(); @@ -1750,19 +1921,22 @@ PropertyMetaInfos NodeMetaInfo::properties() const return propertyMetaInfos; } - - return {}; } PropertyMetaInfos NodeMetaInfo::localProperties() const { + if (!isValid()) + return {}; + if constexpr (useProjectStorage()) { - if (isValid()) { - return Utils::transform<PropertyMetaInfos>( - m_projectStorage->localPropertyDeclarationIds(m_typeId), [&](auto id) { - return PropertyMetaInfo{id, m_projectStorage}; - }); - } + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get local properties"_t, category(), keyValue("type id", m_typeId)}; + + return Utils::transform<PropertyMetaInfos>( + m_projectStorage->localPropertyDeclarationIds(m_typeId), [&](auto id) { + return PropertyMetaInfo{id, m_projectStorage}; + }); + } else { const auto &properties = m_privateData->localProperties(); @@ -1774,71 +1948,82 @@ PropertyMetaInfos NodeMetaInfo::localProperties() const return propertyMetaInfos; } - - return {}; } PropertyMetaInfo NodeMetaInfo::property(const PropertyName &propertyName) const { + if (!isValid()) + return {}; + if constexpr (useProjectStorage()) { - if (isValid()) - return {propertyId(*m_projectStorage, m_typeId, propertyName), m_projectStorage}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get property"_t, + category(), + keyValue("type id", m_typeId), + keyValue("property name", propertyName)}; + + return {propertyId(*m_projectStorage, m_typeId, propertyName), m_projectStorage}; } else { if (hasProperty(propertyName)) { return PropertyMetaInfo{m_privateData, propertyName}; } + return {}; } - - return {}; } PropertyNameList NodeMetaInfo::signalNames() const { + if (!isValid()) + return {}; + if constexpr (useProjectStorage()) { - if (isValid()) { - return Utils::transform<PropertyNameList>(m_projectStorage->signalDeclarationNames( - m_typeId), - [&](const auto &name) { - return name.toQByteArray(); - }); - } + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get signal names"_t, category(), keyValue("type id", m_typeId)}; + + return Utils::transform<PropertyNameList>(m_projectStorage->signalDeclarationNames(m_typeId), + [&](const auto &name) { + return name.toQByteArray(); + }); + } else { - if (isValid()) - return m_privateData->signalNames(); + return m_privateData->signalNames(); } - - return {}; } PropertyNameList NodeMetaInfo::slotNames() const { + if (!isValid()) + return {}; + if constexpr (useProjectStorage()) { - if (isValid()) { - return Utils::transform<PropertyNameList>(m_projectStorage->functionDeclarationNames( - m_typeId), - [&](const auto &name) { - return name.toQByteArray(); - }); - } + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get slot names"_t, category(), keyValue("type id", m_typeId)}; + return Utils::transform<PropertyNameList>(m_projectStorage->functionDeclarationNames(m_typeId), + [&](const auto &name) { + return name.toQByteArray(); + }); } else { - if (isValid()) - return m_privateData->slotNames(); + return m_privateData->slotNames(); } - - return {}; } PropertyName NodeMetaInfo::defaultPropertyName() const { + if (!isValid()) + return {}; + if constexpr (useProjectStorage()) { - if (isValid()) { - if (auto name = m_projectStorage->propertyName(typeData().defaultPropertyId)) { - return name->toQByteArray(); - } + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get default property name"_t, + category(), + keyValue("type id", m_typeId)}; + if (auto name = m_projectStorage->propertyName(defaultPropertyDeclarationId())) { + tracer.end(keyValue("default property name", name)); + return name->toQByteArray(); } + } else { - if (isValid()) - return m_privateData->defaultPropertyName(); + return m_privateData->defaultPropertyName(); } return {}; @@ -1846,88 +2031,128 @@ PropertyName NodeMetaInfo::defaultPropertyName() const PropertyMetaInfo NodeMetaInfo::defaultProperty() const { + if (!isValid()) + return {}; + if constexpr (useProjectStorage()) { - if (isValid()) { - return PropertyMetaInfo(typeData().defaultPropertyId, m_projectStorage); - } + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get default property"_t, category(), keyValue("type id", m_typeId)}; + + auto id = defaultPropertyDeclarationId(); + + tracer.end(keyValue("default property id", id)); + + return PropertyMetaInfo(id, m_projectStorage); } else { return property(defaultPropertyName()); } - - return {}; } bool NodeMetaInfo::hasDefaultProperty() const { - if constexpr (useProjectStorage()) - return isValid() && bool(typeData().defaultPropertyId); - else + if (!isValid()) + return false; + + if constexpr (useProjectStorage()) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"has default property"_t, category(), keyValue("type id", m_typeId)}; + auto hasDefaultProperty = bool(defaultPropertyDeclarationId()); + tracer.end(keyValue("has default property", hasDefaultProperty)); + + return hasDefaultProperty; + } else { return !defaultPropertyName().isEmpty(); + } } std::vector<NodeMetaInfo> NodeMetaInfo::selfAndPrototypes() const { + if (!isValid()) + return {}; + if constexpr (useProjectStorage()) { - if (isValid()) { - return Utils::transform<NodeMetaInfos>( - m_projectStorage->prototypeAndSelfIds(m_typeId), [&](TypeId typeId) { - return NodeMetaInfo{typeId, m_projectStorage}; - }); - } - } else { - if (isValid()) { - NodeMetaInfos hierarchy = {*this}; - Model *model = m_privateData->model(); - for (const TypeDescription &type : m_privateData->prototypes()) { - auto &last = hierarchy.emplace_back(model, - type.className.toUtf8(), - type.majorVersion, - type.minorVersion); - if (!last.isValid()) - hierarchy.pop_back(); - } + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get self and prototypes"_t, + category(), + keyValue("type id", m_typeId)}; - return hierarchy; + return Utils::transform<NodeMetaInfos>(m_projectStorage->prototypeAndSelfIds(m_typeId), + [&](TypeId typeId) { + return NodeMetaInfo{typeId, m_projectStorage}; + }); + } else { + NodeMetaInfos hierarchy = {*this}; + Model *model = m_privateData->model(); + for (const TypeDescription &type : m_privateData->prototypes()) { + auto &last = hierarchy.emplace_back(model, + type.className.toUtf8(), + type.majorVersion, + type.minorVersion); + if (!last.isValid()) + hierarchy.pop_back(); } - } - return {}; + return hierarchy; + } } NodeMetaInfos NodeMetaInfo::prototypes() const { + if (!isValid()) + return {}; + if constexpr (useProjectStorage()) { - if (isValid()) { - return Utils::transform<NodeMetaInfos>( - m_projectStorage->prototypeIds(m_typeId), [&](TypeId typeId) { - return NodeMetaInfo{typeId, m_projectStorage}; - }); - } - } else { - if (isValid()) { - NodeMetaInfos hierarchy; - Model *model = m_privateData->model(); - for (const TypeDescription &type : m_privateData->prototypes()) { - auto &last = hierarchy.emplace_back(model, - type.className.toUtf8(), - type.majorVersion, - type.minorVersion); - if (!last.isValid()) - hierarchy.pop_back(); - } + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get prototypes"_t, category(), keyValue("type id", m_typeId)}; + return Utils::transform<NodeMetaInfos>(m_projectStorage->prototypeIds(m_typeId), + [&](TypeId typeId) { + return NodeMetaInfo{typeId, m_projectStorage}; + }); - return hierarchy; + } else { + NodeMetaInfos hierarchy; + Model *model = m_privateData->model(); + for (const TypeDescription &type : m_privateData->prototypes()) { + auto &last = hierarchy.emplace_back(model, + type.className.toUtf8(), + type.majorVersion, + type.minorVersion); + if (!last.isValid()) + hierarchy.pop_back(); } + + return hierarchy; } +} - return {}; +namespace { +template<const char *moduleName, const char *typeName, ModuleKind moduleKind = ModuleKind::QmlLibrary> +bool isBasedOnCommonType(NotNullPointer<const ProjectStorageType> projectStorage, TypeId typeId) +{ + if (!typeId) + return false; + + auto base = projectStorage->commonTypeId<moduleName, typeName, moduleKind>(); + + return projectStorage->isBasedOn(typeId, base); } +} // namespace bool NodeMetaInfo::defaultPropertyIsComponent() const { - if (hasDefaultProperty()) - return defaultProperty().propertyType().isQmlComponent(); + if (!isValid()) + return false; - return false; + if (useProjectStorage()) { + auto id = defaultPropertyDeclarationId(); + auto propertyDeclaration = m_projectStorage->propertyDeclaration(id); + + using namespace Storage::Info; + return isBasedOnCommonType<QML, Component>(m_projectStorage, propertyDeclaration->typeId); + } else { + if (hasDefaultProperty()) + return defaultProperty().propertyType().isQmlComponent(); + return false; + } } TypeName NodeMetaInfo::displayName() const @@ -1973,10 +2198,16 @@ int NodeMetaInfo::minorVersion() const Storage::Info::ExportedTypeNames NodeMetaInfo::allExportedTypeNames() const { + if (!isValid()) + return {}; + if constexpr (useProjectStorage()) { - if (isValid()) { - return m_projectStorage->exportedTypeNames(m_typeId); - } + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get all exported type names"_t, + category(), + keyValue("type id", m_typeId)}; + + return m_projectStorage->exportedTypeNames(m_typeId); } return {}; @@ -1984,10 +2215,17 @@ Storage::Info::ExportedTypeNames NodeMetaInfo::allExportedTypeNames() const Storage::Info::ExportedTypeNames NodeMetaInfo::exportedTypeNamesForSourceId(SourceId sourceId) const { + if (!isValid()) + return {}; + if constexpr (useProjectStorage()) { - if (isValid()) { - return m_projectStorage->exportedTypeNames(m_typeId, sourceId); - } + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get exported type names for source id"_t, + category(), + keyValue("type id", m_typeId), + keyValue("source id", sourceId)}; + + return m_projectStorage->exportedTypeNames(m_typeId, sourceId); } return {}; @@ -1995,9 +2233,18 @@ Storage::Info::ExportedTypeNames NodeMetaInfo::exportedTypeNamesForSourceId(Sour Storage::Info::TypeHints NodeMetaInfo::typeHints() const { + if (!isValid()) + return {}; + if constexpr (useProjectStorage()) { - if (isValid()) - return m_projectStorage->typeHints(m_typeId); + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get type hints"_t, category(), keyValue("type id", m_typeId)}; + + auto hints = m_projectStorage->typeHints(m_typeId); + + tracer.end(keyValue("type hints", hints)); + + return hints; } return {}; @@ -2005,9 +2252,18 @@ Storage::Info::TypeHints NodeMetaInfo::typeHints() const Utils::PathString NodeMetaInfo::iconPath() const { + if (!isValid()) + return {}; + if constexpr (useProjectStorage()) { - if (isValid()) - return m_projectStorage->typeIconPath(m_typeId); + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get icon path"_t, category(), keyValue("type id", m_typeId)}; + + auto iconPath = m_projectStorage->typeIconPath(m_typeId); + + tracer.end(keyValue("icon path", iconPath)); + + return iconPath; } return {}; @@ -2015,9 +2271,20 @@ Utils::PathString NodeMetaInfo::iconPath() const Storage::Info::ItemLibraryEntries NodeMetaInfo::itemLibrariesEntries() const { + if (!isValid()) + return {}; + if constexpr (useProjectStorage()) { - if (isValid()) - return m_projectStorage->itemLibraryEntries(m_typeId); + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get item library entries"_t, + category(), + keyValue("type id", m_typeId)}; + + auto entries = m_projectStorage->itemLibraryEntries(m_typeId); + + tracer.end(keyValue("item library entries", entries)); + + return entries; } return {}; @@ -2025,10 +2292,18 @@ Storage::Info::ItemLibraryEntries NodeMetaInfo::itemLibrariesEntries() const SourceId NodeMetaInfo::sourceId() const { + if (!isValid()) + return SourceId{}; + if constexpr (useProjectStorage()) { - if (isValid()) { - return typeData().sourceId; - } + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get source id"_t, category(), keyValue("type id", m_typeId)}; + + auto id = typeData().sourceId; + + tracer.end(keyValue("source id", id)); + + return id; } return SourceId{}; @@ -2061,18 +2336,31 @@ QString NodeMetaInfo::requiredImportString() const if (!isValid()) return {}; - Import imp = m_privateData->requiredImport(); - if (!imp.isEmpty()) - return imp.toImportString(); + if constexpr (!useProjectStorage()) { + Import imp = m_privateData->requiredImport(); + if (!imp.isEmpty()) + return imp.toImportString(); + } return {}; } SourceId NodeMetaInfo::propertyEditorPathId() const { + if (!isValid()) + return SourceId{}; + if (useProjectStorage()) { - if (isValid()) - return m_projectStorage->propertyEditorPathId(m_typeId); + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get property editor path id"_t, + category(), + keyValue("type id", m_typeId)}; + + auto id = m_projectStorage->propertyEditorPathId(m_typeId); + + tracer.end(keyValue("property editor path id", id)); + + return id; } return SourceId{}; @@ -2086,6 +2374,14 @@ const Storage::Info::Type &NodeMetaInfo::typeData() const return *m_typeData; } +PropertyDeclarationId NodeMetaInfo::defaultPropertyDeclarationId() const +{ + if (!m_defaultPropertyId) + m_defaultPropertyId.emplace(m_projectStorage->defaultPropertyDeclarationId(m_typeId)); + + return *m_defaultPropertyId; +} + bool NodeMetaInfo::isSubclassOf(const TypeName &type, int majorVersion, int minorVersion) const { if (!isValid()) { @@ -2122,9 +2418,13 @@ bool NodeMetaInfo::isSubclassOf(const TypeName &type, int majorVersion, int mino bool NodeMetaInfo::isSuitableForMouseAreaFill() const { if constexpr (useProjectStorage()) { - if (!isValid()) { + if (!isValid()) return false; - } + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is suitable for mouse area fill"_t, + category(), + keyValue("type id", m_typeId)}; using namespace Storage::Info; auto itemId = m_projectStorage->commonTypeId<QtQuick, Item>(); @@ -2132,11 +2432,16 @@ bool NodeMetaInfo::isSuitableForMouseAreaFill() const auto controlsControlId = m_projectStorage->commonTypeId<QtQuick_Controls, Control>(); auto templatesControlId = m_projectStorage->commonTypeId<QtQuick_Templates, Control>(); - return m_projectStorage->isBasedOn(m_typeId, - itemId, - mouseAreaId, - controlsControlId, - templatesControlId); + auto isSuitableForMouseAreaFill = m_projectStorage->isBasedOn(m_typeId, + itemId, + mouseAreaId, + controlsControlId, + templatesControlId); + + tracer.end(keyValue("is suitable for mouse area fill", isSuitableForMouseAreaFill)); + + return isSuitableForMouseAreaFill; + } else { return isSubclassOf("QtQuick.Item") && !isSubclassOf("QtQuick.MouseArea") && !isSubclassOf("QtQuick.Controls.Control") @@ -2147,6 +2452,15 @@ bool NodeMetaInfo::isSuitableForMouseAreaFill() const bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo) const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is based on"_t, + category(), + keyValue("type id", m_typeId), + keyValue("meta info type id", metaInfo.m_typeId)}; + return m_projectStorage->isBasedOn(m_typeId, metaInfo.m_typeId); } else { if (!isValid()) @@ -2160,6 +2474,12 @@ bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo) const bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo1, const NodeMetaInfo &metaInfo2) const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is based on"_t, category(), keyValue("type id", m_typeId)}; + return m_projectStorage->isBasedOn(m_typeId, metaInfo1.m_typeId, metaInfo2.m_typeId); } else { if (!isValid()) @@ -2178,6 +2498,12 @@ bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo1, const NodeMetaInfo &metaInfo3) const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is based on"_t, category(), keyValue("type id", m_typeId)}; + return m_projectStorage->isBasedOn(m_typeId, metaInfo1.m_typeId, metaInfo2.m_typeId, @@ -2202,6 +2528,12 @@ bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo1, const NodeMetaInfo &metaInfo4) const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is based on"_t, category(), keyValue("type id", m_typeId)}; + return m_projectStorage->isBasedOn(m_typeId, metaInfo1.m_typeId, metaInfo2.m_typeId, @@ -2229,6 +2561,12 @@ bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo1, const NodeMetaInfo &metaInfo5) const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is based on"_t, category(), keyValue("type id", m_typeId)}; + return m_projectStorage->isBasedOn(m_typeId, metaInfo1.m_typeId, metaInfo2.m_typeId, @@ -2261,6 +2599,12 @@ bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo1, const NodeMetaInfo &metaInfo6) const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is based on"_t, category(), keyValue("type id", m_typeId)}; + return m_projectStorage->isBasedOn(m_typeId, metaInfo1.m_typeId, metaInfo2.m_typeId, @@ -2298,6 +2642,12 @@ bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo1, const NodeMetaInfo &metaInfo7) const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is based on"_t, category(), keyValue("type id", m_typeId)}; + return m_projectStorage->isBasedOn(m_typeId, metaInfo1.m_typeId, metaInfo2.m_typeId, @@ -2330,26 +2680,14 @@ bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo1, } } -namespace { -template<const char *moduleName, const char *typeName> -bool isBasedOnCommonType(NotNullPointer<const ProjectStorageType> projectStorage, TypeId typeId) -{ - if (!typeId) { - return false; - } - - auto base = projectStorage->commonTypeId<moduleName, typeName>(); - - return projectStorage->isBasedOn(typeId, base); -} -} // namespace - bool NodeMetaInfo::isGraphicalItem() const { if constexpr (useProjectStorage()) { - if (!isValid()) { + if (!isValid()) return false; - } + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is graphical item"_t, category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; auto itemId = m_projectStorage->commonTypeId<QtQuick, Item>(); @@ -2369,6 +2707,12 @@ bool NodeMetaInfo::isGraphicalItem() const bool NodeMetaInfo::isQtObject() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is Qt object"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QML, QtObject>(m_projectStorage, m_typeId); } else { @@ -2379,6 +2723,14 @@ bool NodeMetaInfo::isQtObject() const bool NodeMetaInfo::isQtQmlConnections() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is Qt Qml connections"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQml, Connections>(m_projectStorage, m_typeId); } else { @@ -2389,9 +2741,11 @@ bool NodeMetaInfo::isQtQmlConnections() const bool NodeMetaInfo::isLayoutable() const { if constexpr (useProjectStorage()) { - if (!isValid()) { + if (!isValid()) return false; - } + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is layoutable"_t, category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; auto positionerId = m_projectStorage->commonTypeId<QtQuick, Positioner>(); @@ -2410,6 +2764,14 @@ bool NodeMetaInfo::isLayoutable() const bool NodeMetaInfo::isQtQuickLayoutsLayout() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Layouts.Layout"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick_Layouts, Layout>(m_projectStorage, m_typeId); } else { @@ -2420,9 +2782,11 @@ bool NodeMetaInfo::isQtQuickLayoutsLayout() const bool NodeMetaInfo::isView() const { if constexpr (useProjectStorage()) { - if (!isValid()) { + if (!isValid()) return false; - } + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is view"_t, category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; auto listViewId = m_projectStorage->commonTypeId<QtQuick, ListView>(); @@ -2439,17 +2803,20 @@ bool NodeMetaInfo::isView() const bool NodeMetaInfo::usesCustomParser() const { if constexpr (useProjectStorage()) { - return isValid() && typeData().traits.usesCustomParser; + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"uses custom parser"_t, category(), keyValue("type id", m_typeId)}; + + return typeData().traits.usesCustomParser; } else { if (!isValid()) return false; - 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"; } } @@ -2468,8 +2835,14 @@ bool isTypeId(TypeId typeId, TypeIds... otherTypeIds) bool NodeMetaInfo::isVector2D() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is vector2d"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; - return isValid() && isTypeId(m_typeId, m_projectStorage->commonTypeId<QtQuick, vector2d>()); + return isTypeId(m_typeId, m_projectStorage->commonTypeId<QtQuick, vector2d>()); } else { if (!m_privateData) return false; @@ -2483,8 +2856,14 @@ bool NodeMetaInfo::isVector2D() const bool NodeMetaInfo::isVector3D() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is vector3d"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; - return isValid() && isTypeId(m_typeId, m_projectStorage->commonTypeId<QtQuick, vector3d>()); + return isTypeId(m_typeId, m_projectStorage->commonTypeId<QtQuick, vector3d>()); } else { if (!m_privateData) return false; @@ -2498,8 +2877,14 @@ bool NodeMetaInfo::isVector3D() const bool NodeMetaInfo::isVector4D() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is vector4d"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; - return isValid() && isTypeId(m_typeId, m_projectStorage->commonTypeId<QtQuick, vector4d>()); + return isTypeId(m_typeId, m_projectStorage->commonTypeId<QtQuick, vector4d>()); } else { if (!m_privateData) return false; @@ -2513,6 +2898,14 @@ bool NodeMetaInfo::isVector4D() const bool NodeMetaInfo::isQtQuickPropertyChanges() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.PropertyChanges"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick, Storage::Info::PropertyChanges>(m_projectStorage, m_typeId); @@ -2524,6 +2917,14 @@ bool NodeMetaInfo::isQtQuickPropertyChanges() const bool NodeMetaInfo::isQtSafeRendererSafeRendererPicture() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is Qt.SafeRenderer.SafeRendererPicture"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<Qt_SafeRenderer, SafeRendererPicture>(m_projectStorage, m_typeId); } else { @@ -2534,6 +2935,14 @@ bool NodeMetaInfo::isQtSafeRendererSafeRendererPicture() const bool NodeMetaInfo::isQtSafeRendererSafePicture() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is Qt.SafeRenderer.SafePicture"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<Qt_SafeRenderer, SafePicture>(m_projectStorage, m_typeId); } else { @@ -2544,6 +2953,14 @@ bool NodeMetaInfo::isQtSafeRendererSafePicture() const bool NodeMetaInfo::isQtQuickTimelineKeyframe() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Timeline.Keyframe"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick_Timeline, Keyframe>(m_projectStorage, m_typeId); @@ -2555,6 +2972,14 @@ bool NodeMetaInfo::isQtQuickTimelineKeyframe() const bool NodeMetaInfo::isQtQuickTimelineTimelineAnimation() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Timeline.TimelineAnimation"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick_Timeline, TimelineAnimation>(m_projectStorage, m_typeId); } else { @@ -2565,6 +2990,14 @@ bool NodeMetaInfo::isQtQuickTimelineTimelineAnimation() const bool NodeMetaInfo::isQtQuickTimelineTimeline() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Timeline.Timeline"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick_Timeline, Timeline>(m_projectStorage, m_typeId); } else { @@ -2575,6 +3008,14 @@ bool NodeMetaInfo::isQtQuickTimelineTimeline() const bool NodeMetaInfo::isQtQuickTimelineKeyframeGroup() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Timeline.KeyframeGroup"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick_Timeline, KeyframeGroup>(m_projectStorage, m_typeId); } else { @@ -2585,9 +3026,11 @@ bool NodeMetaInfo::isQtQuickTimelineKeyframeGroup() const bool NodeMetaInfo::isListOrGridView() const { if constexpr (useProjectStorage()) { - if (!isValid()) { + if (!isValid()) return false; - } + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is list or grid view"_t, category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; auto listViewId = m_projectStorage->commonTypeId<QtQuick, ListView>(); @@ -2601,9 +3044,11 @@ bool NodeMetaInfo::isListOrGridView() const bool NodeMetaInfo::isNumber() const { if constexpr (useProjectStorage()) { - if (!isValid()) { + if (!isValid()) return false; - } + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is number"_t, category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; auto intId = m_projectStorage->builtinTypeId<int>(); @@ -2624,6 +3069,14 @@ bool NodeMetaInfo::isNumber() const bool NodeMetaInfo::isQtQuickExtrasPicture() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Extras.Picture"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick_Extras, Picture>(m_projectStorage, m_typeId); } else { @@ -2634,6 +3087,12 @@ bool NodeMetaInfo::isQtQuickExtrasPicture() const bool NodeMetaInfo::isQtQuickImage() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Image"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick, Image>(m_projectStorage, m_typeId); @@ -2645,6 +3104,14 @@ bool NodeMetaInfo::isQtQuickImage() const bool NodeMetaInfo::isQtQuickBorderImage() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.BorderImage"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick, BorderImage>(m_projectStorage, m_typeId); @@ -2656,7 +3123,13 @@ bool NodeMetaInfo::isQtQuickBorderImage() const bool NodeMetaInfo::isAlias() const { if constexpr (useProjectStorage()) { - return false; // there is no type alias + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is alias"_t, category(), keyValue("type id", m_typeId)}; + + return false; // all types are already resolved } else { return isValid() && m_privateData->qualfiedTypeName() == "alias"; } @@ -2665,6 +3138,14 @@ bool NodeMetaInfo::isAlias() const bool NodeMetaInfo::isQtQuickPositioner() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Positioner"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick, Positioner>(m_projectStorage, m_typeId); @@ -2676,6 +3157,14 @@ bool NodeMetaInfo::isQtQuickPositioner() const bool NodeMetaInfo::isQtQuickPropertyAnimation() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.PropertyAnimation"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick, PropertyAnimation>(m_projectStorage, m_typeId); } else { @@ -2686,6 +3175,12 @@ bool NodeMetaInfo::isQtQuickPropertyAnimation() const bool NodeMetaInfo::isQtQuickRepeater() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Repeater"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick, Repeater>(m_projectStorage, m_typeId); } else { @@ -2696,6 +3191,14 @@ bool NodeMetaInfo::isQtQuickRepeater() const bool NodeMetaInfo::isQtQuickControlsTabBar() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Controls.TabBar"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick_Controls, TabBar>(m_projectStorage, m_typeId); } else { @@ -2706,6 +3209,14 @@ bool NodeMetaInfo::isQtQuickControlsTabBar() const bool NodeMetaInfo::isQtQuickControlsSwipeView() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Controls.SwipeView"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick_Controls, SwipeView>(m_projectStorage, m_typeId); } else { @@ -2716,6 +3227,12 @@ bool NodeMetaInfo::isQtQuickControlsSwipeView() const bool NodeMetaInfo::isQtQuick3DCamera() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Camera"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick3D, Camera>(m_projectStorage, m_typeId); } else { @@ -2726,6 +3243,14 @@ bool NodeMetaInfo::isQtQuick3DCamera() const bool NodeMetaInfo::isQtQuick3DBakedLightmap() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.BakedLightmap"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick3D, BakedLightmap>(m_projectStorage, m_typeId); } else { @@ -2736,6 +3261,12 @@ bool NodeMetaInfo::isQtQuick3DBakedLightmap() const bool NodeMetaInfo::isQtQuick3DBuffer() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Buffer"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick3D, Buffer>(m_projectStorage, m_typeId); } else { @@ -2746,6 +3277,14 @@ bool NodeMetaInfo::isQtQuick3DBuffer() const bool NodeMetaInfo::isQtQuick3DInstanceListEntry() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.InstanceListEntry"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick3D, InstanceListEntry>(m_projectStorage, m_typeId); } else { @@ -2756,6 +3295,12 @@ bool NodeMetaInfo::isQtQuick3DInstanceListEntry() const bool NodeMetaInfo::isQtQuick3DLight() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Light"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick3D, Light>(m_projectStorage, m_typeId); } else { @@ -2763,9 +3308,17 @@ bool NodeMetaInfo::isQtQuick3DLight() const } } -bool NodeMetaInfo::isQtQuickListElement() const +bool NodeMetaInfo::isQtQmlModelsListElement() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQml.Models.ListElement"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQml_Models, ListElement>(m_projectStorage, m_typeId); } else { @@ -2776,6 +3329,12 @@ bool NodeMetaInfo::isQtQuickListElement() const bool NodeMetaInfo::isQtQuickListModel() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.ListModel"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQml_Models, ListModel>(m_projectStorage, m_typeId); } else { @@ -2786,6 +3345,12 @@ bool NodeMetaInfo::isQtQuickListModel() const bool NodeMetaInfo::isQtQuickListView() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.ListView"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick, ListView>(m_projectStorage, m_typeId); } else { @@ -2793,9 +3358,33 @@ bool NodeMetaInfo::isQtQuickListView() const } } +bool QmlDesigner::NodeMetaInfo::isQtQuickGridView() const +{ + if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.GridView"_t, category(), keyValue("type id", m_typeId)}; + + using namespace Storage::Info; + return isBasedOnCommonType<QtQuick, GridView>(m_projectStorage, m_typeId); + } else { + return isValid() && (isSubclassOf("QtQuick.GridView")); + } +} + bool NodeMetaInfo::isQtQuick3DInstanceList() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.InstanceList"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick3D, InstanceList>(m_projectStorage, m_typeId); } else { @@ -2806,6 +3395,14 @@ bool NodeMetaInfo::isQtQuick3DInstanceList() const bool NodeMetaInfo::isQtQuick3DParticles3DParticle3D() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Particles3D.Particle3D"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick3D_Particles3D, Particle3D>(m_projectStorage, m_typeId); } else { @@ -2816,6 +3413,14 @@ bool NodeMetaInfo::isQtQuick3DParticles3DParticle3D() const bool NodeMetaInfo::isQtQuick3DParticles3DParticleEmitter3D() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Particles3D.ParticleEmitter3D"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick3D_Particles3D, ParticleEmitter3D>(m_projectStorage, m_typeId); @@ -2827,6 +3432,14 @@ bool NodeMetaInfo::isQtQuick3DParticles3DParticleEmitter3D() const bool NodeMetaInfo::isQtQuick3DParticles3DAttractor3D() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Particles3D.Attractor3D"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick3D_Particles3D, Attractor3D>(m_projectStorage, m_typeId); } else { @@ -2837,8 +3450,16 @@ bool NodeMetaInfo::isQtQuick3DParticles3DAttractor3D() const bool NodeMetaInfo::isQtQuick3DParticlesAbstractShape() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Particles3D.AbstractShape"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; - return isBasedOnCommonType<QtQuick3D_Particles3D_cppnative, QQuick3DParticleAbstractShape>( + return isBasedOnCommonType<QtQuick3D_Particles3D, QQuick3DParticleAbstractShape, ModuleKind::CppLibrary>( m_projectStorage, m_typeId); } else { return isValid() && isSubclassOf("QQuick3DParticleAbstractShape"); @@ -2848,6 +3469,12 @@ bool NodeMetaInfo::isQtQuick3DParticlesAbstractShape() const bool NodeMetaInfo::isQtQuickItem() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Item"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick, Item>(m_projectStorage, m_typeId); } else { @@ -2858,6 +3485,12 @@ bool NodeMetaInfo::isQtQuickItem() const bool NodeMetaInfo::isQtQuickPath() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Path"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick, Path>(m_projectStorage, m_typeId); } else { @@ -2868,6 +3501,14 @@ bool NodeMetaInfo::isQtQuickPath() const bool NodeMetaInfo::isQtQuickPauseAnimation() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.PauseAnimation"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick, PauseAnimation>(m_projectStorage, m_typeId); } else { @@ -2878,6 +3519,14 @@ bool NodeMetaInfo::isQtQuickPauseAnimation() const bool NodeMetaInfo::isQtQuickTransition() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Transition"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick, Transition>(m_projectStorage, m_typeId); } else { @@ -2888,6 +3537,14 @@ bool NodeMetaInfo::isQtQuickTransition() const bool NodeMetaInfo::isQtQuickWindowWindow() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Window.Window"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick_Window, Window>(m_projectStorage, m_typeId); } else { @@ -2898,6 +3555,12 @@ bool NodeMetaInfo::isQtQuickWindowWindow() const bool NodeMetaInfo::isQtQuickLoader() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Loader"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick, Loader>(m_projectStorage, m_typeId); } else { @@ -2908,6 +3571,12 @@ bool NodeMetaInfo::isQtQuickLoader() const bool NodeMetaInfo::isQtQuickState() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.State"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick, State>(m_projectStorage, m_typeId); } else { @@ -2918,9 +3587,17 @@ bool NodeMetaInfo::isQtQuickState() const bool NodeMetaInfo::isQtQuickStateOperation() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.StateOperation"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; - return isBasedOnCommonType<QtQuick_cppnative, QQuickStateOperation>(m_projectStorage, - m_typeId); + return isBasedOnCommonType<QtQuick, QQuickStateOperation, ModuleKind::CppLibrary>(m_projectStorage, + m_typeId); } else { return isValid() && isSubclassOf("<cpp>.QQuickStateOperation"); } @@ -2929,6 +3606,12 @@ bool NodeMetaInfo::isQtQuickStateOperation() const bool NodeMetaInfo::isQtQuickText() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Text"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick, Text>(m_projectStorage, m_typeId); } else { @@ -2939,6 +3622,14 @@ bool NodeMetaInfo::isQtQuickText() const bool NodeMetaInfo::isQtMultimediaSoundEffect() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtMultimedia.SoundEffect"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtMultimedia, SoundEffect>(m_projectStorage, m_typeId); } else { @@ -2949,9 +3640,11 @@ bool NodeMetaInfo::isQtMultimediaSoundEffect() const bool NodeMetaInfo::isFlowViewItem() const { if constexpr (useProjectStorage()) { - if (!isValid()) { + if (!isValid()) return false; - } + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is FlowView.ViewItem"_t, category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; auto flowItemId = m_projectStorage->commonTypeId<FlowView, FlowItem>(); @@ -2968,6 +3661,12 @@ bool NodeMetaInfo::isFlowViewItem() const bool NodeMetaInfo::isFlowViewFlowItem() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is FlowView.FlowItem"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<FlowView, FlowItem>(m_projectStorage, m_typeId); } else { @@ -2978,6 +3677,12 @@ bool NodeMetaInfo::isFlowViewFlowItem() const bool NodeMetaInfo::isFlowViewFlowView() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is FlowView.FlowView"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<FlowView, FlowView>(m_projectStorage, m_typeId); } else { @@ -2998,6 +3703,14 @@ bool NodeMetaInfo::isFlowViewFlowActionArea() const bool NodeMetaInfo::isFlowViewFlowTransition() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is FlowView.FlowTransition"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<FlowView, FlowTransition>(m_projectStorage, m_typeId); } else { @@ -3008,6 +3721,14 @@ bool NodeMetaInfo::isFlowViewFlowTransition() const bool NodeMetaInfo::isFlowViewFlowDecision() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is FlowView.FlowDecision"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<FlowView, FlowDecision>(m_projectStorage, m_typeId); } else { @@ -3018,6 +3739,14 @@ bool NodeMetaInfo::isFlowViewFlowDecision() const bool NodeMetaInfo::isFlowViewFlowWildcard() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is FlowView.FlowWildcard"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<FlowView, FlowWildcard>(m_projectStorage, m_typeId); } else { @@ -3028,6 +3757,14 @@ bool NodeMetaInfo::isFlowViewFlowWildcard() const bool NodeMetaInfo::isQtQuickStudioComponentsGroupItem() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Studio.Components.GroupItem"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick_Studio_Components, GroupItem>(m_projectStorage, m_typeId); } else { @@ -3038,6 +3775,14 @@ bool NodeMetaInfo::isQtQuickStudioComponentsGroupItem() const bool NodeMetaInfo::isQtQuickStudioUtilsJsonListModel() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Studio.Utils.JsonListModel"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick_Studio_Components, JsonListModel>(m_projectStorage, m_typeId); @@ -3049,6 +3794,12 @@ bool NodeMetaInfo::isQtQuickStudioUtilsJsonListModel() const bool NodeMetaInfo::isQmlComponent() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QML.Component"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QML, Component>(m_projectStorage, m_typeId); } else { @@ -3064,6 +3815,12 @@ bool NodeMetaInfo::isQmlComponent() const bool NodeMetaInfo::isFont() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is font"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isValid() && isTypeId(m_typeId, m_projectStorage->commonTypeId<QtQuick, font>()); } else { @@ -3074,6 +3831,12 @@ bool NodeMetaInfo::isFont() const bool NodeMetaInfo::isColor() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is color"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isValid() && isTypeId(m_typeId, m_projectStorage->builtinTypeId<QColor>()); } else { @@ -3089,6 +3852,12 @@ bool NodeMetaInfo::isColor() const bool NodeMetaInfo::isBool() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is bool"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isValid() && isTypeId(m_typeId, m_projectStorage->builtinTypeId<bool>()); } else { @@ -3104,6 +3873,12 @@ bool NodeMetaInfo::isBool() const bool NodeMetaInfo::isInteger() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is integer"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isValid() && isTypeId(m_typeId, m_projectStorage->builtinTypeId<int>()); } else { @@ -3119,9 +3894,11 @@ bool NodeMetaInfo::isInteger() const bool NodeMetaInfo::isFloat() const { if constexpr (useProjectStorage()) { - if (!isValid()) { + if (!isValid()) return false; - } + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is float"_t, category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; auto floatId = m_projectStorage->builtinTypeId<float>(); @@ -3141,6 +3918,12 @@ bool NodeMetaInfo::isFloat() const bool NodeMetaInfo::isVariant() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is variant"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isValid() && isTypeId(m_typeId, m_projectStorage->builtinTypeId<QVariant>()); } else { @@ -3156,6 +3939,12 @@ bool NodeMetaInfo::isVariant() const bool NodeMetaInfo::isString() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is string"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isValid() && isTypeId(m_typeId, m_projectStorage->builtinTypeId<QString>()); } else { @@ -3171,6 +3960,12 @@ bool NodeMetaInfo::isString() const bool NodeMetaInfo::isUrl() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is url"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isValid() && isTypeId(m_typeId, m_projectStorage->builtinTypeId<QUrl>()); } else { @@ -3186,6 +3981,12 @@ bool NodeMetaInfo::isUrl() const bool NodeMetaInfo::isQtQuick3DTexture() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Texture"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick3D, Texture>(m_projectStorage, m_typeId); } else { @@ -3197,6 +3998,12 @@ bool NodeMetaInfo::isQtQuick3DTexture() const bool NodeMetaInfo::isQtQuick3DShader() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Shader"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick3D, Shader>(m_projectStorage, m_typeId); } else { @@ -3207,6 +4014,12 @@ bool NodeMetaInfo::isQtQuick3DShader() const bool NodeMetaInfo::isQtQuick3DPass() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Pass"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick3D, Pass>(m_projectStorage, m_typeId); } else { @@ -3217,6 +4030,12 @@ bool NodeMetaInfo::isQtQuick3DPass() const bool NodeMetaInfo::isQtQuick3DCommand() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Command"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick3D, Command>(m_projectStorage, m_typeId); } else { @@ -3227,6 +4046,14 @@ bool NodeMetaInfo::isQtQuick3DCommand() const bool NodeMetaInfo::isQtQuick3DDefaultMaterial() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.DefaultMaterial"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick3D, DefaultMaterial>(m_projectStorage, m_typeId); } else { @@ -3247,6 +4074,12 @@ bool NodeMetaInfo::isQtQuick3DMaterial() const bool NodeMetaInfo::isQtQuick3DModel() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Model"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick3D, Storage::Info::Model>(m_projectStorage, m_typeId); } else { @@ -3257,6 +4090,12 @@ bool NodeMetaInfo::isQtQuick3DModel() const bool NodeMetaInfo::isQtQuick3DNode() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Node"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick3D, Node>(m_projectStorage, m_typeId); } else { @@ -3267,6 +4106,14 @@ bool NodeMetaInfo::isQtQuick3DNode() const bool NodeMetaInfo::isQtQuick3DParticles3DAffector3D() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Particles3D.Affector3D"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick3D_Particles3D, Affector3D>(m_projectStorage, m_typeId); } else { @@ -3277,6 +4124,12 @@ bool NodeMetaInfo::isQtQuick3DParticles3DAffector3D() const bool NodeMetaInfo::isQtQuick3DView3D() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.View3D"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick3D, View3D>(m_projectStorage, m_typeId); } else { @@ -3287,6 +4140,14 @@ bool NodeMetaInfo::isQtQuick3DView3D() const bool NodeMetaInfo::isQtQuick3DPrincipledMaterial() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.PrincipledMaterial"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick3D, PrincipledMaterial>(m_projectStorage, m_typeId); } else { @@ -3297,6 +4158,14 @@ bool NodeMetaInfo::isQtQuick3DPrincipledMaterial() const bool NodeMetaInfo::isQtQuick3DSpecularGlossyMaterial() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.SpecularGlossyMaterial"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick3D, SpecularGlossyMaterial>(m_projectStorage, m_typeId); } else { @@ -3307,6 +4176,14 @@ bool NodeMetaInfo::isQtQuick3DSpecularGlossyMaterial() const bool NodeMetaInfo::isQtQuick3DParticles3DSpriteParticle3D() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Particles3D.SpriteParticle3D"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick3D_Particles3D, SpriteParticle3D>(m_projectStorage, m_typeId); @@ -3318,6 +4195,14 @@ bool NodeMetaInfo::isQtQuick3DParticles3DSpriteParticle3D() const bool NodeMetaInfo::isQtQuick3DTextureInput() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.TextureInput"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick3D, TextureInput>(m_projectStorage, m_typeId); } else { @@ -3328,6 +4213,14 @@ bool NodeMetaInfo::isQtQuick3DTextureInput() const bool NodeMetaInfo::isQtQuick3DCubeMapTexture() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.CubeMapTexture"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick3D, CubeMapTexture>(m_projectStorage, m_typeId); } else { @@ -3340,6 +4233,14 @@ bool NodeMetaInfo::isQtQuick3DCubeMapTexture() const bool NodeMetaInfo::isQtQuick3DSceneEnvironment() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.SceneEnvironment"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick3D, SceneEnvironment>(m_projectStorage, m_typeId); } else { @@ -3350,6 +4251,12 @@ bool NodeMetaInfo::isQtQuick3DSceneEnvironment() const bool NodeMetaInfo::isQtQuick3DEffect() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Effect"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType<QtQuick3D, Effect>(m_projectStorage, m_typeId); } else { @@ -3359,8 +4266,15 @@ bool NodeMetaInfo::isQtQuick3DEffect() const bool NodeMetaInfo::isEnumeration() const { - if constexpr (useProjectStorage()) - return isValid() && typeData().traits.isEnum; + if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is enumeration"_t, category(), keyValue("type id", m_typeId)}; + + return typeData().traits.isEnum; + } return false; } @@ -3385,8 +4299,15 @@ PropertyMetaInfo::~PropertyMetaInfo() = default; NodeMetaInfo PropertyMetaInfo::propertyType() const { if constexpr (useProjectStorage()) { - if (isValid()) - return {propertyData().propertyTypeId, m_projectStorage}; + if (!isValid()) + return {}; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get property type"_t, + category(), + keyValue("property declaration id", m_id)}; + + return {propertyData().propertyTypeId, m_projectStorage}; } else { if (isValid()) return NodeMetaInfo{nodeMetaInfoPrivateData()->model(), @@ -3401,8 +4322,15 @@ NodeMetaInfo PropertyMetaInfo::propertyType() const NodeMetaInfo PropertyMetaInfo::type() const { if constexpr (useProjectStorage()) { - if (isValid()) - return NodeMetaInfo(propertyData().typeId, m_projectStorage); + if (!isValid()) + return {}; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get property owner type "_t, + category(), + keyValue("property declaration id", m_id)}; + + return NodeMetaInfo(propertyData().typeId, m_projectStorage); } return {}; @@ -3410,59 +4338,121 @@ NodeMetaInfo PropertyMetaInfo::type() const PropertyName PropertyMetaInfo::name() const { - if (isValid()) { - if constexpr (useProjectStorage()) - return PropertyName(Utils::SmallStringView(propertyData().name)); - else - return propertyName(); - } + if (!isValid()) + return {}; - return {}; + if constexpr (useProjectStorage()) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get property name"_t, + category(), + keyValue("property declaration id", m_id)}; + + return PropertyName(Utils::SmallStringView(propertyData().name)); + } else { + return propertyName(); + } } bool PropertyMetaInfo::isWritable() const { - if constexpr (useProjectStorage()) - return isValid() && !(propertyData().traits & Storage::PropertyDeclarationTraits::IsReadOnly); - else + if constexpr (useProjectStorage()) { + if (!isValid()) + return {}; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is property writable"_t, + category(), + keyValue("property declaration id", m_id)}; + + return !(propertyData().traits & Storage::PropertyDeclarationTraits::IsReadOnly); + } else { return isValid() && nodeMetaInfoPrivateData()->isPropertyWritable(propertyName()); + } } bool PropertyMetaInfo::isReadOnly() const { - return !isWritable(); + if constexpr (useProjectStorage()) { + if (!isValid()) + return {}; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is property read only"_t, + category(), + keyValue("property declaration id", m_id)}; + + return propertyData().traits & Storage::PropertyDeclarationTraits::IsReadOnly; + } else { + return !isWritable(); + } } bool PropertyMetaInfo::isListProperty() const { - if constexpr (useProjectStorage()) - return isValid() && propertyData().traits & Storage::PropertyDeclarationTraits::IsList; - else + if constexpr (useProjectStorage()) { + if (!isValid()) + return {}; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is list property"_t, + category(), + keyValue("property declaration id", m_id)}; + + return propertyData().traits & Storage::PropertyDeclarationTraits::IsList; + } else { return isValid() && nodeMetaInfoPrivateData()->isPropertyList(propertyName()); + } } bool PropertyMetaInfo::isEnumType() const { - if constexpr (useProjectStorage()) + if constexpr (useProjectStorage()) { + if (!isValid()) + return {}; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is enum type"_t, + category(), + keyValue("property has enumeration type", m_id)}; + return propertyType().isEnumeration(); - else + } else { return isValid() && nodeMetaInfoPrivateData()->isPropertyEnum(propertyName()); + } } bool PropertyMetaInfo::isPrivate() const { - if constexpr (useProjectStorage()) + if constexpr (useProjectStorage()) { + if (!isValid()) + return {}; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is private property"_t, + category(), + keyValue("property declaration id", m_id)}; + return isValid() && propertyData().name.startsWith("__"); - else + } else { return isValid() && propertyName().startsWith("__"); + } } bool PropertyMetaInfo::isPointer() const { - if constexpr (useProjectStorage()) + if constexpr (useProjectStorage()) { + if (!isValid()) + return {}; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is pointer property"_t, + category(), + keyValue("property declaration id", m_id)}; + return isValid() && (propertyData().traits & Storage::PropertyDeclarationTraits::IsPointer); - else + } else { return isValid() && nodeMetaInfoPrivateData()->isPropertyPointer(propertyName()); + } } namespace { @@ -3479,6 +4469,11 @@ QVariant PropertyMetaInfo::castedValue(const QVariant &value) const return {}; if constexpr (!useProjectStorage()) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"cast value"_t, + category(), + keyValue("property declaration id", m_id)}; + const QVariant variant = value; QVariant copyVariant = variant; const TypeName &typeName = propertyTypeName(); @@ -3496,7 +4491,7 @@ QVariant PropertyMetaInfo::castedValue(const QVariant &value) const return variant; } else if (typeId == QVariant::UserType && typeName == "var") { return variant; - } else if (variant.typeId() == QVariant::List) { + } else if (variant.typeId() == QMetaType::QVariantList) { // TODO: check the contents of the list return variant; } else if (typeName == "var" || typeName == "variant") { diff --git a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp index 403731d1c4..29a093f199 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; } @@ -344,11 +345,8 @@ void SubComponentManager::unregisterQmlFile(const QFileInfo &fileInfo, const QSt void SubComponentManager::registerQmlFile(const QFileInfo &fileInfo, const QString &qualifier, bool addToLibrary) { - if (!addToLibrary || !model() - || fileInfo.path().contains(QLatin1String(Constants::QUICK_3D_ASSETS_FOLDER)) - || fileInfo.path().contains(QLatin1String(Constants::DEFAULT_EFFECTS_IMPORT_FOLDER))) { + if (!addToLibrary || !model() || m_componentUtils.isGeneratedPath(fileInfo.path())) return; - } QString componentName = fileInfo.baseName(); const QString baseComponentName = componentName; @@ -395,7 +393,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 +458,8 @@ 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.import3dTypePath(); + const QString assetPath = impPath + '/' + path3d; if (QFileInfo::exists(assetPath)) retPaths << assetPath; } @@ -520,7 +519,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.import3dTypePath(); for (const auto &watchPath : watchPaths) { if (watchPath.endsWith(quick3DAssetFolder)) m_watcher.removePath(watchPath); @@ -580,7 +579,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/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp index 061ab8ae2b..125c7195a8 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 diff --git a/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp b/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp index 141548047e..23c17dc61a 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."; diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index a4cd31b2a8..d4eea26378 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -9,7 +9,6 @@ #include "../projectstorage/sourcepath.h" #include "../projectstorage/sourcepathcache.h" #include "abstractview.h" -#include "auxiliarydataproperties.h" #include "internalbindingproperty.h" #include "internalnodeabstractproperty.h" #include "internalnodelistproperty.h" @@ -33,6 +32,8 @@ #include "signalhandlerproperty.h" #include "variantproperty.h" +#include <uniquename.h> + #include <projectstorage/projectstorage.h> #include <qmljs/qmljsmodelmanagerinterface.h> @@ -46,8 +47,6 @@ #include <QRegularExpression> #include <qcompilerdetection.h> -#include <string> - /*! \defgroup CoreModel */ @@ -170,10 +169,10 @@ Storage::Imports createStorageImports(const Imports &imports, SourceId fileId) { return Utils::transform<Storage::Imports>(imports, [&](const Import &import) { - return Storage::Import{projectStorage.moduleId(Utils::SmallString{import.url()}), - import.majorVersion(), - import.minorVersion(), - fileId}; + using Storage::ModuleKind; + auto moduleKind = import.isLibraryImport() ? ModuleKind::QmlLibrary : ModuleKind::PathLibrary; + auto moduleId = projectStorage.moduleId(Utils::SmallString{import.url()}, moduleKind); + return Storage::Import{moduleId, import.majorVersion(), import.minorVersion(), fileId}; }); } @@ -244,7 +243,7 @@ void ModelPrivate::notifyUsedImportsChanged(const Imports &usedImports) } } -QUrl ModelPrivate::fileUrl() const +const QUrl &ModelPrivate::fileUrl() const { return m_fileUrl; } @@ -390,7 +389,11 @@ ImportedTypeNameId ModelPrivate::importedTypeNameId(Utils::SmallStringView typeN return import.alias() == aliasName; }); if (found != m_imports.end()) { - ModuleId moduleId = projectStorage->moduleId(Utils::PathString{found->url()}); + using Storage::ModuleKind; + auto moduleKind = found->isLibraryImport() ? ModuleKind::QmlLibrary + : ModuleKind::PathLibrary; + ModuleId moduleId = projectStorage->moduleId(Utils::PathString{found->url()}, + moduleKind); ImportId importId = projectStorage->importId( Storage::Import{moduleId, found->majorVersion(), found->minorVersion(), m_sourceId}); return projectStorage->importedTypeNameId(importId, shortTypeName); @@ -1758,14 +1761,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 +1784,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 +1796,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 +1810,7 @@ void Model::setUsedImports(Imports usedImports) d->notifyUsedImportsChanged(d->m_usedImportList); } } +#endif static bool compareVersions(const Import &import1, const Import &import2, bool allowHigherVersion) { @@ -1848,89 +1863,18 @@ bool Model::hasImport(const QString &importUrl) const }); } -static QString firstCharToLower(const QString &string) -{ - QString resultString = string; - - if (!resultString.isEmpty()) - resultString[0] = resultString.at(0).toLower(); - - return resultString; -} - -QString Model::generateNewId(const QString &prefixName, - const QString &fallbackPrefix, - std::optional<std::function<bool(const QString &)>> isDuplicate) const +QString Model::generateNewId(const QString &prefixName, const QString &fallbackPrefix) const { - // First try just the prefixName without number as postfix, then continue with 2 and further - // as postfix until id does not already exist. - // Properties of the root node are not allowed for ids, because they are available in the - // complete context without qualification. - - int counter = 0; - - QString newBaseId = QStringView(u"%1").arg(firstCharToLower(prefixName)); - newBaseId.remove(QRegularExpression(QStringLiteral("[^a-zA-Z0-9_]"))); - - if (!newBaseId.isEmpty()) { - QChar firstChar = newBaseId.at(0); - if (firstChar.isDigit()) - newBaseId.prepend('_'); - } else { - newBaseId = fallbackPrefix; - } - - QString newId = newBaseId; + QString newId = prefixName; - if (!isDuplicate.has_value()) - isDuplicate = std::bind(&Model::hasId, this, std::placeholders::_1); + if (newId.isEmpty()) + newId = fallbackPrefix; - while (!ModelNode::isValidId(newId) || isDuplicate.value()(newId) - || d->rootNode()->property(newId.toUtf8())) { - ++counter; - newId = QStringView(u"%1%2").arg(firstCharToLower(newBaseId)).arg(counter); - } - - return newId; -} - -// Generate a unique camelCase id from a name -// note: this methods does the same as generateNewId(). The 2 methods should be merged into one -QString Model::generateIdFromName(const QString &name, const QString &fallbackId) const -{ - QString newId; - if (name.isEmpty()) { - newId = fallbackId; - } else { - // convert to camel case - QStringList nameWords = name.split(" "); - nameWords[0] = nameWords[0].at(0).toLower() + nameWords[0].mid(1); - for (int i = 1; i < nameWords.size(); ++i) - nameWords[i] = nameWords[i].at(0).toUpper() + nameWords[i].mid(1); - newId = nameWords.join(""); - - // if id starts with a number prepend an underscore - if (newId.at(0).isDigit()) - newId.prepend('_'); - } - - // If the new id is not valid (e.g. qml keyword match), try fixing it by prepending underscore - if (!ModelNode::isValidId(newId)) - newId.prepend("_"); - - QRegularExpression rgx("\\d+$"); // matches a number at the end of a string - while (hasId(newId)) { // id exists - QRegularExpressionMatch match = rgx.match(newId); - if (match.hasMatch()) { // ends with a number, increment it - QString numStr = match.captured(); - int num = numStr.toInt() + 1; - newId = newId.mid(0, match.capturedStart()) + QString::number(num); - } else { - newId.append('1'); - } - } - - return newId; + return UniqueName::generateId(prefixName, [&] (const QString &id) { + // Properties of the root node are not allowed for ids, because they are available in the + // complete context without qualification. + return hasId(id) || d->rootNode()->property(id.toUtf8()); + }); } void Model::startDrag(QMimeData *mimeData, const QPixmap &icon) @@ -2021,14 +1965,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()) @@ -2110,7 +2046,7 @@ void Model::clearMetaInfoCache() \brief Returns the URL against which relative URLs within the model should be resolved. \return The base URL. */ -QUrl Model::fileUrl() const +const QUrl &Model::fileUrl() const { return d->fileUrl(); } @@ -2205,6 +2141,16 @@ NodeMetaInfo Model::qtQmlModelsListElementMetaInfo() const } } +NodeMetaInfo Model::qtQmlXmlListModelXmlListModelRoleMetaInfo() const +{ + if constexpr (useProjectStorage()) { + using namespace Storage::Info; + return createNodeMetaInfo<QtQml_XmlListModel, XmlListModelRole>(); + } else { + return metaInfo("QtQml.XmlListModel.XmlListModelRole"); + } +} + NodeMetaInfo Model::qmlQtObjectMetaInfo() const { if constexpr (useProjectStorage()) { @@ -2541,8 +2487,7 @@ QList<ItemLibraryEntry> Model::itemLibraryEntries() const { #ifdef QDS_USE_PROJECTSTORAGE using namespace Storage::Info; - return toItemLibraryEntries(d->projectStorage->itemLibraryEntries(d->m_sourceId), - *d->projectStorage); + return toItemLibraryEntries(d->projectStorage->itemLibraryEntries(d->m_sourceId)); #else return d->metaInfo().itemLibraryInfo()->entries(); #endif @@ -2608,11 +2553,10 @@ MetaInfo Model::metaInfo() } #endif -Module Model::module(Utils::SmallStringView moduleName) +Module Model::module(Utils::SmallStringView moduleName, Storage::ModuleKind moduleKind) { - if constexpr (useProjectStorage()) { - return Module(d->projectStorage->moduleId(moduleName), d->projectStorage); - } + if constexpr (useProjectStorage()) + return Module(d->projectStorage->moduleId(moduleName, moduleKind), d->projectStorage); return {}; } diff --git a/src/plugins/qmldesigner/designercore/model/model_p.h b/src/plugins/qmldesigner/designercore/model/model_p.h index a3e972f329..cb082fd1d7 100644 --- a/src/plugins/qmldesigner/designercore/model/model_p.h +++ b/src/plugins/qmldesigner/designercore/model/model_p.h @@ -122,7 +122,7 @@ public: ModelPrivate(const ModelPrivate &) = delete; ModelPrivate &operator=(const ModelPrivate &) = delete; - QUrl fileUrl() const; + const QUrl &fileUrl() const; void setFileUrl(const QUrl &url); InternalNodePointer createNode(const TypeName &typeName, diff --git a/src/plugins/qmldesigner/designercore/model/modelresourcemanagement.cpp b/src/plugins/qmldesigner/designercore/model/modelresourcemanagement.cpp index a61f1001f9..27ac2e67ab 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/modelutils.cpp b/src/plugins/qmldesigner/designercore/model/modelutils.cpp index cb3f482289..6c3e1ea50f 100644 --- a/src/plugins/qmldesigner/designercore/model/modelutils.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelutils.cpp @@ -9,6 +9,8 @@ #include <projectstorage/projectstorage.h> #include <projectstorage/sourcepathcache.h> +#include <coreplugin/messagebox.h> + #include <utils/expected.h> #include <utils/ranges.h> @@ -107,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/designercore/model/propertycontainer.cpp b/src/plugins/qmldesigner/designercore/model/propertycontainer.cpp index cd7cebbb73..8eaa7947de 100644 --- a/src/plugins/qmldesigner/designercore/model/propertycontainer.cpp +++ b/src/plugins/qmldesigner/designercore/model/propertycontainer.cpp @@ -41,7 +41,7 @@ PropertyName PropertyContainer::name() const QVariant PropertyContainer::value() const { - if (m_value.typeId() == QVariant::String) + if (m_value.typeId() == QMetaType::QString) m_value = PropertyParser::read(m_type, m_value.toString()); return m_value; } diff --git a/src/plugins/qmldesigner/designercore/model/propertyparser.cpp b/src/plugins/qmldesigner/designercore/model/propertyparser.cpp index 788530c291..264c944fea 100644 --- a/src/plugins/qmldesigner/designercore/model/propertyparser.cpp +++ b/src/plugins/qmldesigner/designercore/model/propertyparser.cpp @@ -201,7 +201,7 @@ QVariant read(const QString &typeStr, const QString &str, const MetaInfo &) QVariant read(const QString &typeStr, const QString &str) { - int type = QMetaType::type(typeStr.toUtf8().constData()); + int type = QMetaType::fromName(typeStr.toUtf8().constData()).id(); if (type == 0) { if (typeStr != "binding"_L1 && typeStr != "enum"_L1) { qWarning() << "Type " << typeStr @@ -270,15 +270,14 @@ QVariant read(int variantType, const QString &str) value = QVariant::fromValue<Enumeration>(enumerationFromString(str, &conversionOk)); } else { value = QVariant(str); - value.convert(static_cast<QVariant::Type>(variantType)); + value.convert(QMetaType(variantType)); } break; } } if (!conversionOk) { - qWarning() << "Could not convert" << str - << "to" << QMetaType::typeName(variantType); + qWarning() << "Could not convert" << str << "to" << QMetaType(variantType).name(); value = QVariant(str); } diff --git a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp index 826856428b..6e3b739096 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 <model.h> #include <abstractview.h> +#include <generatedcomponentutils.h> +#include <model.h> #include <coreplugin/icore.h> @@ -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}, {}); @@ -748,7 +751,6 @@ void QmlFlowActionAreaNode::assignTargetFlowItem(const QmlFlowTargetNode &flowIt ModelNode transition = flowView.addTransition(flowParent.modelNode(), flowItem.modelNode()); - modelNode().bindingProperty("target").setExpression(transition.validId()); } diff --git a/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp index c84f234257..726d3d52af 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp @@ -24,6 +24,8 @@ #include <QDir> #include <QRandomGenerator> +#include <memory> + namespace QmlDesigner { static char imagePlaceHolder[] = "qrc:/qtquickplugin/images/template_image.png"; @@ -182,8 +184,8 @@ void QmlVisualNode::scatter(const ModelNode &targetNode, const std::optional<int if (!scatter) return; - if (offset.has_value()) { // offset - double offsetValue = offset.value(); + if (offset) { // offset + double offsetValue = *offset; this->translate(QVector3D(offsetValue, offsetValue, offsetValue)); } else { // scatter in range const double scatterRange = 20.; @@ -250,8 +252,7 @@ QmlObjectNode QmlVisualNode::createQmlObjectNode(AbstractView *view, NodeAbstractProperty parentProperty = parentQmlItemNode.defaultNodeAbstractProperty(); - - NodeHints hints = NodeHints::fromItemLibraryEntry(itemLibraryEntry); + NodeHints hints = NodeHints::fromItemLibraryEntry(itemLibraryEntry, view->model()); const PropertyName forceNonDefaultProperty = hints.forceNonDefaultProperty().toUtf8(); QmlObjectNode newNode = QmlItemNode::createQmlObjectNode(view, @@ -289,17 +290,17 @@ static QmlObjectNode createQmlObjectNodeFromSource(AbstractView *view, textEdit.setPlainText(source); NotIndentingTextEditModifier modifier(&textEdit); - QScopedPointer<RewriterView> rewriterView( - new RewriterView(view->externalDependencies(), RewriterView::Amend)); + std::unique_ptr<RewriterView> rewriterView = std::make_unique<RewriterView>( + view->externalDependencies(), RewriterView::Amend); rewriterView->setCheckSemanticErrors(false); rewriterView->setTextModifier(&modifier); rewriterView->setAllowComponentRoot(true); rewriterView->setPossibleImportsEnabled(false); - inputModel->setRewriterView(rewriterView.data()); + inputModel->setRewriterView(rewriterView.get()); if (rewriterView->errors().isEmpty() && rewriterView->rootModelNode().isValid()) { ModelNode rootModelNode = rewriterView->rootModelNode(); - inputModel->detachView(rewriterView.data()); + inputModel->detachView(rewriterView.get()); QmlVisualNode(rootModelNode).setPosition(position); ModelMerger merger(view); return merger.insertModel(rootModelNode); @@ -329,7 +330,7 @@ QmlObjectNode QmlVisualNode::createQmlObjectNode(AbstractView *view, { QmlObjectNode newQmlObjectNode; - NodeHints hints = NodeHints::fromItemLibraryEntry(itemLibraryEntry); + NodeHints hints = NodeHints::fromItemLibraryEntry(itemLibraryEntry, view->model()); auto createNodeFunc = [=, &newQmlObjectNode, &parentProperty]() { #ifndef QDS_USE_PROJECTSTORAGE @@ -361,13 +362,17 @@ QmlObjectNode QmlVisualNode::createQmlObjectNode(AbstractView *view, propertyPairList.append(position.propertyPairList()); ModelNode::NodeSourceType nodeSourceType = ModelNode::NodeWithoutSource; - if (itemLibraryEntry.typeName() == "QtQml.Component") - nodeSourceType = ModelNode::NodeWithComponentSource; #ifdef QDS_USE_PROJECTSTORAGE + NodeMetaInfo metaInfo{itemLibraryEntry.typeId(), view->model()->projectStorage()}; + if (metaInfo.isQmlComponent()) + nodeSourceType = ModelNode::NodeWithComponentSource; newQmlObjectNode = QmlObjectNode(view->createModelNode( itemLibraryEntry.typeName(), propertyPairList, {}, {}, nodeSourceType)); #else + if (itemLibraryEntry.typeName() == "QtQml.Component") + nodeSourceType = ModelNode::NodeWithComponentSource; + newQmlObjectNode = QmlObjectNode(view->createModelNode(itemLibraryEntry.typeName(), majorVersion, minorVersion, diff --git a/src/plugins/qmldesigner/designercore/model/rewriteactioncompressor.cpp b/src/plugins/qmldesigner/designercore/model/rewriteactioncompressor.cpp index c4d96bb250..edee6840ef 100644 --- a/src/plugins/qmldesigner/designercore/model/rewriteactioncompressor.cpp +++ b/src/plugins/qmldesigner/designercore/model/rewriteactioncompressor.cpp @@ -58,7 +58,6 @@ void RewriteActionCompressor::compressImports(QList<RewriteAction *> &actions) c actionsToRemove.append(action); actionsToRemove.append(addImportAction); addedImports.remove(import); - delete addImportAction; } else { removedImports.insert(import, action); } @@ -67,13 +66,11 @@ void RewriteActionCompressor::compressImports(QList<RewriteAction *> &actions) c if (RewriteAction *duplicateAction = addedImports.value(import, 0)) { actionsToRemove.append(duplicateAction); addedImports.remove(import); - delete duplicateAction; addedImports.insert(import, action); } else if (RewriteAction *removeAction = removedImports.value(import, 0)) { actionsToRemove.append(action); actionsToRemove.append(removeAction); removedImports.remove(import); - delete removeAction; } else { addedImports.insert(import, action); } diff --git a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp index 4ae2261e60..3c481573d2 100644 --- a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp +++ b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp @@ -16,12 +16,14 @@ #include <filemanager/objectlengthcalculator.h> #include <modelnode.h> #include <modelnodepositionstorage.h> +#include <nodemetainfo.h> #include <nodeproperty.h> +#include <projectstorage/projectstorage.h> +#include <qmlobjectnode.h> +#include <qmltimelinekeyframegroup.h> #include <rewritingexception.h> #include <signalhandlerproperty.h> #include <variantproperty.h> -#include <qmlobjectnode.h> -#include <qmltimelinekeyframegroup.h> #include <qmljs/parser/qmljsengine_p.h> #include <qmljs/qmljsmodelmanagerinterface.h> @@ -56,9 +58,9 @@ RewriterView::RewriterView(ExternalDependenciesInterface &externalDependencies, DifferenceHandling differenceHandling) : AbstractView{externalDependencies} , m_differenceHandling(differenceHandling) - , m_positionStorage(new ModelNodePositionStorage) - , m_modelToTextMerger(new Internal::ModelToTextMerger(this)) - , m_textToModelMerger(new Internal::TextToModelMerger(this)) + , m_positionStorage(std::make_unique<ModelNodePositionStorage>()) + , m_modelToTextMerger(std::make_unique<Internal::ModelToTextMerger>(this)) + , m_textToModelMerger(std::make_unique<Internal::TextToModelMerger>(this)) { m_amendTimer.setSingleShot(true); @@ -78,12 +80,12 @@ RewriterView::~RewriterView() = default; Internal::ModelToTextMerger *RewriterView::modelToTextMerger() const { - return m_modelToTextMerger.data(); + return m_modelToTextMerger.get(); } Internal::TextToModelMerger *RewriterView::textToModelMerger() const { - return m_textToModelMerger.data(); + return m_textToModelMerger.get(); } void RewriterView::modelAttached(Model *model) @@ -92,7 +94,7 @@ void RewriterView::modelAttached(Model *model) AbstractView::modelAttached(model); - ModelAmender differenceHandler(m_textToModelMerger.data()); + ModelAmender differenceHandler(m_textToModelMerger.get()); const QString qmlSource = m_textModifier->text(); if (m_textToModelMerger->load(qmlSource, differenceHandler)) m_lastCorrectQmlSource = qmlSource; @@ -104,6 +106,7 @@ void RewriterView::modelAttached(Model *model) m_modelAttachPending = true; QTimer::singleShot(1000, this, [this, model](){ modelAttached(model); + restoreAuxiliaryData(); }); } } @@ -492,7 +495,7 @@ void RewriterView::amendQmlText() const QString newQmlText = m_textModifier->text(); - ModelAmender differenceHandler(m_textToModelMerger.data()); + ModelAmender differenceHandler(m_textToModelMerger.get()); if (m_textToModelMerger->load(newQmlText, differenceHandler)) m_lastCorrectQmlSource = newQmlText; emitCustomNotification(EndRewriterAmend); @@ -593,7 +596,7 @@ QString RewriterView::auxiliaryDataAsQML() const hasAuxData = true; QString strValue = value.toString(); - auto metaType = static_cast<QMetaType::Type>(value.type()); + const int metaType = value.typeId(); if (metaType == QMetaType::QString || metaType == QMetaType::QColor) { @@ -698,7 +701,7 @@ void RewriterView::forceAmend() Internal::ModelNodePositionStorage *RewriterView::positionStorage() const { - return m_positionStorage.data(); + return m_positionStorage.get(); } QList<DocumentMessage> RewriterView::warnings() const @@ -755,7 +758,7 @@ void RewriterView::resetToLastCorrectQml() { m_textModifier->textDocument()->undo(); m_textModifier->textDocument()->clearUndoRedoStacks(QTextDocument::RedoStack); - ModelAmender differenceHandler(m_textToModelMerger.data()); + ModelAmender differenceHandler(m_textToModelMerger.get()); Internal::WriteLocker::unlock(model()); m_textToModelMerger->load(m_textModifier->text(), differenceHandler); Internal::WriteLocker::lock(model()); @@ -933,10 +936,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,38 +994,74 @@ QString RewriterView::convertTypeToImportAlias(const QString &type) const return result; } -QString RewriterView::pathForImport(const Import &import) +QStringList RewriterView::importDirectories() const { - if (scopeChain() && scopeChain()->context() && document()) { - const QString importStr = import.isFileImport() ? import.file() : import.url(); - const QmlJS::Imports *imports = scopeChain()->context()->imports(document()); + const QList<Utils::FilePath> list(m_textToModelMerger->vContext().paths.begin(), + m_textToModelMerger->vContext().paths.end()); - QmlJS::ImportInfo importInfo; + return Utils::transform(list, [](const Utils::FilePath &p) { return p.toString(); }); +} - for (const QmlJS::Import &qmljsImport : imports->all()) { - if (qmljsImport.info.name() == importStr) - importInfo = qmljsImport.info; - } - const QString importPath = importInfo.path(); - return importPath; +QSet<QPair<QString, QString> > RewriterView::qrcMapping() const +{ + return m_textToModelMerger->qrcMapping(); +} + +namespace { +#ifdef QDS_USE_PROJECTSTORAGE + +ModuleIds generateModuleIds(const ModelNodes &nodes) +{ + ModuleIds moduleIds; + moduleIds.reserve(Utils::usize(nodes)); + for (const auto &node : nodes) { + auto exportedNames = node.metaInfo().allExportedTypeNames(); + if (exportedNames.size()) + moduleIds.push_back(exportedNames.front().moduleId); } - return QString(); + std::sort(moduleIds.begin(), moduleIds.end()); + moduleIds.erase(std::unique(moduleIds.begin(), moduleIds.end()), moduleIds.end()); + + return moduleIds; } -QStringList RewriterView::importDirectories() const +QStringList generateImports(ModuleIds moduleIds, const ProjectStorageType &projectStorage) { - const QList<Utils::FilePath> list(m_textToModelMerger->vContext().paths.begin(), - m_textToModelMerger->vContext().paths.end()); + QStringList imports; + imports.reserve(std::ssize(moduleIds)); - return Utils::transform(list, [](const Utils::FilePath &p) { return p.toString(); }); + for (auto moduleId : moduleIds) { + using Storage::ModuleKind; + auto module = projectStorage.module(moduleId); + switch (module.kind) { + case ModuleKind::QmlLibrary: + imports.push_back("import " + module.name.toQString()); + break; + case ModuleKind::PathLibrary: + imports.push_back("import \"" + module.name.toQString() + "\""); + break; + case ModuleKind::CppLibrary: + break; + } + } + + return imports; } -QSet<QPair<QString, QString> > RewriterView::qrcMapping() const +QStringList generateImports(const ModelNodes &nodes) { - return m_textToModelMerger->qrcMapping(); + if (nodes.empty()) + return {}; + + auto moduleIds = generateModuleIds(nodes); + + return generateImports(moduleIds, *nodes.front().model()->projectStorage()); } +#endif +} // namespace + void RewriterView::moveToComponent(const ModelNode &modelNode) { if (!modelNode.isValid()) @@ -1029,20 +1070,26 @@ void RewriterView::moveToComponent(const ModelNode &modelNode) int offset = nodeOffset(modelNode); const QList<ModelNode> nodes = modelNode.allSubModelNodesAndThisNode(); - QSet<QString> directPaths; +#ifdef QDS_USE_PROJECTSTORAGE + auto directPaths = generateImports(nodes); +#else + QSet<QString> directPathsSet; // Always add QtQuick import QString quickImport = model()->qtQuickItemMetaInfo().requiredImportString(); if (!quickImport.isEmpty()) - directPaths.insert(quickImport); + directPathsSet.insert(quickImport); for (const ModelNode &partialNode : nodes) { QString importStr = partialNode.metaInfo().requiredImportString(); if (importStr.size()) - directPaths << importStr; + directPathsSet << importStr; } - QString importData = Utils::sorted(directPaths.values()).join(QChar::LineFeed); + auto directPaths = directPathsSet.values(); +#endif + + QString importData = Utils::sorted(directPaths).join(QChar::LineFeed); if (importData.size()) importData.append(QString(2, QChar::LineFeed)); @@ -1108,7 +1155,7 @@ void RewriterView::qmlTextChanged() switch (m_differenceHandling) { case Validate: { - ModelValidator differenceHandler(m_textToModelMerger.data()); + ModelValidator differenceHandler(m_textToModelMerger.get()); if (m_textToModelMerger->load(newQmlText, differenceHandler)) m_lastCorrectQmlSource = newQmlText; break; diff --git a/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp b/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp index 16877b61db..4f744d54e6 100644 --- a/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp @@ -23,6 +23,8 @@ #include <QQueue> #include <QRegularExpression> +#include <memory> + namespace { QPoint pointForModelNode(const QmlDesigner::ModelNode &node) @@ -641,10 +643,10 @@ void StylesheetMerger::styleMerge(const QString &qmlTemplateString, textEditTemplate.setPlainText(imports + qmlTemplateString); NotIndentingTextEditModifier textModifierTemplate(&textEditTemplate); - QScopedPointer<RewriterView> templateRewriterView( - new RewriterView(externalDependencies, RewriterView::Amend)); + std::unique_ptr<RewriterView> templateRewriterView = std::make_unique<RewriterView>( + externalDependencies, RewriterView::Amend); templateRewriterView->setTextModifier(&textModifierTemplate); - templateModel->attachView(templateRewriterView.data()); + templateModel->attachView(templateRewriterView.get()); templateRewriterView->setCheckSemanticErrors(false); templateRewriterView->setPossibleImportsEnabled(false); @@ -665,12 +667,12 @@ void StylesheetMerger::styleMerge(const QString &qmlTemplateString, textEditStyle.setPlainText(parentRewriterView->textModifierContent()); NotIndentingTextEditModifier textModifierStyle(&textEditStyle); - QScopedPointer<RewriterView> styleRewriterView( - new RewriterView(externalDependencies, RewriterView::Amend)); + std::unique_ptr<RewriterView> styleRewriterView = std::make_unique<RewriterView>( + externalDependencies, RewriterView::Amend); styleRewriterView->setTextModifier(&textModifierStyle); - styleModel->attachView(styleRewriterView.data()); + styleModel->attachView(styleRewriterView.get()); - StylesheetMerger merger(templateRewriterView.data(), styleRewriterView.data()); + StylesheetMerger merger(templateRewriterView.get(), styleRewriterView.get()); try { merger.merge(); diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index f7be6cf5e4..ded7fbc5ef 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 { @@ -67,17 +67,6 @@ bool isSupportedAttachedProperties(const QString &propertyName) || propertyName.startsWith(QLatin1String("InsightCategory.")); } -bool isSupportedVersion(QmlDesigner::Version version) -{ - if (version.major == 2) - return version.minor <= 15; - - if (version.major == 6) - return version.minor <= 6; - - return false; -} - bool isGlobalQtEnums(QStringView value) { static constexpr auto list = Utils::to_array<std::u16string_view>( @@ -95,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)); @@ -102,32 +94,28 @@ bool isGlobalQtEnums(QStringView value) bool isKnownEnumScopes(QStringView value) { - static constexpr auto list = Utils::to_array<std::u16string_view>({u"TextInput", - u"TextEdit", - u"Material", - u"Universal", - u"Font", - u"Shape", - u"ShapePath", - u"AbstractButton", - u"Text", - u"ShaderEffectSource", - u"Grid", - u"ItemLayer", - u"ImageLayer", - u"SpriteLayer", - u"Light"}); + static constexpr auto list = Utils::to_array<std::u16string_view>( + {u"TextInput", + u"TextEdit", + u"Material", + u"Universal", + u"Font", + u"Shape", + u"ShapePath", + u"AbstractButton", + u"Text", + u"ShaderEffectSource", + u"Grid", + u"ItemLayer", + u"ImageLayer", + u"SpriteLayer", + u"Light", + u"ExtendedSceneEnvironment.GlowBlendMode"}); return std::find(std::begin(list), std::end(list), QmlDesigner::ModelUtils::toStdStringView(value)) != std::end(list); } -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('"'))) @@ -431,14 +419,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) { } @@ -446,12 +439,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<NodeMetaInfo, TypeName> lookup(AST::UiQualifiedId *astTypeNode) { @@ -481,113 +481,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<const ObjectValue *> 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<ObjectValue>(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(); @@ -610,9 +503,9 @@ public: if (!propertyMetaInfo.isValid()) { 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" + if (!node.metaInfo().isQtQmlModelsListElement() && !isAttached) { + qCInfo(texttomodelMergerLog) + << Q_FUNC_INFO << "\nUnknown property" << propertyPrefix + QLatin1Char('.') + toString(propertyId) << "on line" << propertyId->identifierToken.startLine << "column" << propertyId->identifierToken.startColumn; @@ -668,6 +561,11 @@ public: //Check for known enum scopes used globally if (isKnownEnumScopes(astValueList.constFirst())) return QVariant::fromValue(Enumeration(astValue)); + } else if (astValueList.size() == 3) { + QString enumName = astValueList.constFirst() + '.' + astValueList.at(1); + if (isKnownEnumScopes(enumName)) + return QVariant::fromValue( + Enumeration(enumName.toUtf8(), astValueList.constLast().toUtf8())); } auto eStmt = AST::cast<AST::ExpressionStatement *>(rhs); @@ -685,9 +583,12 @@ public: return QVariant(); } - +#ifndef QDS_USE_PROJECTSTORAGE const ScopeChain &scopeChain() const - { return m_scopeChain; } + { + return m_scopeChain; + } +#endif QList<DiagnosticMessage> diagnosticLinkMessages() const { return m_diagnosticLinkMessages; } @@ -695,9 +596,11 @@ public: private: Document::Ptr m_doc; QList<DiagnosticMessage> m_diagnosticLinkMessages; +#ifndef QDS_USE_PROJECTSTORAGE ContextPtr m_context; ScopeChain m_scopeChain; ScopeBuilder m_scopeBuilder; +#endif Model *m_model; }; @@ -841,6 +744,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"), @@ -866,6 +770,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) || ...); }, @@ -931,9 +836,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()) @@ -942,10 +849,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) { @@ -975,7 +882,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()); @@ -1010,6 +919,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<DocumentMessage> *errors) { @@ -1100,15 +1010,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<const ScopeChain>( - new ScopeChain(ctxt.scopeChain())); + +#ifndef QDS_USE_PROJECTSTORAGE + m_scopeChain = QSharedPointer<const ScopeChain>(new ScopeChain(ctxt.scopeChain())); if (view()->checkLinkErrors()) { qCInfo(rewriterBenchmark) << "linked:" << time.elapsed(); collectLinkErrors(&errors, ctxt); } - setupPossibleImports(); +#endif qCInfo(rewriterBenchmark) << "possible imports:" << time.elapsed(); @@ -1142,7 +1053,9 @@ bool TextToModelMerger::load(const QString &data, DifferenceHandler &differenceH qCInfo(rewriterBenchmark) << "synced nodes:" << time.elapsed(); +#ifndef QDS_USE_PROJECTSTORAGE setupUsedImports(); +#endif setActive(false); @@ -1239,8 +1152,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<PropertyName> modelPropertyNames = Utils::toSet(modelNode.propertyNames()); if (!modelNode.id().isEmpty()) @@ -1254,7 +1168,8 @@ void TextToModelMerger::syncNode(ModelNode &modelNode, if (auto array = AST::cast<AST::UiArrayBinding *>(member)) { const QString astPropertyName = toString(array->qualifiedId); - if (isPropertyChangesType(typeName) || isConnectionsType(typeName) || context->lookupProperty(QString(), array->qualifiedId)) { + if (isPropertyChangesType(typeName) || isConnectionsType(typeName) + || modelNode.metaInfo().hasProperty(astPropertyName.toUtf8())) { AbstractProperty modelProperty = modelNode.property(astPropertyName.toUtf8()); QList<AST::UiObjectMember *> arrayMembers; for (AST::UiArrayMemberList *iter = array->members; iter; iter = iter->next) @@ -1286,13 +1201,8 @@ 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) + || modelNode.metaInfo().hasProperty(astPropertyName.toUtf8())) { AbstractProperty modelProperty = modelNode.property(astPropertyName.toUtf8()); if (context->isArrayProperty(modelProperty)) syncArrayProperty(modelProperty, {member}, context, differenceHandler); @@ -1396,7 +1306,9 @@ void TextToModelMerger::syncNode(ModelNode &modelNode, differenceHandler.propertyAbsentFromQml(modelProperty); } +#ifndef QDS_USE_PROJECTSTORAGE context->leaveScope(); +#endif } static QVariant parsePropertyExpression(AST::ExpressionNode *expressionNode) @@ -1476,9 +1388,8 @@ 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())) { AbstractProperty modelProperty = modelNode.property(astPropertyName.toUtf8()); QVariant variantValue = parsePropertyScriptBinding(script); if (!variantValue.isValid()) @@ -1512,15 +1423,14 @@ 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()) - || context->lookupProperty(prefix, script->qualifiedId) - || 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(); } 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(); } @@ -1655,7 +1565,7 @@ void TextToModelMerger::syncVariantProperty(AbstractProperty &modelProperty, const TypeName &astType, DifferenceHandler &differenceHandler) { - if (astValue.canConvert(QMetaType::QString)) + if (astValue.canConvert(QMetaType(QMetaType::QString))) populateQrcMapping(astValue.toString()); if (modelProperty.isVariantProperty()) { @@ -2232,42 +2142,31 @@ void TextToModelMerger::collectImportErrors(QList<DocumentMessage> *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; - - 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 { + hasQtQuick = true; + + 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", - "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()))); } } } @@ -2276,8 +2175,10 @@ void TextToModelMerger::collectImportErrors(QList<DocumentMessage> *errors) errors->append(DocumentMessage(QCoreApplication::translate("QmlDesigner::TextToModelMerger", "No import for Qt Quick found."))); } -void TextToModelMerger::collectSemanticErrorsAndWarnings(QList<DocumentMessage> *errors, QList<DocumentMessage> *warnings) +void TextToModelMerger::collectSemanticErrorsAndWarnings( + [[maybe_unused]] QList<DocumentMessage> *errors, [[maybe_unused]] QList<DocumentMessage> *warnings) { +#ifndef QDS_USE_PROJECTSTORAGE Check check(m_document, m_scopeChain->context()); check.disableMessage(StaticAnalysis::ErrPrototypeCycle); check.disableMessage(StaticAnalysis::ErrCouldNotResolvePrototype); @@ -2306,6 +2207,7 @@ void TextToModelMerger::collectSemanticErrorsAndWarnings(QList<DocumentMessage> if (message.severity == Severity::Warning) warnings->append(DocumentMessage(message.toDiagnosticMessage(), fileNameUrl)); } +#endif } void TextToModelMerger::populateQrcMapping(const QString &filePath) @@ -2410,6 +2312,9 @@ QSet<QPair<QString, QString> > TextToModelMerger::qrcMapping() const QList<QmlTypeData> TextToModelMerger::getQMLSingletons() const { +#ifdef QDS_USE_PROJECTSTORAGE + return {}; +#else QList<QmlTypeData> list; if (!m_scopeChain || !m_scopeChain->document()) return list; @@ -2440,6 +2345,7 @@ QList<QmlTypeData> 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 f511906040..e22f747718 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<const QmlJS::ScopeChain> m_scopeChain; +#endif QmlJS::Document::Ptr m_document; QTimer m_setupTimer; QSet<ModelNode> m_setupComponentList; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h index 03c25dfac7..76305b1fbe 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h @@ -84,16 +84,15 @@ inline constexpr char PrincipledMaterial[] = "PrincipledMaterial"; inline constexpr char PropertyAnimation[] = "PropertyAnimation"; inline constexpr char PropertyChanges[] = "PropertyChanges"; inline constexpr char QML[] = "QML"; -inline constexpr char QML_cppnative[] = "QML-cppnative"; inline constexpr char QQuick3DParticleAbstractShape[] = "QQuick3DParticleAbstractShape"; inline constexpr char QQuickStateOperation[] = "QQuickStateOperation"; inline constexpr char QtMultimedia[] = "QtMultimedia"; 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"; inline constexpr char QtQuick[] = "QtQuick"; inline constexpr char QtQuick_Controls[] = "QtQuick.Controls"; inline constexpr char QtQuick_Dialogs[] = "QtQuick.Dialogs"; @@ -103,7 +102,6 @@ inline constexpr char QtQuick_Studio_Components[] = "QtQuick.Studio.Components"; inline constexpr char QtQuick_Templates[] = "QtQuick.Templates"; inline constexpr char QtQuick_Timeline[] = "QtQuick.Timeline"; inline constexpr char QtQuick_Window[] = "QtQuick.Window"; -inline constexpr char QtQuick_cppnative[] = "QtQuick-cppnative"; inline constexpr char Qt_SafeRenderer[] = "Qt.SafeRenderer"; inline constexpr char Rectangle[] = "Rectangle"; inline constexpr char Repeater[] = "Repeater"; @@ -131,6 +129,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"; @@ -147,7 +146,7 @@ struct BaseCacheType QmlDesigner::TypeId typeId; }; -template<const char *moduleName_, const char *typeName_> +template<const char *moduleName_, ModuleKind moduleKind, const char *typeName_> struct CacheType : public BaseCacheType { }; @@ -155,105 +154,107 @@ struct CacheType : public BaseCacheType template<typename ProjectStorage> class CommonTypeCache { - using CommonTypes = std::tuple<CacheType<FlowView, FlowActionArea>, - CacheType<FlowView, FlowDecision>, - CacheType<FlowView, FlowItem>, - CacheType<FlowView, FlowTransition>, - CacheType<FlowView, FlowView>, - CacheType<FlowView, FlowWildcard>, - CacheType<QML, BoolType>, - CacheType<QML, Component>, - CacheType<QML, DoubleType>, - CacheType<QML, IntType>, - CacheType<QML, QtObject>, - CacheType<QML, date>, - CacheType<QML, string>, - CacheType<QML, url>, - CacheType<QML, var>, - CacheType<QML_cppnative, FloatType>, - CacheType<QML_cppnative, UIntType>, - CacheType<QtQml, Connections>, - CacheType<QtMultimedia, SoundEffect>, - CacheType<QtQml_Models, ListElement>, - CacheType<QtQml_Models, ListModel>, - CacheType<QtQuick, BorderImage>, - CacheType<QtQuick, GridView>, - CacheType<QtQuick, Image>, - CacheType<QtQuick, Item>, - CacheType<QtQuick, ListView>, - CacheType<QtQuick, Loader>, - CacheType<QtQuick, MouseArea>, - CacheType<QtQuick, Path>, - CacheType<QtQuick, PathView>, - CacheType<QtQuick, PauseAnimation>, - CacheType<QtQuick, Positioner>, - CacheType<QtQuick, PropertyAnimation>, - CacheType<QtQuick, PropertyChanges>, - CacheType<QtQuick, Rectangle>, - CacheType<QtQuick, Repeater>, - CacheType<QtQuick, State>, - CacheType<QtQuick, StateGroup>, - CacheType<QtQuick, Text>, - CacheType<QtQuick, TextEdit>, - CacheType<QtQuick, Transition>, - CacheType<QtQuick, color>, - CacheType<QtQuick, font>, - CacheType<QtQuick, vector2d>, - CacheType<QtQuick, vector3d>, - CacheType<QtQuick, vector4d>, - CacheType<QtQuick3D, BakedLightmap>, - CacheType<QtQuick3D, Buffer>, - CacheType<QtQuick3D, Camera>, - CacheType<QtQuick3D, Command>, - CacheType<QtQuick3D, CubeMapTexture>, - CacheType<QtQuick3D, DefaultMaterial>, - CacheType<QtQuick3D, DirectionalLight>, - CacheType<QtQuick3D, Effect>, - CacheType<QtQuick3D, InstanceList>, - CacheType<QtQuick3D, InstanceListEntry>, - CacheType<QtQuick3D, Light>, - CacheType<QtQuick3D, Material>, - CacheType<QtQuick3D, Model>, - CacheType<QtQuick3D, Node>, - CacheType<QtQuick3D, OrthographicCamera>, - CacheType<QtQuick3D, Pass>, - CacheType<QtQuick3D, PerspectiveCamera>, - CacheType<QtQuick3D, PointLight>, - CacheType<QtQuick3D, PrincipledMaterial>, - CacheType<QtQuick3D, SceneEnvironment>, - CacheType<QtQuick3D, Shader>, - CacheType<QtQuick3D, SpecularGlossyMaterial>, - CacheType<QtQuick3D, SpotLight>, - CacheType<QtQuick3D, Texture>, - CacheType<QtQuick3D, TextureInput>, - CacheType<QtQuick3D, View3D>, - CacheType<QtQuick3D_Particles3D, Affector3D>, - CacheType<QtQuick3D_Particles3D, Attractor3D>, - CacheType<QtQuick3D_Particles3D, Model>, - CacheType<QtQuick3D_Particles3D, Particle3D>, - CacheType<QtQuick3D_Particles3D, ParticleEmitter3D>, - CacheType<QtQuick3D_Particles3D, SpriteParticle3D>, - CacheType<QtQuick3D_Particles3D_cppnative, QQuick3DParticleAbstractShape>, - CacheType<QtQuick_Controls, Control>, - CacheType<QtQuick_Controls, Popup>, - CacheType<QtQuick_Controls, SplitView>, - CacheType<QtQuick_Controls, SwipeView>, - CacheType<QtQuick_Controls, TabBar>, - CacheType<QtQuick_Controls, TextArea>, - CacheType<QtQuick_Dialogs, Dialog>, - CacheType<QtQuick_Extras, Picture>, - CacheType<QtQuick_Layouts, Layout>, - CacheType<QtQuick_Studio_Components, GroupItem>, - CacheType<QtQuick_Studio_Components, JsonListModel>, - CacheType<QtQuick_Templates, Control>, - CacheType<QtQuick_Timeline, Keyframe>, - CacheType<QtQuick_Timeline, KeyframeGroup>, - CacheType<QtQuick_Timeline, Timeline>, - CacheType<QtQuick_Timeline, TimelineAnimation>, - CacheType<QtQuick_cppnative, QQuickStateOperation>, - CacheType<Qt_SafeRenderer, SafePicture>, - CacheType<Qt_SafeRenderer, SafeRendererPicture>, - CacheType<QtQuick_Window, Window>>; + using CommonTypes = std::tuple< + CacheType<FlowView, ModuleKind::QmlLibrary, FlowActionArea>, + CacheType<FlowView, ModuleKind::QmlLibrary, FlowDecision>, + CacheType<FlowView, ModuleKind::QmlLibrary, FlowItem>, + CacheType<FlowView, ModuleKind::QmlLibrary, FlowTransition>, + CacheType<FlowView, ModuleKind::QmlLibrary, FlowView>, + CacheType<FlowView, ModuleKind::QmlLibrary, FlowWildcard>, + CacheType<QML, ModuleKind::QmlLibrary, BoolType>, + CacheType<QML, ModuleKind::QmlLibrary, Component>, + CacheType<QML, ModuleKind::QmlLibrary, DoubleType>, + CacheType<QML, ModuleKind::QmlLibrary, IntType>, + CacheType<QML, ModuleKind::QmlLibrary, QtObject>, + CacheType<QML, ModuleKind::QmlLibrary, date>, + CacheType<QML, ModuleKind::QmlLibrary, string>, + CacheType<QML, ModuleKind::QmlLibrary, url>, + CacheType<QML, ModuleKind::QmlLibrary, var>, + CacheType<QML, ModuleKind::CppLibrary, FloatType>, + CacheType<QML, ModuleKind::CppLibrary, UIntType>, + CacheType<QtQml, ModuleKind::QmlLibrary, Connections>, + CacheType<QtMultimedia, ModuleKind::QmlLibrary, SoundEffect>, + CacheType<QtQml_Models, ModuleKind::QmlLibrary, ListElement>, + CacheType<QtQml_Models, ModuleKind::QmlLibrary, ListModel>, + CacheType<QtQml_XmlListModel, ModuleKind::QmlLibrary, XmlListModelRole>, + CacheType<QtQuick, ModuleKind::QmlLibrary, BorderImage>, + CacheType<QtQuick, ModuleKind::QmlLibrary, GridView>, + CacheType<QtQuick, ModuleKind::QmlLibrary, Image>, + CacheType<QtQuick, ModuleKind::QmlLibrary, Item>, + CacheType<QtQuick, ModuleKind::QmlLibrary, ListView>, + CacheType<QtQuick, ModuleKind::QmlLibrary, Loader>, + CacheType<QtQuick, ModuleKind::QmlLibrary, MouseArea>, + CacheType<QtQuick, ModuleKind::QmlLibrary, Path>, + CacheType<QtQuick, ModuleKind::QmlLibrary, PathView>, + CacheType<QtQuick, ModuleKind::QmlLibrary, PauseAnimation>, + CacheType<QtQuick, ModuleKind::QmlLibrary, Positioner>, + CacheType<QtQuick, ModuleKind::QmlLibrary, PropertyAnimation>, + CacheType<QtQuick, ModuleKind::QmlLibrary, PropertyChanges>, + CacheType<QtQuick, ModuleKind::QmlLibrary, Rectangle>, + CacheType<QtQuick, ModuleKind::QmlLibrary, Repeater>, + CacheType<QtQuick, ModuleKind::QmlLibrary, State>, + CacheType<QtQuick, ModuleKind::QmlLibrary, StateGroup>, + CacheType<QtQuick, ModuleKind::QmlLibrary, Text>, + CacheType<QtQuick, ModuleKind::QmlLibrary, TextEdit>, + CacheType<QtQuick, ModuleKind::QmlLibrary, Transition>, + CacheType<QtQuick, ModuleKind::QmlLibrary, color>, + CacheType<QtQuick, ModuleKind::QmlLibrary, font>, + CacheType<QtQuick, ModuleKind::QmlLibrary, vector2d>, + CacheType<QtQuick, ModuleKind::QmlLibrary, vector3d>, + CacheType<QtQuick, ModuleKind::QmlLibrary, vector4d>, + CacheType<QtQuick3D, ModuleKind::QmlLibrary, BakedLightmap>, + CacheType<QtQuick3D, ModuleKind::QmlLibrary, Buffer>, + CacheType<QtQuick3D, ModuleKind::QmlLibrary, Camera>, + CacheType<QtQuick3D, ModuleKind::QmlLibrary, Command>, + CacheType<QtQuick3D, ModuleKind::QmlLibrary, CubeMapTexture>, + CacheType<QtQuick3D, ModuleKind::QmlLibrary, DefaultMaterial>, + CacheType<QtQuick3D, ModuleKind::QmlLibrary, DirectionalLight>, + CacheType<QtQuick3D, ModuleKind::QmlLibrary, Effect>, + CacheType<QtQuick3D, ModuleKind::QmlLibrary, InstanceList>, + CacheType<QtQuick3D, ModuleKind::QmlLibrary, InstanceListEntry>, + CacheType<QtQuick3D, ModuleKind::QmlLibrary, Light>, + CacheType<QtQuick3D, ModuleKind::QmlLibrary, Material>, + CacheType<QtQuick3D, ModuleKind::QmlLibrary, Model>, + CacheType<QtQuick3D, ModuleKind::QmlLibrary, Node>, + CacheType<QtQuick3D, ModuleKind::QmlLibrary, OrthographicCamera>, + CacheType<QtQuick3D, ModuleKind::QmlLibrary, Pass>, + CacheType<QtQuick3D, ModuleKind::QmlLibrary, PerspectiveCamera>, + CacheType<QtQuick3D, ModuleKind::QmlLibrary, PointLight>, + CacheType<QtQuick3D, ModuleKind::QmlLibrary, PrincipledMaterial>, + CacheType<QtQuick3D, ModuleKind::QmlLibrary, SceneEnvironment>, + CacheType<QtQuick3D, ModuleKind::QmlLibrary, Shader>, + CacheType<QtQuick3D, ModuleKind::QmlLibrary, SpecularGlossyMaterial>, + CacheType<QtQuick3D, ModuleKind::QmlLibrary, SpotLight>, + CacheType<QtQuick3D, ModuleKind::QmlLibrary, Texture>, + CacheType<QtQuick3D, ModuleKind::QmlLibrary, TextureInput>, + CacheType<QtQuick3D, ModuleKind::QmlLibrary, View3D>, + CacheType<QtQuick3D_Particles3D, ModuleKind::QmlLibrary, Affector3D>, + CacheType<QtQuick3D_Particles3D, ModuleKind::QmlLibrary, Attractor3D>, + CacheType<QtQuick3D_Particles3D, ModuleKind::QmlLibrary, Model>, + CacheType<QtQuick3D_Particles3D, ModuleKind::QmlLibrary, Particle3D>, + CacheType<QtQuick3D_Particles3D, ModuleKind::QmlLibrary, ParticleEmitter3D>, + CacheType<QtQuick3D_Particles3D, ModuleKind::QmlLibrary, SpriteParticle3D>, + CacheType<QtQuick3D_Particles3D, ModuleKind::CppLibrary, QQuick3DParticleAbstractShape>, + CacheType<QtQuick_Controls, ModuleKind::QmlLibrary, Control>, + CacheType<QtQuick_Controls, ModuleKind::QmlLibrary, Popup>, + CacheType<QtQuick_Controls, ModuleKind::QmlLibrary, SplitView>, + CacheType<QtQuick_Controls, ModuleKind::QmlLibrary, SwipeView>, + CacheType<QtQuick_Controls, ModuleKind::QmlLibrary, TabBar>, + CacheType<QtQuick_Controls, ModuleKind::QmlLibrary, TextArea>, + CacheType<QtQuick_Dialogs, ModuleKind::QmlLibrary, Dialog>, + CacheType<QtQuick_Extras, ModuleKind::QmlLibrary, Picture>, + CacheType<QtQuick_Layouts, ModuleKind::QmlLibrary, Layout>, + CacheType<QtQuick_Studio_Components, ModuleKind::QmlLibrary, GroupItem>, + CacheType<QtQuick_Studio_Components, ModuleKind::QmlLibrary, JsonListModel>, + CacheType<QtQuick_Templates, ModuleKind::QmlLibrary, Control>, + CacheType<QtQuick_Timeline, ModuleKind::QmlLibrary, Keyframe>, + CacheType<QtQuick_Timeline, ModuleKind::QmlLibrary, KeyframeGroup>, + CacheType<QtQuick_Timeline, ModuleKind::QmlLibrary, Timeline>, + CacheType<QtQuick_Timeline, ModuleKind::QmlLibrary, TimelineAnimation>, + CacheType<QtQuick, ModuleKind::CppLibrary, QQuickStateOperation>, + CacheType<Qt_SafeRenderer, ModuleKind::QmlLibrary, SafePicture>, + CacheType<Qt_SafeRenderer, ModuleKind::QmlLibrary, SafeRendererPicture>, + CacheType<QtQuick_Window, ModuleKind::QmlLibrary, Window>>; public: CommonTypeCache(const ProjectStorage &projectStorage) @@ -280,14 +281,14 @@ public: std::fill(std::begin(m_typesWithoutProperties), std ::end(m_typesWithoutProperties), TypeId{}); } - template<const char *moduleName, const char *typeName> + template<const char *moduleName, const char *typeName, ModuleKind moduleKind = ModuleKind::QmlLibrary> TypeId typeId() const { - auto &type = std::get<CacheType<moduleName, typeName>>(m_types); + auto &type = std::get<CacheType<moduleName, moduleKind, typeName>>(m_types); if (type.typeId) return type.typeId; - return refreshTypedId(type, moduleName, typeName); + return refreshTypedId(type, moduleName, moduleKind, typeName); } template<const char *typeName> @@ -304,11 +305,11 @@ public: else if constexpr (std::is_same_v<Type, int>) return typeId<QML, IntType>(); else if constexpr (std::is_same_v<Type, uint>) - return typeId<QML_cppnative, UIntType>(); + return typeId<QML, UIntType, ModuleKind::CppLibrary>(); else if constexpr (std::is_same_v<Type, bool>) return typeId<QML, BoolType>(); else if constexpr (std::is_same_v<Type, float>) - return typeId<QML_cppnative, FloatType>(); + return typeId<QML, FloatType, ModuleKind::CppLibrary>(); else if constexpr (std::is_same_v<Type, QString>) return typeId<QML, string>(); else if constexpr (std::is_same_v<Type, QDateTime>) @@ -338,10 +339,11 @@ public: private: TypeId refreshTypedId(BaseCacheType &type, ::Utils::SmallStringView moduleName, + ModuleKind moduleKind, ::Utils::SmallStringView typeName) const { if (!type.moduleId) - type.moduleId = m_projectStorage.moduleId(moduleName); + type.moduleId = m_projectStorage.moduleId(moduleName, moduleKind); type.typeId = m_projectStorage.typeId(type.moduleId, typeName, Storage::Version{}); @@ -350,10 +352,11 @@ private: TypeId refreshTypedIdWithoutTransaction(BaseCacheType &type, ::Utils::SmallStringView moduleName, - ::Utils::SmallStringView typeName) const + ::Utils::SmallStringView typeName, + ModuleKind moduleKind) const { if (!type.moduleId) - type.moduleId = m_projectStorage.fetchModuleIdUnguarded(moduleName); + type.moduleId = m_projectStorage.fetchModuleIdUnguarded(moduleName, moduleKind); type.typeId = m_projectStorage.fetchTypeIdByModuleIdAndExportedName(type.moduleId, typeName); @@ -368,26 +371,27 @@ private: std::copy(std::begin(typeIds), std::end(typeIds), std::begin(m_typesWithoutProperties)); } - template<const char *moduleName, const char *typeName> + template<const char *moduleName, const char *typeName, ModuleKind moduleKind = ModuleKind::QmlLibrary> TypeId typeIdWithoutTransaction() const { - auto &type = std::get<CacheType<moduleName, typeName>>(m_types); + auto &type = std::get<CacheType<moduleName, moduleKind, typeName>>(m_types); if (type.typeId) return type.typeId; - return refreshTypedIdWithoutTransaction(type, moduleName, typeName); + return refreshTypedIdWithoutTransaction(type, moduleName, typeName, moduleKind); } void updateTypeIdsWithoutProperties() { - setupTypeIdsWithoutProperties({typeIdWithoutTransaction<QML, BoolType>(), - typeIdWithoutTransaction<QML, IntType>(), - typeIdWithoutTransaction<QML_cppnative, UIntType>(), - typeIdWithoutTransaction<QML, DoubleType>(), - typeIdWithoutTransaction<QML_cppnative, FloatType>(), - typeIdWithoutTransaction<QML, date>(), - typeIdWithoutTransaction<QML, string>(), - typeIdWithoutTransaction<QML, url>()}); + setupTypeIdsWithoutProperties( + {typeIdWithoutTransaction<QML, BoolType>(), + typeIdWithoutTransaction<QML, IntType>(), + typeIdWithoutTransaction<QML, UIntType, ModuleKind::CppLibrary>(), + typeIdWithoutTransaction<QML, DoubleType>(), + typeIdWithoutTransaction<QML, FloatType, ModuleKind::CppLibrary>(), + typeIdWithoutTransaction<QML, date>(), + typeIdWithoutTransaction<QML, string>(), + typeIdWithoutTransaction<QML, url>()}); } private: diff --git a/src/plugins/qmldesigner/designercore/projectstorage/filestatus.h b/src/plugins/qmldesigner/designercore/projectstorage/filestatus.h index f3e275b8f3..48b3ba2700 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<typename String> + 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/filesystem.cpp b/src/plugins/qmldesigner/designercore/projectstorage/filesystem.cpp index 1376b2c3d9..d11190fdc7 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/filesystem.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/filesystem.cpp @@ -11,6 +11,7 @@ #include <QDateTime> #include <QDir> +#include <QDirIterator> #include <QFileInfo> namespace QmlDesigner { @@ -69,6 +70,18 @@ QString FileSystem::contentAsQString(const QString &filePath) const return {}; } +QStringList FileSystem::subdirectories(const QString &directoryPath) const +{ + QStringList directoryPaths; + directoryPaths.reserve(100); + QDirIterator directoryIterator{directoryPath, QDir::Dirs | QDir::NoDotAndDotDot}; + + while (directoryIterator.hasNext()) + directoryPaths.push_back(directoryIterator.next()); + + return directoryPaths; +} + void FileSystem::remove(const SourceIds &sourceIds) { for (SourceId sourceId : sourceIds) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/filesystem.h b/src/plugins/qmldesigner/designercore/projectstorage/filesystem.h index 078fd1ee98..1c881741c6 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<typename ProjectStorage, typename Mutex> class SourcePathCache; -template<typename Database> -class ProjectStorage; - class FileSystem : public FileSystemInterface { - using PathCache = SourcePathCache<ProjectStorage<Sqlite::Database>, NonLockingMutex>; + using PathCache = SourcePathCache<ProjectStorage, NonLockingMutex>; public: FileSystem(PathCache &sourcePathCache) @@ -33,6 +31,7 @@ public: long long lastModified(SourceId sourceId) const override; FileStatus fileStatus(SourceId sourceId) const override; QString contentAsQString(const QString &filePath) const override; + QStringList subdirectories(const QString &directoryPath) const override; void remove(const SourceIds &sourceIds) override; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/filesysteminterface.h b/src/plugins/qmldesigner/designercore/projectstorage/filesysteminterface.h index 6a7c964fa6..ff7608c9a3 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/filesysteminterface.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/filesysteminterface.h @@ -20,6 +20,7 @@ public: virtual FileStatus fileStatus(SourceId sourceId) const = 0; virtual void remove(const SourceIds &sourceIds) = 0; virtual QString contentAsQString(const QString &filePath) const = 0; + virtual QStringList subdirectories(const QString &directoryPath) const = 0; protected: ~FileSystemInterface() = default; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp index 3e493e8772..2283b64945 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp @@ -3,20 +3,4896 @@ #include "projectstorage.h" -#include <tracing/qmldesignertracing.h> - #include <sqlitedatabase.h> namespace QmlDesigner { -NanotraceHR::StringViewCategory<projectStorageTracingStatus()> &projectStorageCategory() +enum class SpecialIdState { Unresolved = -1 }; + +constexpr TypeId unresolvedTypeId = TypeId::createSpecialState(SpecialIdState::Unresolved); + +class UnresolvedTypeId : public TypeId +{ +public: + constexpr UnresolvedTypeId() + : TypeId{TypeId::createSpecialState(SpecialIdState::Unresolved)} + {} + + static constexpr UnresolvedTypeId create(DatabaseType idNumber) + { + UnresolvedTypeId id; + id.id = idNumber; + return id; + } +}; + +struct ProjectStorage::Statements { - thread_local NanotraceHR::StringViewCategory<projectStorageTracingStatus()> - projectStorageCategory_{"project storage"_t, Tracing::eventQueue(), projectStorageCategory}; + 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 " + " OR prototypeId IS NOT ?4 " + " OR extensionNameId IS NOT ?5)", + database}; + mutable Sqlite::ReadStatement<1, 1> selectTypeIdByExportedNameStatement{ + "SELECT typeId FROM exportedTypeNames WHERE name=?1", database}; + 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{ + "WITH RECURSIVE " + " typeSelection(typeId) AS (" + " VALUES(?1) " + " UNION ALL " + " SELECT t.typeId " + " FROM types AS t JOIN typeSelection AS ts " + " WHERE prototypeId=ts.typeId " + " AND t.typeId NOT IN (SELECT typeId FROM typeAnnotations)) " + "UPDATE types AS t " + "SET annotationTraits = ?2 " + "FROM typeSelection ts " + "WHERE t.typeId=ts.typeId", + database}; + Sqlite::ReadStatement<1, 2> selectNotUpdatedTypesInSourcesStatement{ + "SELECT DISTINCT typeId FROM types WHERE (sourceId IN carray(?1) AND typeId NOT IN " + "carray(?2))", + 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, 2> selectModuleIdByNameStatement{ + "SELECT moduleId FROM modules WHERE kind=?1 AND name=?2 LIMIT 1", database}; + mutable Sqlite::ReadWriteStatement<1, 2> insertModuleNameStatement{ + "INSERT INTO modules(kind, name) VALUES(?1, ?2) RETURNING moduleId", database}; + mutable Sqlite::ReadStatement<2, 1> selectModuleStatement{ + "SELECT name, kind FROM modules WHERE moduleId =?1", database}; + mutable Sqlite::ReadStatement<3> selectAllModulesStatement{ + "SELECT name, kind, moduleId FROM modules", database}; + mutable Sqlite::ReadStatement<1, 2> selectTypeIdBySourceIdAndNameStatement{ + "SELECT typeId FROM types WHERE sourceId=?1 and name=?2", database}; + mutable Sqlite::ReadStatement<1, 3> selectTypeIdByModuleIdsAndExportedNameStatement{ + "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, 2> updatePrototypeIdToTypeIdStatement{ + "UPDATE types " + "SET prototypeId=?2 " + "WHERE prototypeId=?1 " + "RETURNING typeId, prototypeNameId", + database}; + Sqlite::ReadWriteStatement<2, 2> updateExtensionIdToTypeIdStatement{ + "UPDATE types " + "SET extensionId=?2 " + "WHERE extensionId=?1 " + "RETURNING typeId, extensionNameId", + database}; + Sqlite::ReadStatement<2, 2> selectTypeIdAndPrototypeNameIdForPrototypeIdAndTypeNameStatement{ + "SELECT typeId, prototypeNameId " + "FROM types " + "WHERE prototypeNameId IN ( " + " SELECT importedTypeNameId " + " FROM " + " importedTypeNames WHERE name=?1) " + " AND prototypeId=?2", + database}; + Sqlite::ReadStatement<2, 2> selectTypeIdAndPrototypeNameIdForPrototypeIdAndSourceIdStatement{ + "SELECT typeId , prototypeNameId " + "FROM types " + "WHERE prototypeId=?1 AND sourceId=?2", + database}; + Sqlite::ReadStatement<2, 2> selectTypeIdAndExtensionNameIdForExtensionIdAndSourceIdStatement{ + "SELECT typeId, extensionNameId " + "FROM types " + "WHERE extensionId=?1 AND sourceId=?2", + database}; + Sqlite::ReadWriteStatement<3, 3> updatePrototypeIdAndExtensionIdToTypeIdForSourceIdStatement{ + "UPDATE types " + "SET prototypeId=?2, extensionId=?3 " + "WHERE sourceId=?1 " + "RETURNING typeId, prototypeNameId, extensionNameId", + database}; + Sqlite::ReadStatement<2, 2> selectTypeIdForExtensionIdAndTypeNameStatement{ + "SELECT typeId , prototypeNameId " + "FROM types " + "WHERE extensionNameId IN ( " + " SELECT importedTypeNameId " + " FROM importedTypeNames " + " WHERE name=?1) " + " AND extensionId=?2", + database}; + Sqlite::WriteStatement<2> updateTypePrototypeStatement{ + "UPDATE types SET prototypeId=?2 WHERE typeId=?1", database}; + 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> selectDirectoryInfosForSourceIdsStatement{ + "SELECT directorySourceId, sourceId, moduleId, fileType FROM directoryInfos WHERE " + "directorySourceId IN carray(?1) ORDER BY directorySourceId, sourceId", + database}; + Sqlite::WriteStatement<4> insertDirectoryInfoStatement{ + "INSERT INTO directoryInfos(directorySourceId, sourceId, " + "moduleId, fileType) VALUES(?1, ?2, ?3, ?4)", + database}; + Sqlite::WriteStatement<2> deleteDirectoryInfoStatement{ + "DELETE FROM directoryInfos WHERE directorySourceId=?1 AND sourceId=?2", database}; + Sqlite::WriteStatement<4> updateDirectoryInfoStatement{ + "UPDATE directoryInfos SET moduleId=?3, fileType=?4 WHERE directorySourceId=?1 AND sourceId=?2", + database}; + mutable Sqlite::ReadStatement<4, 1> selectDirectoryInfosForSourceIdStatement{ + "SELECT directorySourceId, sourceId, moduleId, fileType FROM directoryInfos WHERE " + "directorySourceId=?1", + database}; + mutable Sqlite::ReadStatement<4, 2> selectDirectoryInfosForSourceIdAndFileTypeStatement{ + "SELECT directorySourceId, sourceId, moduleId, fileType FROM directoryInfos WHERE " + "directorySourceId=?1 AND fileType=?2", + database}; + mutable Sqlite::ReadStatement<1, 2> selectDirectoryInfosSourceIdsForSourceIdAndFileTypeStatement{ + "SELECT sourceId FROM directoryInfos WHERE directorySourceId=?1 AND fileType=?2", database}; + mutable Sqlite::ReadStatement<4, 1> selectDirectoryInfoForSourceIdStatement{ + "SELECT directorySourceId, sourceId, moduleId, fileType FROM directoryInfos WHERE " + "sourceId=?1 LIMIT 1", + 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> selectSourceIdByTypeIdStatement{ + "SELECT sourceId FROM types WHERE typeId=?", database}; + mutable Sqlite::ReadStatement<1, 1> selectPrototypeAnnotationTraitsByTypeIdStatement{ + "SELECT annotationTraits " + "FROM types " + "WHERE typeId=(SELECT prototypeId FROM types WHERE typeId=?)", + database}; + mutable Sqlite::ReadStatement<1, 1> selectDefaultPropertyDeclarationIdStatement{ + "SELECT defaultPropertyId FROM types WHERE typeId=?", database}; + mutable Sqlite::ReadStatement<1, 1> selectPrototypeIdsForTypeIdInOrderStatement{ + "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<5, 1> selectTypeAnnotationsForSourceIdsStatement{ + "SELECT typeId, typeName, iconPath, itemLibrary, hints FROM typeAnnotations WHERE " + "sourceId IN carray(?1) ORDER BY typeId", + database}; + Sqlite::WriteStatement<7> insertTypeAnnotationStatement{ + "INSERT INTO " + " typeAnnotations(typeId, sourceId, directorySourceId, typeName, iconPath, itemLibrary, " + " hints) " + "VALUES(?1, ?2, ?3, ?4, ?5, ?6, ?7)", + database}; + Sqlite::WriteStatement<5> updateTypeAnnotationStatement{ + "UPDATE typeAnnotations " + "SET typeName=?2, iconPath=?3, itemLibrary=?4, hints=?5 " + "WHERE typeId=?1", + database}; + Sqlite::WriteStatement<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<10> selectItemLibraryEntriesStatement{ + "SELECT typeId, typeName, i.value->>'$.name', i.value->>'$.iconPath', " + " i.value->>'$.category', i.value->>'$.import', i.value->>'$.toolTip', " + " i.value->>'$.properties', i.value->>'$.extraFilePaths', i.value->>'$.templatePath' " + "FROM typeAnnotations AS ta , json_each(ta.itemLibrary) AS i " + "WHERE ta.itemLibrary IS NOT NULL", + database}; + mutable Sqlite::ReadStatement<10, 1> selectItemLibraryEntriesByTypeIdStatement{ + "SELECT typeId, typeName, i.value->>'$.name', i.value->>'$.iconPath', " + " i.value->>'$.category', i.value->>'$.import', i.value->>'$.toolTip', " + " i.value->>'$.properties', i.value->>'$.extraFilePaths', i.value->>'$.templatePath' " + "FROM typeAnnotations AS ta, json_each(ta.itemLibrary) AS i " + "WHERE typeId=?1 AND ta.itemLibrary IS NOT NULL", + database}; + mutable Sqlite::ReadStatement<10, 1> selectItemLibraryEntriesBySourceIdStatement{ + "SELECT typeId, typeName, i.value->>'$.name', i.value->>'$.iconPath', " + "i.value->>'$.category', " + " i.value->>'$.import', i.value->>'$.toolTip', i.value->>'$.properties', " + " i.value->>'$.extraFilePaths', i.value->>'$.templatePath' " + "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: + 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); + createDirectoryInfosTable(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.addColumn("prototypeId", + Sqlite::StrictColumnType::Integer); + auto &prototypeNameIdColumn = typesTable.addColumn("prototypeNameId", + Sqlite::StrictColumnType::Integer); + auto &extensionIdColumn = typesTable.addColumn("extensionId", + Sqlite::StrictColumnType::Integer); + auto &extensionNameIdColumn = typesTable.addColumn("extensionNameId", + Sqlite::StrictColumnType::Integer); + auto &defaultPropertyIdColumn = typesTable.addColumn("defaultPropertyId", + Sqlite::StrictColumnType::Integer); + typesTable.addColumn("annotationTraits", Sqlite::StrictColumnType::Integer); + typesTable.addUniqueIndex({sourceIdColumn, typesNameColumn}); + typesTable.addIndex({defaultPropertyIdColumn}); + typesTable.addIndex({prototypeIdColumn, sourceIdColumn}); + typesTable.addIndex({extensionIdColumn, sourceIdColumn}); + typesTable.addIndex({prototypeNameIdColumn}); + typesTable.addIndex({extensionNameIdColumn}); + + 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 &kindColumn = table.addColumn("kind", Sqlite::StrictColumnType::Integer); + auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text); + + table.addUniqueIndex({kindColumn, nameColumn}); - return projectStorageCategory_; + 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 createDirectoryInfosTable(Database &database) + { + Sqlite::StrictTable table; + table.setUseIfNotExists(true); + table.setUseWithoutRowId(true); + table.setName("directoryInfos"); + auto &directorySourceIdColumn = table.addColumn("directorySourceId", + Sqlite::StrictColumnType::Integer); + auto &sourceIdColumn = table.addColumn("sourceId", Sqlite::StrictColumnType::Integer); + table.addColumn("moduleId", Sqlite::StrictColumnType::Integer); + auto &fileTypeColumn = table.addColumn("fileType", Sqlite::StrictColumnType::Integer); + + table.addPrimaryKeyContraint({directorySourceIdColumn, sourceIdColumn}); + table.addUniqueIndex({sourceIdColumn}); + table.addIndex({directorySourceIdColumn, fileTypeColumn}); + + 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("typeName", Sqlite::StrictColumnType::Text); + 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, + ProjectStorageErrorNotifierInterface &errorNotifier, + bool isInitialized) + : database{database} + , errorNotifier{&errorNotifier} + , exclusiveTransaction{database} + , initializer{std::make_unique<ProjectStorage::Initializer>(database, isInitialized)} + , moduleCache{ModuleStorageAdapter{*this}} + , s{std::make_unique<ProjectStorage::Statements>(database)} +{ + NanotraceHR::Tracer tracer{"initialize"_t, projectStorageCategory()}; + + exclusiveTransaction.commit(); + + database.walCheckpointFull(); + + moduleCache.populate(); } -} // namespace QmlDesigner +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, + relinkablePrototypes, + relinkableExtensions); + 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); + + synchronizeDirectoryInfos(package.directoryInfos, package.updatedDirectoryInfoSourceIds); + + 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, [&] { + AliasPropertyDeclarations relinkableAliasPropertyDeclarations; + PropertyDeclarations relinkablePropertyDeclarations; + Prototypes relinkablePrototypes; + Prototypes relinkableExtensions; + TypeIds deletedTypeIds; + + synchronizeDocumentImports(imports, + {sourceId}, + Storage::Synchronization::ImportKind::Import, + Relink::Yes, + relinkablePrototypes, + relinkableExtensions); + + relink(relinkableAliasPropertyDeclarations, + relinkablePropertyDeclarations, + relinkablePrototypes, + relinkableExtensions, + deletedTypeIds); + }); +} + +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, Storage::ModuleKind kind) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get module id"_t, + projectStorageCategory(), + keyValue("module name", moduleName)}; + + auto moduleId = moduleCache.id({moduleName, kind}); + + tracer.end(keyValue("module id", moduleId)); + + return moduleId; +} + +Storage::Module ProjectStorage::module(ModuleId moduleId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get module name"_t, + projectStorageCategory(), + keyValue("module id", moduleId)}; + + if (!moduleId) + throw ModuleDoesNotExists{}; + + auto module = moduleCache.value(moduleId); + + tracer.end(keyValue("module name", module.name)); + tracer.end(keyValue("module kind", module.kind)); + + return {module.name, module.kind}; +} + +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 = s->selectTypeIdByModuleIdAndExportedNameAndVersionStatement.valueWithTransaction<TypeId>( + moduleId, exportedTypeName, version.major.value, version.minor.value); + + } else if (version.major) { + typeId = s->selectTypeIdByModuleIdAndExportedNameAndMajorVersionStatement + .valueWithTransaction<TypeId>(moduleId, exportedTypeName, version.major.value); + + } else { + typeId = s->selectTypeIdByModuleIdAndExportedNameStatement + .valueWithTransaction<TypeId>(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<TypeId, 256> 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 = s->selectTypeIdsByModuleIdStatement + .valuesWithTransaction<QVarLengthArray<TypeId, 256>>(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 = s->selectExportedTypesByTypeIdStatement + .valuesWithTransaction<Storage::Info::ExportedTypeName, 4>(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 = s->selectExportedTypesByTypeIdAndSourceIdStatement + .valuesWithTransaction<Storage::Info::ExportedTypeName, 4>(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<PropertyDeclarationId, 128> 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<PropertyDeclarationId, 128> ProjectStorage::localPropertyDeclarationIds(TypeId typeId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get local property declaration ids"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + auto propertyDeclarationIds = s->selectLocalPropertyDeclarationIdsForTypeStatement + .valuesWithTransaction<QVarLengthArray<PropertyDeclarationId, 128>>( + 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 = s->selectLocalPropertyDeclarationIdForTypeAndPropertyNameStatement + .valueWithTransaction<PropertyDeclarationId>(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<Storage::Info::PropertyDeclaration> ProjectStorage::propertyDeclaration( + PropertyDeclarationId propertyDeclarationId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get property declaration"_t, + projectStorageCategory(), + keyValue("property declaration id", propertyDeclarationId)}; + + auto propertyDeclaration = s->selectPropertyDeclarationForPropertyDeclarationIdStatement + .optionalValueWithTransaction<Storage::Info::PropertyDeclaration>( + propertyDeclarationId); + + tracer.end(keyValue("property declaration", propertyDeclaration)); + + return propertyDeclaration; +} + +std::optional<Storage::Info::Type> ProjectStorage::type(TypeId typeId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get type"_t, projectStorageCategory(), keyValue("type id", typeId)}; + + auto type = s->selectInfoTypeByTypeIdStatement.optionalValueWithTransaction<Storage::Info::Type>( + 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 = s->selectTypeIconPathStatement.valueWithTransaction<Utils::PathString>(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 = s->selectTypeHintsStatement.valuesWithTransaction<Storage::Info::TypeHints, 4>( + 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 = s->selectTypeAnnotationSourceIdsStatement.valuesWithTransaction<SmallSourceIds<4>>( + 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 = s->selectTypeAnnotationDirectorySourceIdsStatement + .valuesWithTransaction<SmallSourceIds<64>>(); + + 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 typeName, + 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_, typeName, name, iconPath, category, import, toolTip, templatePath); + if (properties.size()) + s->selectItemLibraryPropertiesStatement.readTo(last.properties, properties); + if (extraFilePaths.size()) + s->selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths); + }; + + s->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 typeName, + 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_, typeName, name, iconPath, category, import, toolTip, templatePath); + if (properties.size()) + s->selectItemLibraryPropertiesStatement.readTo(last.properties, properties); + if (extraFilePaths.size()) + s->selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths); + }; + + s->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 typeName, + 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, typeName, name, iconPath, category, import, toolTip, templatePath); + if (properties.size()) + s->selectItemLibraryPropertiesStatement.readTo(last.properties, properties); + if (extraFilePaths.size()) + s->selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths); + }; + + s->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 typeName, + 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, typeName, name, iconPath, category, import, toolTip, templatePath); + if (properties.size()) + s->selectItemLibraryPropertiesStatement.readTo(last.properties, properties); + if (extraFilePaths.size()) + s->selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths); + }; + + s->selectItemLibraryEntriesStatement.readCallbackWithTransaction(callback); + + tracer.end(keyValue("item library entries", entries)); + + return entries; +} + +std::vector<Utils::SmallString> ProjectStorage::signalDeclarationNames(TypeId typeId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get signal names"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + auto signalDeclarationNames = s->selectSignalDeclarationNamesForTypeStatement + .valuesWithTransaction<Utils::SmallString, 32>(typeId); + + tracer.end(keyValue("signal names", signalDeclarationNames)); + + return signalDeclarationNames; +} + +std::vector<Utils::SmallString> ProjectStorage::functionDeclarationNames(TypeId typeId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get function names"_t, + projectStorageCategory(), + keyValue("type id", typeId)}; + + auto functionDeclarationNames = s->selectFuncionDeclarationNamesForTypeStatement + .valuesWithTransaction<Utils::SmallString, 32>(typeId); + + tracer.end(keyValue("function names", functionDeclarationNames)); + + return functionDeclarationNames; +} + +std::optional<Utils::SmallString> ProjectStorage::propertyName( + PropertyDeclarationId propertyDeclarationId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get property name"_t, + projectStorageCategory(), + keyValue("property declaration id", propertyDeclarationId)}; + + auto propertyName = s->selectPropertyNameStatement.optionalValueWithTransaction<Utils::SmallString>( + 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 = s->selectPrototypeAndExtensionIdsStatement + .valuesWithTransaction<SmallTypeIds<16>>(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); + + s->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 = s->selectHeirTypeIdsStatement.valuesWithTransaction<SmallTypeIds<64>>(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 = s->selectTypeIdByExportedNameStatement.valueWithTransaction<TypeId>(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 = s->selectTypeIdByModuleIdsAndExportedNameStatement.valueWithTransaction<TypeId>( + static_cast<void *>(moduleIds.data()), static_cast<long long>(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 = s->selectTypeIdBySourceIdAndNameStatement.valueWithTransaction<TypeId>(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 = s->selectTypeByTypeIdStatement.value<Storage::Synchronization::Type>(typeId); + + type.exportedTypes = fetchExportedTypes(typeId); + type.propertyDeclarations = fetchPropertyDeclarations(type.typeId); + type.functionDeclarations = fetchFunctionDeclarations(type.typeId); + type.signalDeclarations = fetchSignalDeclarations(type.typeId); + type.enumerationDeclarations = fetchEnumerationDeclarations(type.typeId); + + 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 = s->selectTypesStatement.values<Storage::Synchronization::Type, 64>(); + + for (Storage::Synchronization::Type &type : types) { + type.exportedTypes = fetchExportedTypes(type.typeId); + type.propertyDeclarations = fetchPropertyDeclarations(type.typeId); + type.functionDeclarations = fetchFunctionDeclarations(type.typeId); + type.signalDeclarations = fetchSignalDeclarations(type.typeId); + type.enumerationDeclarations = fetchEnumerationDeclarations(type.typeId); + } + + return types; + }); + + 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 = s->selectSourceContextPathFromSourceContextsBySourceContextIdStatement + .optionalValue<Utils::PathString>(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 s->selectAllSourceContextsStatement.valuesWithTransaction<Cache::SourceContext, 128>(); +} + +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 = s->selectSourceNameAndSourceContextIdFromSourcesBySourceIdStatement + .valueWithTransaction<Cache::SourceNameAndSourceContextId>(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, [&] { + s->deleteAllSourceContextsStatement.execute(); + s->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 = s->selectSourceContextIdFromSourcesBySourceIdStatement + .valueWithTransaction<SourceContextId>(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 s->selectAllSourcesStatement.valuesWithTransaction<Cache::Source, 1024>(); +} + +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; +} + +FileStatuses ProjectStorage::fetchAllFileStatuses() const +{ + NanotraceHR::Tracer tracer{"fetch all file statuses"_t, projectStorageCategory()}; + + return s->selectAllFileStatusesStatement.valuesWithTransaction<FileStatus>(); +} + +FileStatus ProjectStorage::fetchFileStatus(SourceId sourceId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch file status"_t, + projectStorageCategory(), + keyValue("source id", sourceId)}; + + auto fileStatus = s->selectFileStatusesForSourceIdStatement.valueWithTransaction<FileStatus>( + sourceId); + + tracer.end(keyValue("file status", fileStatus)); + + return fileStatus; +} + +std::optional<Storage::Synchronization::DirectoryInfo> ProjectStorage::fetchDirectoryInfo(SourceId sourceId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch directory info"_t, + projectStorageCategory(), + keyValue("source id", sourceId)}; + + auto directoryInfo = s->selectDirectoryInfoForSourceIdStatement + .optionalValueWithTransaction<Storage::Synchronization::DirectoryInfo>( + sourceId); + + tracer.end(keyValue("directory info", directoryInfo)); + + return directoryInfo; +} + +Storage::Synchronization::DirectoryInfos ProjectStorage::fetchDirectoryInfos(SourceId directorySourceId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch directory infos by source id"_t, + projectStorageCategory(), + keyValue("source id", directorySourceId)}; + + auto directoryInfos = s->selectDirectoryInfosForSourceIdStatement + .valuesWithTransaction<Storage::Synchronization::DirectoryInfo, 1024>( + directorySourceId); + + tracer.end(keyValue("directory infos", directoryInfos)); + + return directoryInfos; +} + +Storage::Synchronization::DirectoryInfos ProjectStorage::fetchDirectoryInfos( + SourceId directorySourceId, Storage::Synchronization::FileType fileType) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch directory infos by source id and file type"_t, + projectStorageCategory(), + keyValue("source id", directorySourceId), + keyValue("file type", fileType)}; + + auto directoryInfos = s->selectDirectoryInfosForSourceIdAndFileTypeStatement + .valuesWithTransaction<Storage::Synchronization::DirectoryInfo, 16>( + directorySourceId, fileType); + + tracer.end(keyValue("directory infos", directoryInfos)); + + return directoryInfos; +} + +Storage::Synchronization::DirectoryInfos ProjectStorage::fetchDirectoryInfos( + const SourceIds &directorySourceIds) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch directory infos by source ids"_t, + projectStorageCategory(), + keyValue("source ids", directorySourceIds)}; + + auto directoryInfos = s->selectDirectoryInfosForSourceIdsStatement + .valuesWithTransaction<Storage::Synchronization::DirectoryInfo, 64>( + toIntegers(directorySourceIds)); + + tracer.end(keyValue("directory infos", directoryInfos)); + + return directoryInfos; +} + +SmallSourceIds<32> ProjectStorage::fetchSubdirectorySourceIds(SourceId directorySourceId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch subdirectory source ids"_t, + projectStorageCategory(), + keyValue("source id", directorySourceId)}; + + auto sourceIds = s->selectDirectoryInfosSourceIdsForSourceIdAndFileTypeStatement + .valuesWithTransaction<SmallSourceIds<32>>( + directorySourceId, Storage::Synchronization::FileType::Directory); + + tracer.end(keyValue("source ids", sourceIds)); + + return sourceIds; +} + +void ProjectStorage::setPropertyEditorPathId(TypeId typeId, SourceId pathId) +{ + Sqlite::ImmediateSessionTransaction transaction{database}; + + s->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 = s->selectPropertyEditorPathIdStatement.valueWithTransaction<SourceId>(typeId); + + tracer.end(keyValue("source id", sourceId)); + + return sourceId; +} + +Storage::Imports ProjectStorage::fetchDocumentImports() const +{ + NanotraceHR::Tracer tracer{"fetch document imports"_t, projectStorageCategory()}; + + return s->selectAllDocumentImportForSourceIdStatement.valuesWithTransaction<Storage::Imports>(); +} + +void ProjectStorage::resetForTestsOnly() +{ + database.clearAllTablesForTestsOnly(); + commonTypeCache_.clearForTestsOnly(); + moduleCache.clearForTestOnly(); +} + +ModuleId ProjectStorage::fetchModuleId(Utils::SmallStringView moduleName, + Storage::ModuleKind moduleKind) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch module id"_t, + projectStorageCategory(), + keyValue("module name", moduleName), + keyValue("module kind", moduleKind)}; + + auto moduleId = Sqlite::withDeferredTransaction(database, [&] { + return fetchModuleIdUnguarded(moduleName, moduleKind); + }); + + tracer.end(keyValue("module id", moduleId)); + + return moduleId; +} + +Storage::Module ProjectStorage::fetchModule(ModuleId id) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch module name"_t, + projectStorageCategory(), + keyValue("module id", id)}; + + auto module = Sqlite::withDeferredTransaction(database, [&] { return fetchModuleUnguarded(id); }); + + tracer.end(keyValue("module name", module.name)); + tracer.end(keyValue("module name", module.kind)); + + return module; +} + +ProjectStorage::ModuleCacheEntries ProjectStorage::fetchAllModules() const +{ + NanotraceHR::Tracer tracer{"fetch all modules"_t, projectStorageCategory()}; + + return s->selectAllModulesStatement.valuesWithTransaction<ModuleCacheEntry, 128>(); +} + +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 s->selectTypeIdsForSourceIdsStatement.values<TypeId, 128>(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)}; + + s->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); + } + + 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 = s->selectTypeAnnotationsForSourceIdsStatement.range<TypeAnnotationView>( + toIntegers(updatedTypeAnnotationSourceIds)); + + auto insert = [&](const TypeAnnotation &annotation) { + if (!annotation.sourceId) + throw TypeAnnotationHasInvalidSourceId{}; + + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert type annotations"_t, + projectStorageCategory(), + keyValue("type annotation", annotation)}; + + s->insertTypeAnnotationStatement.write(annotation.typeId, + annotation.sourceId, + annotation.directorySourceId, + annotation.typeName, + annotation.iconPath, + createEmptyAsNull(annotation.itemLibraryJson), + createEmptyAsNull(annotation.hintsJson)); + + synchronizeTypeTraits(annotation.typeId, annotation.traits); + }; + + auto update = [&](const TypeAnnotationView &annotationFromDatabase, + const TypeAnnotation &annotation) { + + if (annotationFromDatabase.typeName != annotation.typeName + || 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)}; + + s->updateTypeAnnotationStatement.write(annotation.typeId, + annotation.typeName, + annotation.iconPath, + createEmptyAsNull(annotation.itemLibraryJson), + createEmptyAsNull(annotation.hintsJson)); + + synchronizeTypeTraits(annotation.typeId, annotation.traits); + + return Sqlite::UpdateChange::Update; + } + + synchronizeTypeTraits(annotation.typeId, annotation.traits); + + return Sqlite::UpdateChange::No; + }; + + auto remove = [&](const TypeAnnotationView &annotationFromDatabase) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"remove type annotations"_t, + projectStorageCategory(), + keyValue("type annotation", annotationFromDatabase)}; + + auto prototypeAnnotationTraits = s->selectPrototypeAnnotationTraitsByTypeIdStatement + .value<long long>(annotationFromDatabase.typeId); + s->deleteTypeAnnotationStatement.write(annotationFromDatabase.typeId); + + s->updateTypeAnnotationTraitStatement.write(annotationFromDatabase.typeId, + prototypeAnnotationTraits); + }; + + Sqlite::insertUpdateDelete(range, typeAnnotations, compareKey, insert, update, remove); +} + +void ProjectStorage::synchronizeTypeTrait(const Storage::Synchronization::Type &type) +{ + s->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::synchronizeDirectoryInfos(Storage::Synchronization::DirectoryInfos &directoryInfos, + const SourceIds &updatedDirectoryInfoSourceIds) +{ + NanotraceHR::Tracer tracer{"synchronize directory infos"_t, projectStorageCategory()}; + + auto compareKey = [](auto &&first, auto &&second) { + auto directorySourceIdDifference = first.directorySourceId - second.directorySourceId; + if (directorySourceIdDifference != 0) + return directorySourceIdDifference; + + return first.sourceId - second.sourceId; + }; + + std::sort(directoryInfos.begin(), directoryInfos.end(), [&](auto &&first, auto &&second) { + return std::tie(first.directorySourceId, first.sourceId) + < std::tie(second.directorySourceId, second.sourceId); + }); + + auto range = s->selectDirectoryInfosForSourceIdsStatement.range<Storage::Synchronization::DirectoryInfo>( + toIntegers(updatedDirectoryInfoSourceIds)); + + auto insert = [&](const Storage::Synchronization::DirectoryInfo &directoryInfo) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"insert directory info"_t, + projectStorageCategory(), + keyValue("directory info", directoryInfo)}; + + if (!directoryInfo.directorySourceId) + throw DirectoryInfoHasInvalidProjectSourceId{}; + if (!directoryInfo.sourceId) + throw DirectoryInfoHasInvalidSourceId{}; + + s->insertDirectoryInfoStatement.write(directoryInfo.directorySourceId, + directoryInfo.sourceId, + directoryInfo.moduleId, + directoryInfo.fileType); + }; + + auto update = [&](const Storage::Synchronization::DirectoryInfo &directoryInfoFromDatabase, + const Storage::Synchronization::DirectoryInfo &directoryInfo) { + if (directoryInfoFromDatabase.fileType != directoryInfo.fileType + || !compareInvalidAreTrue(directoryInfoFromDatabase.moduleId, directoryInfo.moduleId)) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"update directory info"_t, + projectStorageCategory(), + keyValue("directory info", directoryInfo), + keyValue("directory info from database", + directoryInfoFromDatabase)}; + + s->updateDirectoryInfoStatement.write(directoryInfo.directorySourceId, + directoryInfo.sourceId, + directoryInfo.moduleId, + directoryInfo.fileType); + return Sqlite::UpdateChange::Update; + } + + return Sqlite::UpdateChange::No; + }; + + auto remove = [&](const Storage::Synchronization::DirectoryInfo &directoryInfo) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"remove directory info"_t, + projectStorageCategory(), + keyValue("directory info", directoryInfo)}; + + s->deleteDirectoryInfoStatement.write(directoryInfo.directorySourceId, directoryInfo.sourceId); + }; + + Sqlite::insertUpdateDelete(range, directoryInfos, 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 = s->selectFileStatusesForSourceIdsStatement.range<FileStatus>( + 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{}; + s->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)}; + + s->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)}; + + s->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, + Prototypes &relinkablePrototypes, + Prototypes &relinkableExtensions) +{ + NanotraceHR::Tracer tracer{"synchronize imports"_t, projectStorageCategory()}; + + synchromizeModuleExportedImports(moduleExportedImports, updatedModuleIds); + NanotraceHR::Tracer importTracer{"synchronize qml document imports"_t, projectStorageCategory()}; + synchronizeDocumentImports(imports, + updatedSourceIds, + Storage::Synchronization::ImportKind::Import, + Relink::No, + relinkablePrototypes, + relinkableExtensions); + importTracer.end(); + NanotraceHR::Tracer moduleDependenciesTracer{"synchronize module depdencies"_t, + projectStorageCategory()}; + synchronizeDocumentImports(moduleDependencies, + updatedModuleDependencySourceIds, + Storage::Synchronization::ImportKind::ModuleDependency, + Relink::Yes, + relinkablePrototypes, + relinkableExtensions); + 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 = s->selectModuleExportedImportsForSourceIdStatement + .range<Storage::Synchronization::ModuleExportedImportView>( + 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) { + s->insertModuleExportedImportWithVersionStatement.write(import.moduleId, + import.exportedModuleId, + 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); + } + }; + + auto update = [](const Storage::Synchronization::ModuleExportedImportView &, + const Storage::Synchronization::ModuleExportedImport &) { + return Sqlite::UpdateChange::No; + }; -template class QmlDesigner::ProjectStorage<Sqlite::Database>; + 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)); + + s->deleteModuleExportedImportStatement.write(view.moduleExportedImportId); + }; + + Sqlite::insertUpdateDelete(range, moduleExportedImports, compareKey, insert, update, remove); +} + +ModuleId ProjectStorage::fetchModuleIdUnguarded(Utils::SmallStringView name, + Storage::ModuleKind kind) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch module id ungarded"_t, + projectStorageCategory(), + keyValue("module name", name), + keyValue("module kind", kind)}; + + auto moduleId = s->selectModuleIdByNameStatement.value<ModuleId>(kind, name); + + if (!moduleId) + moduleId = s->insertModuleNameStatement.value<ModuleId>(kind, name); + + tracer.end(keyValue("module id", moduleId)); + + return moduleId; +} + +Storage::Module ProjectStorage::fetchModuleUnguarded(ModuleId id) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch module ungarded"_t, + projectStorageCategory(), + keyValue("module id", id)}; + + auto module = s->selectModuleStatement.value<Storage::Module>(id); + + if (!module) + throw ModuleDoesNotExists{}; + + tracer.end(keyValue("module name", module.name)); + tracer.end(keyValue("module name", module.kind)); + + return module; +} + +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 = s->selectPropertyNameStatement.value<Utils::SmallString>( + aliasPropertyDeclarationId); + Utils::SmallString aliasPropertyNameTail; + if (aliasPropertyDeclarationTailId) + aliasPropertyNameTail = s->selectPropertyNameStatement.value<Utils::SmallString>( + aliasPropertyDeclarationTailId); + + relinkableAliasPropertyDeclarations.emplace_back(TypeId{typeId_}, + PropertyDeclarationId{propertyDeclarationId}, + ImportedTypeNameId{propertyImportedTypeNameId}, + std::move(aliasPropertyName), + std::move(aliasPropertyNameTail)); + + s->updateAliasPropertyDeclarationToNullStatement.write(propertyDeclarationId); + }; + + s->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)}; + + s->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) { + if (prototypeNameId) + relinkablePrototypes.emplace_back(typeId, prototypeNameId); + }; + + s->updatePrototypeIdToTypeIdStatement.readCallback(callback, prototypeId, unresolvedTypeId); +} + +void ProjectStorage::handlePrototypesWithExportedTypeNameAndTypeId( + Utils::SmallStringView exportedTypeName, TypeId typeId, Prototypes &relinkablePrototypes) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"handle invalid prototypes"_t, + projectStorageCategory(), + keyValue("type id", exportedTypeName), + keyValue("relinkable prototypes", relinkablePrototypes)}; + + auto callback = [&](TypeId typeId, ImportedTypeNameId prototypeNameId) { + relinkablePrototypes.emplace_back(typeId, prototypeNameId); + }; + + s->selectTypeIdAndPrototypeNameIdForPrototypeIdAndTypeNameStatement.readCallback(callback, + exportedTypeName, + typeId); +} + +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) { + if (extensionNameId) + relinkableExtensions.emplace_back(typeId, extensionNameId); + }; + + s->updateExtensionIdToTypeIdStatement.readCallback(callback, extensionId, unresolvedTypeId); +} + +void ProjectStorage::handleExtensionsWithExportedTypeNameAndTypeId( + Utils::SmallStringView exportedTypeName, TypeId typeId, Prototypes &relinkableExtensions) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"handle invalid extensions"_t, + projectStorageCategory(), + keyValue("type id", exportedTypeName), + keyValue("relinkable extensions", relinkableExtensions)}; + + auto callback = [&](TypeId typeId, ImportedTypeNameId extensionNameId) { + relinkableExtensions.emplace_back(typeId, extensionNameId); + }; + + s->selectTypeIdForExtensionIdAndTypeNameStatement.readCallback(callback, exportedTypeName, typeId); +} + +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); + 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, + 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); + + s->updatePropertyDeclarationWithAliasAndTypeStatement.write(alias.propertyDeclarationId, + propertyTypeId, + propertyTraits, + alias.aliasImportedTypeNameId, + aliasId); + }, + TypeCompare<AliasPropertyDeclaration>{}); +} + +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)}; + + s->updatePropertyDeclarationTypeStatement.write(property.propertyDeclarationId, + propertyTypeId); + }, + TypeCompare<PropertyDeclaration>{}); +} + +template<typename Callable> +void ProjectStorage::relinkPrototypes(Prototypes &relinkablePrototypes, + const TypeIds &deletedTypeIds, + Callable updateStatement) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"relink prototypes"_t, + projectStorageCategory(), + keyValue("relinkable prototypes", relinkablePrototypes), + keyValue("deleted type ids", deletedTypeIds)}; + + std::sort(relinkablePrototypes.begin(), relinkablePrototypes.end()); + relinkablePrototypes.erase(std::unique(relinkablePrototypes.begin(), relinkablePrototypes.end()), + relinkablePrototypes.end()); + + Utils::set_greedy_difference( + relinkablePrototypes.cbegin(), + relinkablePrototypes.cend(), + deletedTypeIds.begin(), + deletedTypeIds.end(), + [&](const Prototype &prototype) { + TypeId prototypeId = fetchTypeId(prototype.prototypeNameId); + + if (!prototypeId) + errorNotifier->typeNameCannotBeResolved(fetchImportedTypeName(prototype.prototypeNameId), + fetchTypeSourceId(prototype.typeId)); + + updateStatement(prototype.typeId, prototypeId); + checkForPrototypeChainCycle(prototype.typeId); + }, + TypeCompare<Prototype>{}); +} + +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); + }; + + s->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) { + s->updateTypePrototypeStatement.write(typeId, prototypeId); + }); + relinkPrototypes(relinkableExtensions, deletedTypeIds, [&](TypeId typeId, TypeId prototypeId) { + s->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); + + s->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) { + s->updatetPropertiesDeclarationValuesOfAliasStatement.write( + aliasDeclaration.propertyDeclarationId); + s->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 = s->selectExportedTypesForSourceIdsStatement + .range<Storage::Synchronization::ExportedTypeView>(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) { + s->insertExportedTypeNamesWithVersionStatement.write(type.moduleId, + type.name, + type.version.major.value, + type.version.minor.value, + type.typeId); + + } else if (type.version.major) { + s->insertExportedTypeNamesWithMajorVersionStatement.write(type.moduleId, + type.name, + type.version.major.value, + type.typeId); + } else { + s->insertExportedTypeNamesWithoutVersionStatement.write(type.moduleId, + type.name, + type.typeId); + } + } catch (const Sqlite::ConstraintPreventsModification &) { + throw QmlDesigner::ExportedTypeCannotBeInserted{type.name}; + } + + handlePrototypesWithExportedTypeNameAndTypeId(type.name, unresolvedTypeId, relinkablePrototypes); + handleExtensionsWithExportedTypeNameAndTypeId(type.name, unresolvedTypeId, relinkableExtensions); + }; + + auto update = [&](const Storage::Synchronization::ExportedTypeView &view, + 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); + s->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); + + s->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; + }; + + s->insertAliasPropertyDeclarationStatement.readCallback(callback, typeId, value.name); +} + +QVarLengthArray<PropertyDeclarationId, 128> ProjectStorage::fetchPropertyDeclarationIds( + TypeId baseTypeId) const +{ + QVarLengthArray<PropertyDeclarationId, 128> propertyDeclarationIds; + + s->selectLocalPropertyDeclarationIdsForTypeStatement.readTo(propertyDeclarationIds, baseTypeId); + + auto range = s->selectPrototypeAndExtensionIdsStatement.range<TypeId>(baseTypeId); + + for (TypeId prototype : range) { + s->selectLocalPropertyDeclarationIdsForTypeStatement.readTo(propertyDeclarationIds, prototype); + } + + return propertyDeclarationIds; +} + +PropertyDeclarationId ProjectStorage::fetchNextPropertyDeclarationId( + TypeId baseTypeId, Utils::SmallStringView propertyName) const +{ + auto range = s->selectPrototypeAndExtensionIdsStatement.range<TypeId>(baseTypeId); + + for (TypeId prototype : range) { + auto propertyDeclarationId = s->selectPropertyDeclarationIdByTypeIdAndNameStatement + .value<PropertyDeclarationId>(prototype, propertyName); + + if (propertyDeclarationId) + return propertyDeclarationId; + } + + return PropertyDeclarationId{}; +} + +PropertyDeclarationId ProjectStorage::fetchPropertyDeclarationId(TypeId typeId, + Utils::SmallStringView propertyName) const +{ + auto propertyDeclarationId = s->selectPropertyDeclarationIdByTypeIdAndNameStatement + .value<PropertyDeclarationId>(typeId, propertyName); + + if (propertyDeclarationId) + return propertyDeclarationId; + + return fetchNextPropertyDeclarationId(typeId, propertyName); +} + +PropertyDeclarationId ProjectStorage::fetchNextDefaultPropertyDeclarationId(TypeId baseTypeId) const +{ + auto range = s->selectPrototypeAndExtensionIdsStatement.range<TypeId>(baseTypeId); + + for (TypeId prototype : range) { + auto propertyDeclarationId = s->selectDefaultPropertyDeclarationIdStatement + .value<PropertyDeclarationId>(prototype); + + if (propertyDeclarationId) + return propertyDeclarationId; + } + + return PropertyDeclarationId{}; +} + +PropertyDeclarationId ProjectStorage::fetchDefaultPropertyDeclarationId(TypeId typeId) const +{ + auto propertyDeclarationId = s->selectDefaultPropertyDeclarationIdStatement + .value<PropertyDeclarationId>(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 = s->insertPropertyDeclarationStatement.value<PropertyDeclarationId>( + typeId, value.name, propertyTypeId, value.traits, propertyImportedTypeNameId); + + auto nextPropertyDeclarationId = fetchNextPropertyDeclarationId(typeId, value.name); + if (nextPropertyDeclarationId) { + s->updateAliasIdPropertyDeclarationStatement.write(nextPropertyDeclarationId, + propertyDeclarationId); + s->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; + + 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")); + + 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 = s->selectPropertyDeclarationsForTypeIdStatement + .range<Storage::Synchronization::PropertyDeclarationView>(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) { + s->updateAliasPropertyDeclarationByAliasPropertyDeclarationIdStatement + .write(nextPropertyDeclarationId, view.id); + } + + s->updateDefaultPropertyIdToNullStatement.write(view.id); + s->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 = s->selectPropertyDeclarationsWithAliasForTypeIdStatement + .range<AliasPropertyDeclarationView>(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)}; + + s->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<AliasPropertyDeclaration>{}); +} + +void ProjectStorage::handlePrototypesWithSourceIdAndPrototypeId(SourceId sourceId, + TypeId prototypeId, + Prototypes &relinkablePrototypes) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"handle prototypes with source id and prototype id"_t, + projectStorageCategory(), + keyValue("source id", sourceId), + keyValue("type id", prototypeId)}; + + auto callback = [&](TypeId typeId, ImportedTypeNameId prototypeNameId) { + if (prototypeNameId) + relinkablePrototypes.emplace_back(typeId, prototypeNameId); + }; + + s->selectTypeIdAndPrototypeNameIdForPrototypeIdAndSourceIdStatement.readCallback(callback, + prototypeId, + sourceId); +} + +void ProjectStorage::handlePrototypesAndExtensionsWithSourceId(SourceId sourceId, + TypeId prototypeId, + TypeId extensionId, + Prototypes &relinkablePrototypes, + Prototypes &relinkableExtensions) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"handle prototypes with source id"_t, + projectStorageCategory(), + keyValue("source id", sourceId), + keyValue("prototype id", prototypeId), + keyValue("extension id", extensionId)}; + + auto callback = + [&](TypeId typeId, ImportedTypeNameId prototypeNameId, ImportedTypeNameId extensionNameId) { + if (prototypeNameId) + relinkablePrototypes.emplace_back(typeId, prototypeNameId); + if (extensionNameId) + relinkableExtensions.emplace_back(typeId, extensionNameId); + }; + + s->updatePrototypeIdAndExtensionIdToTypeIdForSourceIdStatement.readCallback(callback, + sourceId, + prototypeId, + extensionId); +} + +void ProjectStorage::handleExtensionsWithSourceIdAndExtensionId(SourceId sourceId, + TypeId extensionId, + Prototypes &relinkableExtensions) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"handle prototypes with source id and prototype id"_t, + projectStorageCategory(), + keyValue("source id", sourceId), + keyValue("type id", extensionId)}; + + auto callback = [&](TypeId typeId, ImportedTypeNameId extensionNameId) { + if (extensionNameId) + relinkableExtensions.emplace_back(typeId, extensionNameId); + }; + + s->selectTypeIdAndExtensionNameIdForExtensionIdAndSourceIdStatement.readCallback(callback, + extensionId, + sourceId); +} + +ImportId ProjectStorage::insertDocumentImport(const Storage::Import &import, + Storage::Synchronization::ImportKind importKind, + ModuleId sourceModuleId, + ImportId parentImportId, + Relink relink, + Prototypes &relinkablePrototypes, + Prototypes &relinkableExtensions) +{ + if (relink == Relink::Yes) { + handlePrototypesWithSourceIdAndPrototypeId(import.sourceId, + unresolvedTypeId, + relinkablePrototypes); + handleExtensionsWithSourceIdAndExtensionId(import.sourceId, + unresolvedTypeId, + relinkableExtensions); + } + + if (import.version.minor) { + return s->insertDocumentImportWithVersionStatement.value<ImportId>(import.sourceId, + import.moduleId, + sourceModuleId, + importKind, + import.version.major.value, + import.version.minor.value, + parentImportId); + } else if (import.version.major) { + return s->insertDocumentImportWithMajorVersionStatement.value<ImportId>(import.sourceId, + import.moduleId, + sourceModuleId, + importKind, + import.version.major.value, + parentImportId); + } else { + return s->insertDocumentImportWithoutVersionStatement.value<ImportId>(import.sourceId, + import.moduleId, + sourceModuleId, + importKind, + parentImportId); + } +} + +void ProjectStorage::synchronizeDocumentImports(Storage::Imports &imports, + const SourceIds &updatedSourceIds, + Storage::Synchronization::ImportKind importKind, + Relink relink, + Prototypes &relinkablePrototypes, + Prototypes &relinkableExtensions) +{ + std::sort(imports.begin(), imports.end(), [](auto &&first, auto &&second) { + return std::tie(first.sourceId, first.moduleId, first.version) + < std::tie(second.sourceId, second.moduleId, second.version); + }); + + auto range = s->selectDocumentImportForSourceIdStatement + .range<Storage::Synchronization::ImportView>(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{}, + relink, + relinkablePrototypes, + relinkableExtensions); + 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, + relink, + relinkablePrototypes, + relinkableExtensions); + + tracer.end(keyValue("import id", indirectImportId)); + }; + + s->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)}; + + s->deleteDocumentImportStatement.write(view.importId); + s->deleteDocumentImportsWithParentImportIdStatement.write(view.sourceId, view.importId); + if (relink == Relink::Yes) { + handlePrototypesAndExtensionsWithSourceId(view.sourceId, + unresolvedTypeId, + unresolvedTypeId, + relinkablePrototypes, + relinkableExtensions); + } + }; + + Sqlite::insertUpdateDelete(range, imports, compareKey, insert, update, remove); +} + +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(Utils::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 s->selectTypeIdByModuleIdAndExportedNameStatement.value<TypeId>(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 = s->selectPropertyEditorPathsForForSourceIdsStatement.range<PropertyEditorQmlPathView>( + 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) + s->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) { + s->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)}; + + s->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 = s->selectFunctionDeclarationsForTypeIdStatement + .range<Storage::Synchronization::FunctionDeclarationView>(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)}; + + s->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; + + s->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)}; + + s->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 = s->selectSignalDeclarationsForTypeIdStatement + .range<Storage::Synchronization::SignalDeclarationView>(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)}; + + s->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)}; + + s->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 = s->selectEnumerationDeclarationsForTypeIdStatement + .range<Storage::Synchronization::EnumerationDeclarationView>(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)}; + + s->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; + + s->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)}; + + s->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 = s->selectTypeIdBySourceIdStatement.value<TypeId>(type.sourceId); + + tracer.end(keyValue("type id", type.typeId)); + + return type.typeId; + } + + type.typeId = s->insertTypeStatement.value<TypeId>(type.sourceId, type.typeName); + + if (!type.typeId) + type.typeId = s->selectTypeIdBySourceIdAndNameStatement.value<TypeId>(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<PropertyDeclaration>{}); +} + +void ProjectStorage::syncDefaultProperties(Storage::Synchronization::Types &types) +{ + NanotraceHR::Tracer tracer{"synchronize default properties"_t, projectStorageCategory()}; + + auto range = s->selectTypesWithDefaultPropertyStatement.range<TypeWithDefaultPropertyView>(); + + 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; + + s->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 = s->selectTypesWithDefaultPropertyStatement.range<TypeWithDefaultPropertyView>(); + + 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; + + s->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{}; + }; + + s->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{}; + }; + + s->selectPropertyDeclarationIdsForAliasChainStatement.readCallback(callback, + propertyDeclarationId); +} + +std::pair<TypeId, ImportedTypeNameId> ProjectStorage::fetchImportedTypeNameIdAndTypeId( + const Storage::Synchronization::ImportedTypeName &importedTypeName, SourceId sourceId) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch imported type name id and type id"_t, + projectStorageCategory(), + keyValue("imported type name", importedTypeName), + keyValue("source id", sourceId)}; + + TypeId typeId; + ImportedTypeNameId typeNameId; + auto typeName = std::visit([](auto &&importedTypeName) { return importedTypeName.name; }, + importedTypeName); + if (!typeName.empty()) { + typeNameId = fetchImportedTypeNameId(importedTypeName, sourceId); + + typeId = fetchTypeId(typeNameId); + + tracer.end(keyValue("type id", typeId), keyValue("type name id", typeNameId)); + + if (!typeId) { + errorNotifier->typeNameCannotBeResolved(typeName, sourceId); + return {unresolvedTypeId, typeNameId}; + } + } + + 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); + + s->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<Prototype>{}); + removeRelinkableEntries(relinkableExtensions, typeIds, TypeCompare<Prototype>{}); +} + +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 = s->selectImportIdBySourceIdAndModuleIdAndVersionStatement.value<ImportId>( + sourceId, import.moduleId, import.version.major.value, import.version.minor.value); + } else if (import.version.major) { + importId = s->selectImportIdBySourceIdAndModuleIdAndMajorVersionStatement + .value<ImportId>(sourceId, import.moduleId, import.version.major.value); + } else { + importId = s->selectImportIdBySourceIdAndModuleIdStatement.value<ImportId>(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 = s->selectKindFromImportedTypeNamesStatement.value<Storage::Synchronization::TypeNameKind>( + 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 s->selectNameFromImportedTypeNamesStatement.value<Utils::SmallString>(typeNameId); +} + +SourceId ProjectStorage::fetchTypeSourceId(TypeId typeId) const +{ + return s->selectSourceIdByTypeIdStatement.value<SourceId>(typeId); +} + +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 = s->selectTypeIdForImportedTypeNameNamesStatement.value<UnresolvedTypeId>(typeNameId); + } else { + typeId = s->selectTypeIdForQualifiedImportedTypeNameNamesStatement.value<UnresolvedTypeId>( + typeNameId); + } + + tracer.end(keyValue("type id", typeId)); + + return typeId; +} + +std::optional<ProjectStorage::FetchPropertyDeclarationResult> +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 = s->selectPropertyDeclarationResultByPropertyDeclarationIdStatement + .optionalValue<FetchPropertyDeclarationResult>( + 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 = s->selectSourceContextIdFromSourceContextsBySourceContextPathStatement + .value<SourceContextId>(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)}; + + s->insertIntoSourceContextsStatement.write(sourceContextPath); + + auto sourceContextId = SourceContextId::create(static_cast<int>(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)}; + + s->insertIntoSourcesStatement.write(sourceContextId, sourceName); + + auto sourceId = SourceId::create(static_cast<int>(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 = s->selectSourceIdFromSourcesBySourceContextIdAndSourceNameStatement + .value<SourceId>(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 = s->selectExportedTypesByTypeIdStatement + .values<Storage::Synchronization::ExportedType, 12>(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 = s->selectPropertyDeclarationsByTypeIdStatement + .values<Storage::Synchronization::PropertyDeclaration, 24>(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 = s->selectFunctionParameterDeclarationsStatement + .values<Storage::Synchronization::ParameterDeclaration, 8>( + functionDeclarationId); + }; + + s->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 = s->selectSignalParameterDeclarationsStatement + .values<Storage::Synchronization::ParameterDeclaration, 8>( + signalDeclarationId); + }; + + s->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, + s->selectEnumeratorDeclarationStatement + .values<Storage::Synchronization::EnumeratorDeclaration, 8>(enumerationDeclarationId)); + }; + + s->selectEnumerationDeclarationsForTypeIdWithoutEnumeratorDeclarationsStatement + .readCallback(callback, typeId); + + tracer.end(keyValue("enumeration declarations", enumerationDeclarations)); + + return enumerationDeclarations; +} + +template<typename... TypeIds> +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<TypeId, TypeIds>) &&...), "Parameter must be a TypeId!"); + + if (((typeId == baseTypeIds) || ...)) { + tracer.end(keyValue("is based on", true)); + return true; + } + + auto range = s->selectPrototypeAndExtensionIdsStatement.rangeWithTransaction<TypeId>(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<typename Id> +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<ImportedTypeNameId>(kind, + id, + typeName); + + if (!importedTypeNameId) + importedTypeNameId = s->insertImportedTypeNameIdStatement.value<ImportedTypeNameId>(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 a770577a65..54d9101596 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -4,11 +4,15 @@ #pragma once #include "commontypecache.h" +#include "projectstorageerrornotifier.h" #include "projectstorageexceptions.h" #include "projectstorageinterface.h" +#include "projectstoragetypes.h" #include "sourcepathcachetypes.h" #include "storagecache.h" +#include <tracing/qmldesignertracing.h> + #include <sqlitealgorithms.h> #include <sqlitedatabase.h> #include <sqlitetable.h> @@ -28,516 +32,167 @@ 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::StringViewCategory<projectStorageTracingStatus()> &projectStorageCategory(); +using ProjectStorageTracing::projectStorageCategory; -template<typename Database> class ProjectStorage final : public ProjectStorageInterface { - friend Storage::Info::CommonTypeCache<Database>; - -public: - template<int ResultCount, int BindParameterCount = 0> - using ReadStatement = typename Database::template ReadStatement<ResultCount, BindParameterCount>; - template<int ResultCount, int BindParameterCount = 0> - using ReadWriteStatement = typename Database::template ReadWriteStatement<ResultCount, BindParameterCount>; - template<int BindParameterCount> - using WriteStatement = typename Database::template WriteStatement<BindParameterCount>; - - ProjectStorage(Database &database, bool isInitialized) - : database{database} - , exclusiveTransaction{database} - , initializer{database, isInitialized} - { - NanotraceHR::Tracer tracer{"initialize"_t, projectStorageCategory()}; + using Database = Sqlite::Database; + friend Storage::Info::CommonTypeCache<ProjectStorageType>; - exclusiveTransaction.commit(); + enum class Relink { No, Yes }; - database.walCheckpointFull(); - - moduleCache.populate(); - } - - void synchronize(Storage::Synchronization::SynchronizationPackage package) override - { - 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); - } +public: + ProjectStorage(Database &database, + ProjectStorageErrorNotifierInterface &errorNotifier, + bool isInitialized); + ~ProjectStorage(); - void synchronizeDocumentImports(Storage::Imports imports, SourceId sourceId) override - { - NanotraceHR::Tracer tracer{"synchronize document imports"_t, projectStorageCategory()}; + void synchronize(Storage::Synchronization::SynchronizationPackage package) override; - Sqlite::withImmediateTransaction(database, [&] { - synchronizeDocumentImports(imports, - {sourceId}, - Storage::Synchronization::ImportKind::Import); - }); - } + void synchronizeDocumentImports(Storage::Imports imports, SourceId sourceId) override; - void addObserver(ProjectStorageObserver *observer) override { observers.push_back(observer); } - - void removeObserver(ProjectStorageObserver *observer) override + void setErrorNotifier(ProjectStorageErrorNotifierInterface &errorNotifier) { - observers.removeOne(observer); + this->errorNotifier = &errorNotifier; } - ModuleId moduleId(Utils::SmallStringView moduleName) const override - { - NanotraceHR::Tracer tracer{"get module id"_t, projectStorageCategory()}; - - return moduleCache.id(moduleName); - } + void addObserver(ProjectStorageObserver *observer) override; - Utils::SmallString moduleName(ModuleId moduleId) const - { - NanotraceHR::Tracer tracer{"get module name"_t, projectStorageCategory()}; + void removeObserver(ProjectStorageObserver *observer) override; - if (!moduleId) - throw ModuleDoesNotExists{}; + ModuleId moduleId(Utils::SmallStringView moduleName, Storage::ModuleKind kind) const override; - return moduleCache.value(moduleId); - } + Storage::Module module(ModuleId moduleId) const override; TypeId typeId(ModuleId moduleId, Utils::SmallStringView exportedTypeName, - Storage::Version version) const override - { - NanotraceHR::Tracer tracer{"get type id by exported name"_t, projectStorageCategory()}; + Storage::Version version) const override; - if (version.minor) - return selectTypeIdByModuleIdAndExportedNameAndVersionStatement - .template valueWithTransaction<TypeId>(moduleId, - exportedTypeName, - version.major.value, - version.minor.value); + TypeId typeId(ImportedTypeNameId typeNameId) const override; - if (version.major) - return selectTypeIdByModuleIdAndExportedNameAndMajorVersionStatement - .template valueWithTransaction<TypeId>(moduleId, exportedTypeName, version.major.value); + QVarLengthArray<TypeId, 256> typeIds(ModuleId moduleId) const override; - return selectTypeIdByModuleIdAndExportedNameStatement - .template valueWithTransaction<TypeId>(moduleId, exportedTypeName); - } - - TypeId typeId(ImportedTypeNameId typeNameId) const override - { - NanotraceHR::Tracer tracer{"get type id by imported type name"_t, projectStorageCategory()}; - - return Sqlite::withDeferredTransaction(database, [&] { return fetchTypeId(typeNameId); }); - } - - QVarLengthArray<TypeId, 256> typeIds(ModuleId moduleId) const override - { - NanotraceHR::Tracer tracer{"get type ids by module id"_t, projectStorageCategory()}; - - return selectTypeIdsByModuleIdStatement - .template valuesWithTransaction<QVarLengthArray<TypeId, 256>>(moduleId); - } - - Storage::Info::ExportedTypeNames exportedTypeNames(TypeId typeId) const override - { - NanotraceHR::Tracer tracer{"get exported type names by type id"_t, projectStorageCategory()}; + Storage::Info::ExportedTypeNames exportedTypeNames(TypeId typeId) const override; - return selectExportedTypesByTypeIdStatement - .template valuesWithTransaction<Storage::Info::ExportedTypeName, 4>(typeId); - } + 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()}; - - return selectExportedTypesByTypeIdAndSourceIdStatement - .template valuesWithTransaction<Storage::Info::ExportedTypeName, 4>(typeId, sourceId); - } + ImportId importId(const Storage::Import &import) const override; - ImportId importId(const Storage::Import &import) const override - { - NanotraceHR::Tracer tracer{"get import id by import"_t, projectStorageCategory()}; + ImportedTypeNameId importedTypeNameId(ImportId importId, Utils::SmallStringView typeName) override; - return Sqlite::withDeferredTransaction(database, [&] { - return fetchImportId(import.sourceId, import); - }); - } + ImportedTypeNameId importedTypeNameId(SourceId sourceId, Utils::SmallStringView typeName) override; - ImportedTypeNameId importedTypeNameId(ImportId importId, - Utils::SmallStringView typeName) override - { - NanotraceHR::Tracer tracer{"get imported type name id by import id"_t, - projectStorageCategory()}; + QVarLengthArray<PropertyDeclarationId, 128> propertyDeclarationIds(TypeId typeId) const override; - return Sqlite::withDeferredTransaction(database, [&] { - return fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::QualifiedExported, - importId, - typeName); - }); - } - - ImportedTypeNameId importedTypeNameId(SourceId sourceId, - Utils::SmallStringView typeName) override - { - NanotraceHR::Tracer tracer{"get imported type name id by source id"_t, - projectStorageCategory()}; - - return Sqlite::withDeferredTransaction(database, [&] { - return fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::Exported, - sourceId, - typeName); - }); - } - - QVarLengthArray<PropertyDeclarationId, 128> propertyDeclarationIds(TypeId typeId) const override - { - NanotraceHR::Tracer tracer{"get property declaration ids"_t, projectStorageCategory()}; - - return selectPropertyDeclarationIdsForTypeStatement - .template valuesWithTransaction<QVarLengthArray<PropertyDeclarationId, 128>>(typeId); - } - - QVarLengthArray<PropertyDeclarationId, 128> localPropertyDeclarationIds(TypeId typeId) const override - { - NanotraceHR::Tracer tracer{"get local property declaration ids"_t, projectStorageCategory()}; - - return selectLocalPropertyDeclarationIdsForTypeStatement - .template valuesWithTransaction<QVarLengthArray<PropertyDeclarationId, 128>>(typeId); - } + QVarLengthArray<PropertyDeclarationId, 128> localPropertyDeclarationIds(TypeId typeId) const override; PropertyDeclarationId propertyDeclarationId(TypeId typeId, - Utils::SmallStringView propertyName) const override - { - NanotraceHR::Tracer tracer{"get property declaration id"_t, projectStorageCategory()}; - - return selectPropertyDeclarationIdForTypeAndPropertyNameStatement - .template valueWithTransaction<PropertyDeclarationId>(typeId, propertyName); - } + Utils::SmallStringView propertyName) const override; PropertyDeclarationId localPropertyDeclarationId(TypeId typeId, - Utils::SmallStringView propertyName) const - { - NanotraceHR::Tracer tracer{"get local property declaration id"_t, projectStorageCategory()}; + Utils::SmallStringView propertyName) const; - return selectLocalPropertyDeclarationIdForTypeAndPropertyNameStatement - .template valueWithTransaction<PropertyDeclarationId>(typeId, propertyName); - } + PropertyDeclarationId defaultPropertyDeclarationId(TypeId typeId) const override; std::optional<Storage::Info::PropertyDeclaration> propertyDeclaration( - PropertyDeclarationId propertyDeclarationId) const override - { - NanotraceHR::Tracer tracer{"get property declaration"_t, projectStorageCategory()}; - - return selectPropertyDeclarationForPropertyDeclarationIdStatement - .template optionalValueWithTransaction<Storage::Info::PropertyDeclaration>( - propertyDeclarationId); - } - - std::optional<Storage::Info::Type> type(TypeId typeId) const override - { - NanotraceHR::Tracer tracer{"get type"_t, projectStorageCategory()}; - - return selectInfoTypeByTypeIdStatement.template optionalValueWithTransaction<Storage::Info::Type>( - typeId); - } + PropertyDeclarationId propertyDeclarationId) const override; - Utils::PathString typeIconPath(TypeId typeId) const override - { - NanotraceHR::Tracer tracer{"get type icon path"_t, projectStorageCategory()}; - - return selectTypeIconPathStatement.template valueWithTransaction<Utils::PathString>(typeId); - } - - Storage::Info::TypeHints typeHints(TypeId typeId) const override - { - NanotraceHR::Tracer tracer{"get type hints"_t, projectStorageCategory()}; + std::optional<Storage::Info::Type> type(TypeId typeId) const override; - return selectTypeHintsStatement.template valuesWithTransaction<Storage::Info::TypeHints, 4>( - typeId); - } + Utils::PathString typeIconPath(TypeId typeId) const override; - Storage::Info::ItemLibraryEntries itemLibraryEntries(TypeId typeId) const override - { - NanotraceHR::Tracer tracer{"get item library entries by type id"_t, projectStorageCategory()}; - - using Storage::Info::ItemLibraryProperties; - Storage::Info::ItemLibraryEntries entries; - - auto callback = [&](TypeId typeId_, - Utils::SmallStringView name, - Utils::SmallStringView iconPath, - Utils::SmallStringView category, - Utils::SmallStringView import, - Utils::SmallStringView toolTip, - Utils::SmallStringView properties, - Utils::SmallStringView extraFilePaths, - Utils::SmallStringView templatePath) { - auto &last = entries.emplace_back( - typeId_, name, iconPath, category, import, toolTip, templatePath); - if (properties.size()) - selectItemLibraryPropertiesStatement.readTo(last.properties, properties); - if (extraFilePaths.size()) - selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths); - }; - - selectItemLibraryEntriesByTypeIdStatement.readCallbackWithTransaction(callback, typeId); - - return entries; - } + Storage::Info::TypeHints typeHints(TypeId typeId) const override; - Storage::Info::ItemLibraryEntries itemLibraryEntries(SourceId sourceId) const override - { - NanotraceHR::Tracer tracer{"get item library entries by source id"_t, - projectStorageCategory()}; + SmallSourceIds<4> typeAnnotationSourceIds(SourceId directoryId) const override; - using Storage::Info::ItemLibraryProperties; - Storage::Info::ItemLibraryEntries entries; - - auto callback = [&](TypeId typeId, - Utils::SmallStringView name, - Utils::SmallStringView iconPath, - Utils::SmallStringView category, - Utils::SmallStringView import, - Utils::SmallStringView toolTip, - Utils::SmallStringView properties, - Utils::SmallStringView extraFilePaths, - Utils::SmallStringView templatePath) { - auto &last = entries.emplace_back( - typeId, name, iconPath, category, import, toolTip, templatePath); - if (properties.size()) - selectItemLibraryPropertiesStatement.readTo(last.properties, properties); - if (extraFilePaths.size()) - selectItemLibraryExtraFilePathsStatement.readTo(last.extraFilePaths, extraFilePaths); - }; - - selectItemLibraryEntriesBySourceIdStatement.readCallbackWithTransaction(callback, sourceId); - - return entries; - } + SmallSourceIds<64> typeAnnotationDirectorySourceIds() const override; - Storage::Info::ItemLibraryEntries allItemLibraryEntries() const override - { - 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); - - return entries; - } + Storage::Info::ItemLibraryEntries itemLibraryEntries(TypeId typeId) const override; - std::vector<Utils::SmallString> signalDeclarationNames(TypeId typeId) const override - { - NanotraceHR::Tracer tracer{"get signal names"_t, projectStorageCategory()}; + Storage::Info::ItemLibraryEntries itemLibraryEntries(ImportId importId) const; - return selectSignalDeclarationNamesForTypeStatement - .template valuesWithTransaction<Utils::SmallString, 32>(typeId); - } + Storage::Info::ItemLibraryEntries itemLibraryEntries(SourceId sourceId) const override; - std::vector<Utils::SmallString> functionDeclarationNames(TypeId typeId) const override - { - NanotraceHR::Tracer tracer{"get function names"_t, projectStorageCategory()}; + Storage::Info::ItemLibraryEntries allItemLibraryEntries() const override; - return selectFuncionDeclarationNamesForTypeStatement - .template valuesWithTransaction<Utils::SmallString, 32>(typeId); - } + std::vector<Utils::SmallString> signalDeclarationNames(TypeId typeId) const override; - std::optional<Utils::SmallString> propertyName(PropertyDeclarationId propertyDeclarationId) const override - { - NanotraceHR::Tracer tracer{"get property name"_t, projectStorageCategory()}; + std::vector<Utils::SmallString> functionDeclarationNames(TypeId typeId) const override; - return selectPropertyNameStatement.template optionalValueWithTransaction<Utils::SmallString>( - propertyDeclarationId); - } + std::optional<Utils::SmallString> propertyName(PropertyDeclarationId propertyDeclarationId) const override; - const Storage::Info::CommonTypeCache<ProjectStorageInterface> &commonTypeCache() const override + const Storage::Info::CommonTypeCache<ProjectStorageType> &commonTypeCache() const override { return commonTypeCache_; } - template<const char *moduleName, const char *typeName> + template<const char *moduleName, const char *typeName, Storage::ModuleKind moduleKind = Storage::ModuleKind::QmlLibrary> 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})}; + + auto typeId = commonTypeCache_.typeId<moduleName, typeName, moduleKind>(); + + tracer.end(keyValue("type id", typeId)); - return commonTypeCache_.template typeId<moduleName, typeName>(); + return typeId; } template<typename BuiltinType> TypeId builtinTypeId() const { + using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"get builtin type id from common type cache"_t, projectStorageCategory()}; - return commonTypeCache_.template builtinTypeId<BuiltinType>(); + auto typeId = commonTypeCache_.builtinTypeId<BuiltinType>(); + + tracer.end(keyValue("type id", typeId)); + + return typeId; } template<const char *builtinType> TypeId builtinTypeId() const { + using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"get builtin type id from common type cache"_t, projectStorageCategory()}; - return commonTypeCache_.template builtinTypeId<builtinType>(); - } + auto typeId = commonTypeCache_.builtinTypeId<builtinType>(); - TypeIds prototypeIds(TypeId type) const override - { - NanotraceHR::Tracer tracer{"get prototypes"_t, projectStorageCategory()}; + tracer.end(keyValue("type id", typeId)); - return selectPrototypeIdsForTypeIdInOrderStatement.template valuesWithTransaction<TypeId, 16>( - type); + return typeId; } - TypeIds prototypeAndSelfIds(TypeId type) const override - { - NanotraceHR::Tracer tracer{"get prototypes and self"_t, projectStorageCategory()}; + SmallTypeIds<16> prototypeIds(TypeId type) const override; - return selectPrototypeAndSelfIdsForTypeIdInOrderStatement - .template valuesWithTransaction<TypeId, 16>(type); - } + SmallTypeIds<16> prototypeAndSelfIds(TypeId typeId) const override; - TypeIds heirIds(TypeId typeId) const override - { - NanotraceHR::Tracer tracer{"get heirs"_t, projectStorageCategory()}; - - return selectHeirTypeIdsStatement.template valuesWithTransaction<TypeId, 64>(typeId); - } + SmallTypeIds<64> heirIds(TypeId typeId) const override; template<typename... TypeIds> - bool isBasedOn_(TypeId typeId, TypeIds... baseTypeIds) const - { - NanotraceHR::Tracer tracer{"is based on"_t, projectStorageCategory()}; + bool isBasedOn_(TypeId typeId, TypeIds... baseTypeIds) const; - static_assert(((std::is_same_v<TypeId, TypeIds>) &&...), "Parameter must be a TypeId!"); + bool isBasedOn(TypeId) const; - if (((typeId == baseTypeIds) || ...)) - return true; + bool isBasedOn(TypeId typeId, TypeId id1) const override; - auto range = selectPrototypeIdsStatement.template rangeWithTransaction<TypeId>(typeId); + bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2) const override; - for ([[maybe_unused]] TypeId currentTypeId : range) { - if (((currentTypeId == baseTypeIds) || ...)) - return true; - } - - return false; - } + bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3) const override; - bool isBasedOn(TypeId typeId) const { return isBasedOn_(typeId); } + bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4) const override; - bool isBasedOn(TypeId typeId, TypeId id1) const override { return isBasedOn_(typeId, id1); } + bool isBasedOn(TypeId typeId, TypeId id1, TypeId id2, TypeId id3, TypeId id4, TypeId id5) 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, TypeId id3) const override - { - return isBasedOn_(typeId, id1, id2, id3); - } - - 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, 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, 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, @@ -546,316 +201,147 @@ 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 - { - NanotraceHR::Tracer tracer{"is based on"_t, projectStorageCategory()}; + TypeId fetchTypeIdByExportedName(Utils::SmallStringView name) const; - return selectTypeIdByExportedNameStatement.template valueWithTransaction<TypeId>(name); - } + TypeId fetchTypeIdByModuleIdsAndExportedName(ModuleIds moduleIds, + Utils::SmallStringView name) const; - TypeId fetchTypeIdByModuleIdsAndExportedName(ModuleIds moduleIds, Utils::SmallStringView name) const - { - return selectTypeIdByModuleIdsAndExportedNameStatement.template valueWithTransaction<TypeId>( - static_cast<void *>(moduleIds.data()), static_cast<long long>(moduleIds.size()), name); - } + TypeId fetchTypeIdByName(SourceId sourceId, Utils::SmallStringView name); - TypeId fetchTypeIdByName(SourceId sourceId, Utils::SmallStringView name) - { - return selectTypeIdBySourceIdAndNameStatement.template valueWithTransaction<TypeId>(sourceId, - name); - } + Storage::Synchronization::Type fetchTypeByTypeId(TypeId typeId); - Storage::Synchronization::Type fetchTypeByTypeId(TypeId typeId) - { - return Sqlite::withDeferredTransaction(database, [&] { - auto type = selectTypeByTypeIdStatement.template value<Storage::Synchronization::Type>( - typeId); - - type.exportedTypes = fetchExportedTypes(typeId); - type.propertyDeclarations = fetchPropertyDeclarations(type.typeId); - type.functionDeclarations = fetchFunctionDeclarations(type.typeId); - type.signalDeclarations = fetchSignalDeclarations(type.typeId); - type.enumerationDeclarations = fetchEnumerationDeclarations(type.typeId); - - return type; - }); - } - - Storage::Synchronization::Types fetchTypes() - { - return Sqlite::withDeferredTransaction(database, [&] { - auto types = selectTypesStatement.template values<Storage::Synchronization::Type, 64>(); - - for (Storage::Synchronization::Type &type : types) { - type.exportedTypes = fetchExportedTypes(type.typeId); - type.propertyDeclarations = fetchPropertyDeclarations(type.typeId); - type.functionDeclarations = fetchFunctionDeclarations(type.typeId); - type.signalDeclarations = fetchSignalDeclarations(type.typeId); - type.enumerationDeclarations = fetchEnumerationDeclarations(type.typeId); - } - - return types; - }); - } - - bool fetchIsProtype(TypeId type, TypeId prototype) - { - return bool(selectPrototypeIdStatement.template valueWithTransaction<TypeId>(type, prototype)); - } - - auto fetchPrototypes(TypeId type) - { - return selectPrototypeIdsInOrderStatement.template rangeWithTransaction<TypeId>(type); - } - - SourceContextId fetchSourceContextIdUnguarded(Utils::SmallStringView sourceContextPath) - { - NanotraceHR::Tracer tracer{"fetch source context id unguarded"_t, projectStorageCategory()}; - - auto sourceContextId = readSourceContextId(sourceContextPath); - - return sourceContextId ? sourceContextId : writeSourceContextId(sourceContextPath); - } - - SourceContextId fetchSourceContextId(Utils::SmallStringView sourceContextPath) - { - NanotraceHR::Tracer tracer{"fetch source context id"_t, projectStorageCategory()}; - - try { - return Sqlite::withDeferredTransaction(database, [&] { - return fetchSourceContextIdUnguarded(sourceContextPath); - }); - } catch (const Sqlite::ConstraintPreventsModification &) { - return fetchSourceContextId(sourceContextPath); - } - } + Storage::Synchronization::Types fetchTypes(); - Utils::PathString fetchSourceContextPath(SourceContextId sourceContextId) const - { - NanotraceHR::Tracer tracer{"fetch source context path"_t, projectStorageCategory()}; - - return Sqlite::withDeferredTransaction(database, [&] { - auto optionalSourceContextPath = selectSourceContextPathFromSourceContextsBySourceContextIdStatement - .template optionalValue<Utils::PathString>( - sourceContextId); - - if (!optionalSourceContextPath) - throw SourceContextIdDoesNotExists(); - - return std::move(*optionalSourceContextPath); - }); - } + SourceContextId fetchSourceContextIdUnguarded(Utils::SmallStringView sourceContextPath); - auto fetchAllSourceContexts() const - { - NanotraceHR::Tracer tracer{"fetch all source contexts"_t, projectStorageCategory()}; + SourceContextId fetchSourceContextId(Utils::SmallStringView sourceContextPath); - return selectAllSourceContextsStatement - .template valuesWithTransaction<Cache::SourceContext, 128>(); - } + Utils::PathString fetchSourceContextPath(SourceContextId sourceContextId) const; - SourceId fetchSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName) - { - NanotraceHR::Tracer tracer{"fetch source id"_t, projectStorageCategory()}; - - return Sqlite::withDeferredTransaction(database, [&] { - return fetchSourceIdUnguarded(sourceContextId, sourceName); - }); - } + Cache::SourceContexts fetchAllSourceContexts() const; - auto fetchSourceNameAndSourceContextId(SourceId sourceId) const - { - NanotraceHR::Tracer tracer{"fetch source name and source context id"_t, - projectStorageCategory()}; + SourceId fetchSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName); - auto value = selectSourceNameAndSourceContextIdFromSourcesBySourceIdStatement - .template valueWithTransaction<Cache::SourceNameAndSourceContextId>(sourceId); + Cache::SourceNameAndSourceContextId fetchSourceNameAndSourceContextId(SourceId sourceId) const; - if (!value.sourceContextId) - throw SourceIdDoesNotExists(); + void clearSources(); - return value; - } + SourceContextId fetchSourceContextId(SourceId sourceId) const; - void clearSources() - { - Sqlite::withImmediateTransaction(database, [&] { - deleteAllSourceContextsStatement.execute(); - deleteAllSourcesStatement.execute(); - }); - } + Cache::Sources fetchAllSources() const; - SourceContextId fetchSourceContextId(SourceId sourceId) const - { - NanotraceHR::Tracer tracer{"fetch source context id"_t, projectStorageCategory()}; + SourceId fetchSourceIdUnguarded(SourceContextId sourceContextId, + Utils::SmallStringView sourceName); - auto sourceContextId = selectSourceContextIdFromSourcesBySourceIdStatement - .template valueWithTransaction<SourceContextId>(sourceId); + FileStatuses fetchAllFileStatuses() const; - if (!sourceContextId) - throw SourceIdDoesNotExists(); + FileStatus fetchFileStatus(SourceId sourceId) const override; - return sourceContextId; - } + std::optional<Storage::Synchronization::DirectoryInfo> fetchDirectoryInfo(SourceId sourceId) const override; - auto fetchAllSources() const - { - NanotraceHR::Tracer tracer{"fetch all sources"_t, projectStorageCategory()}; + Storage::Synchronization::DirectoryInfos fetchDirectoryInfos(SourceId directorySourceId) const override; + Storage::Synchronization::DirectoryInfos fetchDirectoryInfos( + SourceId directorySourceId, Storage::Synchronization::FileType fileType) const override; + Storage::Synchronization::DirectoryInfos fetchDirectoryInfos(const SourceIds &directorySourceIds) const; + SmallSourceIds<32> fetchSubdirectorySourceIds(SourceId directorySourceId) const override; - return selectAllSourcesStatement.template valuesWithTransaction<Cache::Source, 1024>(); - } + void setPropertyEditorPathId(TypeId typeId, SourceId pathId); - SourceId fetchSourceIdUnguarded(SourceContextId sourceContextId, Utils::SmallStringView sourceName) - { - NanotraceHR::Tracer tracer{"fetch source id unguarded"_t, projectStorageCategory()}; + SourceId propertyEditorPathId(TypeId typeId) const override; - auto sourceId = readSourceId(sourceContextId, sourceName); + Storage::Imports fetchDocumentImports() const; - if (sourceId) - return sourceId; + void resetForTestsOnly(); - return writeSourceId(sourceContextId, sourceName); - } - - auto fetchAllFileStatuses() const - { - NanotraceHR::Tracer tracer{"fetch all file statuses"_t, projectStorageCategory()}; - - return selectAllFileStatusesStatement.template rangeWithTransaction<FileStatus>(); - } - - FileStatus fetchFileStatus(SourceId sourceId) const override - { - NanotraceHR::Tracer tracer{"fetch file status"_t, projectStorageCategory()}; - - return selectFileStatusesForSourceIdStatement.template valueWithTransaction<FileStatus>( - sourceId); - } - - std::optional<Storage::Synchronization::ProjectData> fetchProjectData(SourceId sourceId) const override - { - NanotraceHR::Tracer tracer{"fetch project data"_t, projectStorageCategory()}; - - return selectProjectDataForSourceIdStatement - .template optionalValueWithTransaction<Storage::Synchronization::ProjectData>(sourceId); - } - - Storage::Synchronization::ProjectDatas fetchProjectDatas(SourceId projectSourceId) const override - { - NanotraceHR::Tracer tracer{"fetch project datas by source id"_t, projectStorageCategory()}; - - return selectProjectDatasForSourceIdStatement - .template valuesWithTransaction<Storage::Synchronization::ProjectData, 1024>( - projectSourceId); - } - - Storage::Synchronization::ProjectDatas fetchProjectDatas(const SourceIds &projectSourceIds) const - { - NanotraceHR::Tracer tracer{"fetch project datas by source ids"_t, projectStorageCategory()}; - - return selectProjectDatasForSourceIdsStatement - .template valuesWithTransaction<Storage::Synchronization::ProjectData, 64>( - toIntegers(projectSourceIds)); - } - - void setPropertyEditorPathId(TypeId typeId, SourceId pathId) +private: + struct ModuleView { - Sqlite::ImmediateSessionTransaction transaction{database}; + ModuleView() = default; - upsertPropertyEditorPathIdStatement.write(typeId, pathId); + ModuleView(Utils::SmallStringView name, Storage::ModuleKind kind) + : name{name} + , kind{kind} + {} - transaction.commit(); - } + ModuleView(const Storage::Module &module) + : name{module.name} + , kind{module.kind} + {} - SourceId propertyEditorPathId(TypeId typeId) const override - { - return selectPropertyEditorPathIdStatement.template valueWithTransaction<SourceId>(typeId); - } + Utils::SmallStringView name; + Storage::ModuleKind kind; - Storage::Imports fetchDocumentImports() const - { - NanotraceHR::Tracer tracer{"fetch document imports"_t, projectStorageCategory()}; + friend bool operator<(ModuleView first, ModuleView second) + { + return std::tie(first.kind, first.name) < std::tie(second.kind, second.name); + } - return selectAllDocumentImportForSourceIdStatement - .template valuesWithTransaction<Storage::Imports>(); - } + friend bool operator==(const Storage::Module &first, ModuleView second) + { + return first.name == second.name && first.kind == second.kind; + } - void resetForTestsOnly() - { - database.clearAllTablesForTestsOnly(); - commonTypeCache_.clearForTestsOnly(); - moduleCache.clearForTestOnly(); - } + friend bool operator==(ModuleView first, const Storage::Module &second) + { + return second == first; + } + }; -private: class ModuleStorageAdapter { public: - auto fetchId(const Utils::SmallStringView name) { return storage.fetchModuleId(name); } + auto fetchId(ModuleView module) { return storage.fetchModuleId(module.name, module.kind); } - auto fetchValue(ModuleId id) { return storage.fetchModuleName(id); } + auto fetchValue(ModuleId id) { return storage.fetchModule(id); } auto fetchAll() { return storage.fetchAllModules(); } ProjectStorage &storage; }; - class Module : public StorageCacheEntry<Utils::PathString, Utils::SmallStringView, ModuleId> + friend ModuleStorageAdapter; + + static bool moduleNameLess(ModuleView first, ModuleView second) noexcept + { + return first < second; + } + + class ModuleCacheEntry : public StorageCacheEntry<Storage::Module, ModuleView, ModuleId> { - using Base = StorageCacheEntry<Utils::PathString, Utils::SmallStringView, ModuleId>; + using Base = StorageCacheEntry<Storage::Module, ModuleView, ModuleId>; public: using Base::Base; - friend bool operator==(const Module &first, const Module &second) + ModuleCacheEntry(Utils::SmallStringView name, Storage::ModuleKind kind, ModuleId moduleId) + : Base{{name, kind}, moduleId} + {} + + friend bool operator==(const ModuleCacheEntry &first, const ModuleCacheEntry &second) { return &first == &second && first.value == second.value; } - }; - friend ModuleStorageAdapter; + friend bool operator==(const ModuleCacheEntry &first, ModuleView second) + { + return first.value.name == second.name && first.value.kind == second.kind; + } + }; - static bool moduleNameLess(Utils::SmallStringView first, Utils::SmallStringView second) noexcept - { - return first < second; - } + using ModuleCacheEntries = std::vector<ModuleCacheEntry>; - using ModuleCache = StorageCache<Utils::PathString, - Utils::SmallStringView, - ModuleId, - ModuleStorageAdapter, - NonLockingMutex, - moduleNameLess, - Module>; + using ModuleCache + = StorageCache<Storage::Module, ModuleView, ModuleId, ModuleStorageAdapter, NonLockingMutex, moduleNameLess, ModuleCacheEntry>; - ModuleId fetchModuleId(Utils::SmallStringView moduleName) - { - return Sqlite::withDeferredTransaction(database, - [&] { return fetchModuleIdUnguarded(moduleName); }); - } + ModuleId fetchModuleId(Utils::SmallStringView moduleName, Storage::ModuleKind moduleKind); - auto fetchModuleName(ModuleId id) - { - return Sqlite::withDeferredTransaction(database, [&] { return fetchModuleNameUnguarded(id); }); - } + Storage::Module fetchModule(ModuleId id); - auto fetchAllModules() const - { - return selectAllModulesStatement.template valuesWithTransaction<Module, 128>(); - } + ModuleCacheEntries fetchAllModules() const; - void callRefreshMetaInfoCallback(const TypeIds &deletedTypeIds) - { - if (deletedTypeIds.size()) { - for (ProjectStorageObserver *observer : observers) - observer->removedTypeIds(deletedTypeIds); - } - } + void callRefreshMetaInfoCallback(const TypeIds &deletedTypeIds); class AliasPropertyDeclaration { @@ -882,6 +368,25 @@ private: < std::tie(second.typeId, second.propertyDeclarationId); } + template<typename String> + 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; @@ -910,6 +415,20 @@ private: < std::tie(second.typeId, second.propertyDeclarationId); } + template<typename String> + 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; @@ -931,6 +450,22 @@ private: return first.typeId < second.typeId; } + friend bool operator==(Prototype first, Prototype second) + { + return first.typeId == second.typeId; + } + + template<typename String> + 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; @@ -970,127 +505,67 @@ 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; - } - - TypeIds fetchTypeIds(const SourceIds &sourceIds) - { - return selectTypeIdsForSourceIdsStatement.template values<TypeId, 128>(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 unique(SourceIds &sourceIds); - void synchronizeTypeTraits(TypeId typeId, Storage::TypeTraits traits) - { - updateTypeAnnotationTraitStatement.write(typeId, traits.annotation); - } + void synchronizeTypeTraits(TypeId typeId, Storage::TypeTraits traits); class TypeAnnotationView { public: TypeAnnotationView(TypeId typeId, + Utils::SmallStringView typeName, Utils::SmallStringView iconPath, Utils::SmallStringView itemLibraryJson, Utils::SmallStringView hintsJson) : typeId{typeId} + , typeName{typeName} , iconPath{iconPath} , itemLibraryJson{itemLibraryJson} , hintsJson{hintsJson} {} + template<typename String> + friend void convertToString(String &string, const TypeAnnotationView &typeAnnotationView) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("type id", typeAnnotationView.typeId), + keyValue("type name", typeAnnotationView.typeName), + keyValue("icon path", typeAnnotationView.iconPath), + keyValue("item library json", typeAnnotationView.itemLibraryJson), + keyValue("hints json", typeAnnotationView.hintsJson)); + + convertToString(string, dict); + } + public: TypeId typeId; + Utils::SmallStringView typeName; Utils::SmallStringView iconPath; Utils::SmallStringView itemLibraryJson; Utils::PathString hintsJson; }; - void updateTypeIdInTypeAnnotations(Storage::Synchronization::TypeAnnotations &typeAnnotations) - { - for (auto &annotation : typeAnnotations) { - annotation.typeId = fetchTypeIdByModuleIdAndExportedName(annotation.moduleId, - annotation.typeName); - } - } + void updateTypeIdInTypeAnnotations(Storage::Synchronization::TypeAnnotations &typeAnnotations); - void synchronizeTypeAnnotations(Storage::Synchronization::TypeAnnotations &typeAnnotations, - const SourceIds &updatedTypeAnnotationSourceIds) + template<typename Value> + static Sqlite::ValueView createEmptyAsNull(const Value &value) { - 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; }; + if (value.size()) + return Sqlite::ValueView::create(value); - std::sort(typeAnnotations.begin(), typeAnnotations.end(), [&](auto &&first, auto &&second) { - return first.typeId < second.typeId; - }); - - auto range = selectTypeAnnotationsForSourceIdsStatement.template range<TypeAnnotationView>( - toIntegers(updatedTypeAnnotationSourceIds)); - - auto insert = [&](const TypeAnnotation &annotation) { - if (!annotation.sourceId) - throw TypeAnnotationHasInvalidSourceId{}; - - synchronizeTypeTraits(annotation.typeId, annotation.traits); - - insertTypeAnnotationStatement.write(annotation.typeId, - annotation.sourceId, - annotation.iconPath, - annotation.itemLibraryJson, - annotation.hintsJson); - }; - - auto update = [&](const TypeAnnotationView &annotationFromDatabase, - const TypeAnnotation &annotation) { - synchronizeTypeTraits(annotation.typeId, annotation.traits); - - if (annotationFromDatabase.iconPath != annotation.iconPath - || annotationFromDatabase.itemLibraryJson != annotation.itemLibraryJson - || annotationFromDatabase.hintsJson != annotation.hintsJson) { - updateTypeAnnotationStatement.write(annotation.typeId, - annotation.iconPath, - annotation.itemLibraryJson, - annotation.hintsJson); - return Sqlite::UpdateChange::Update; - } - - return Sqlite::UpdateChange::No; - }; - - auto remove = [&](const TypeAnnotationView &annotationFromDatabase) { - synchronizeTypeTraits(annotationFromDatabase.typeId, Storage::TypeTraits{}); - - deleteTypeAnnotationStatement.write(annotationFromDatabase.typeId); - }; - - Sqlite::insertUpdateDelete(range, typeAnnotations, compareKey, insert, update, remove); + return Sqlite::ValueView{}; } - void synchronizeTypeTrait(const Storage::Synchronization::Type &type) - { - updateTypeTraitStatement.write(type.typeId, type.traits.type); - } + void synchronizeTypeAnnotations(Storage::Synchronization::TypeAnnotations &typeAnnotations, + const SourceIds &updatedTypeAnnotationSourceIds); + + void synchronizeTypeTrait(const Storage::Synchronization::Type &type); void synchronizeTypes(Storage::Synchronization::Types &types, TypeIds &updatedTypeIds, @@ -1100,403 +575,62 @@ 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()}; - - 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<Storage::Synchronization::ProjectData>( - toIntegers(updatedProjectSourceIds)); - - auto insert = [&](const Storage::Synchronization::ProjectData &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)) { - 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) { - deleteProjectDataStatement.write(projectData.projectSourceId, projectData.sourceId); - }; - - Sqlite::insertUpdateDelete(range, projectDatas, compareKey, insert, update, remove); - } + void synchronizeDirectoryInfos(Storage::Synchronization::DirectoryInfos &directoryInfos, + const SourceIds &updatedDirectoryInfoSourceIds); - 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<FileStatus>( - toIntegers(updatedSourceIds)); - - auto insert = [&](const FileStatus &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) { - updateFileStatusStatement.write(fileStatus.sourceId, - fileStatus.size, - fileStatus.lastModified); - return Sqlite::UpdateChange::Update; - } - - return Sqlite::UpdateChange::No; - }; - - auto remove = [&](const FileStatus &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); - synchronizeDocumentImports(imports, - updatedSourceIds, - Storage::Synchronization::ImportKind::Import); - synchronizeDocumentImports(moduleDependencies, - updatedModuleDependencySourceIds, - Storage::Synchronization::ImportKind::ModuleDependency); - } + const ModuleIds &updatedModuleIds, + Prototypes &relinkablePrototypes, + Prototypes &relinkableExtensions); void synchromizeModuleExportedImports( Storage::Synchronization::ModuleExportedImports &moduleExportedImports, - const ModuleIds &updatedModuleIds) - { - 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 - .template range<Storage::Synchronization::ModuleExportedImportView>( - 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) { - 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) { - deleteModuleExportedImportStatement.write(view.moduleExportedImportId); - }; - - Sqlite::insertUpdateDelete(range, moduleExportedImports, compareKey, insert, update, remove); - } - - ModuleId fetchModuleIdUnguarded(Utils::SmallStringView name) const override - { - auto moduleId = selectModuleIdByNameStatement.template value<ModuleId>(name); + const ModuleIds &updatedModuleIds); - if (moduleId) - return moduleId; + ModuleId fetchModuleIdUnguarded(Utils::SmallStringView name, + Storage::ModuleKind moduleKind) const override; - return insertModuleNameStatement.template value<ModuleId>(name); - } - - auto fetchModuleNameUnguarded(ModuleId id) const - { - auto moduleName = selectModuleNameStatement.template value<Utils::PathString>(id); - - if (moduleName.empty()) - throw ModuleDoesNotExists{}; - - return moduleName; - } + Storage::Module fetchModuleUnguarded(ModuleId id) const; void handleAliasPropertyDeclarationsWithPropertyType( - TypeId typeId, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations) - { - auto callback = [&](TypeId typeId_, - PropertyDeclarationId propertyDeclarationId, - ImportedTypeNameId propertyImportedTypeNameId, - PropertyDeclarationId aliasPropertyDeclarationId, - PropertyDeclarationId aliasPropertyDeclarationTailId) { - auto aliasPropertyName = selectPropertyNameStatement.template value<Utils::SmallString>( - aliasPropertyDeclarationId); - Utils::SmallString aliasPropertyNameTail; - if (aliasPropertyDeclarationTailId) - aliasPropertyNameTail = selectPropertyNameStatement.template value<Utils::SmallString>( - 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) - { - updatesPropertyDeclarationPropertyTypeToNullStatement.readTo(relinkablePropertyDeclarations, - typeId); - } + PropertyDeclarations &relinkablePropertyDeclarations); - void handlePrototypes(TypeId prototypeId, Prototypes &relinkablePrototypes) - { - auto callback = [&](TypeId typeId, ImportedTypeNameId prototypeNameId) { - relinkablePrototypes.emplace_back(typeId, prototypeNameId); - }; - - updatePrototypeIdToNullStatement.readCallback(callback, prototypeId); - } - - void handleExtensions(TypeId extensionId, Prototypes &relinkableExtensions) - { - auto callback = [&](TypeId typeId, ImportedTypeNameId extensionNameId) { - relinkableExtensions.emplace_back(typeId, extensionNameId); - }; - - updateExtensionIdToNullStatement.readCallback(callback, extensionId); - } + void handlePrototypes(TypeId prototypeId, Prototypes &relinkablePrototypes); + void handlePrototypesWithExportedTypeNameAndTypeId(Utils::SmallStringView exportedTypeName, + TypeId typeId, + Prototypes &relinkablePrototypes); + void handleExtensions(TypeId extensionId, Prototypes &relinkableExtensions); + void handleExtensionsWithExportedTypeNameAndTypeId(Utils::SmallStringView exportedTypeName, + TypeId typeId, + Prototypes &relinkableExtensions); void deleteType(TypeId typeId, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations, PropertyDeclarations &relinkablePropertyDeclarations, Prototypes &relinkablePrototypes, - Prototypes &relinkableExtensions) - { - 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) - { - NanotraceHR::Tracer tracer{"relink alias properties"_t, projectStorageCategory()}; - - 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<AliasPropertyDeclaration>{}); - } + const TypeIds &deletedTypeIds); void relinkPropertyDeclarations(PropertyDeclarations &relinkablePropertyDeclaration, - const TypeIds &deletedTypeIds) - { - NanotraceHR::Tracer tracer{"relink properties"_t, projectStorageCategory()}; - - 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<PropertyDeclaration>{}); - } + const TypeIds &deletedTypeIds); template<typename Callable> void relinkPrototypes(Prototypes &relinkablePrototypes, const TypeIds &deletedTypeIds, - Callable updateStatement) - { - NanotraceHR::Tracer tracer{"relink prototypes"_t, projectStorageCategory()}; - - std::sort(relinkablePrototypes.begin(), relinkablePrototypes.end()); - - Utils::set_greedy_difference( - relinkablePrototypes.cbegin(), - relinkablePrototypes.cend(), - deletedTypeIds.begin(), - deletedTypeIds.end(), - [&](const Prototype &prototype) { - TypeId prototypeId = fetchTypeId(prototype.prototypeNameId); - - if (!prototypeId) - throw TypeNameDoesNotExists{fetchImportedTypeName(prototype.prototypeNameId)}; - - updateStatement(prototype.typeId, prototypeId); - checkForPrototypeChainCycle(prototype.typeId); - }, - TypeCompare<Prototype>{}); - } + Callable updateStatement); void deleteNotUpdatedTypes(const TypeIds &updatedTypeIds, const SourceIds &updatedSourceIds, @@ -1505,295 +639,66 @@ private: PropertyDeclarations &relinkablePropertyDeclarations, Prototypes &relinkablePrototypes, Prototypes &relinkableExtensions, - TypeIds &deletedTypeIds) - { - NanotraceHR::Tracer tracer{"delete not updated types"_t, projectStorageCategory()}; - - 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) - { - if (aliasPropertyNameTail.empty()) - return fetchPropertyDeclarationIdByTypeIdAndNameUngarded(aliasTypeId, aliasPropertyName); + Utils::SmallStringView aliasPropertyNameTail); - auto stemAlias = fetchPropertyDeclarationByTypeIdAndNameUngarded(aliasTypeId, - aliasPropertyName); + void linkAliasPropertyDeclarationAliasIds(const AliasPropertyDeclarations &aliasDeclarations); - return fetchPropertyDeclarationIdByTypeIdAndNameUngarded(stemAlias.propertyTypeId, - aliasPropertyNameTail); - } + void updateAliasPropertyDeclarationValues(const AliasPropertyDeclarations &aliasDeclarations); - void linkAliasPropertyDeclarationAliasIds(const AliasPropertyDeclarations &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) - { - for (const auto &aliasDeclaration : aliasDeclarations) { - updatetPropertiesDeclarationValuesOfAliasStatement.write( - aliasDeclaration.propertyDeclarationId); - updatePropertyAliasDeclarationRecursivelyStatement.write( - aliasDeclaration.propertyDeclarationId); - } - } - - void checkAliasPropertyDeclarationCycles(const AliasPropertyDeclarations &aliasDeclarations) - { - for (const auto &aliasDeclaration : aliasDeclarations) - checkForAliasChainCycle(aliasDeclaration.propertyDeclarationId); - } + void checkAliasPropertyDeclarationCycles(const AliasPropertyDeclarations &aliasDeclarations); void linkAliases(const AliasPropertyDeclarations &insertedAliasPropertyDeclarations, - const AliasPropertyDeclarations &updatedAliasPropertyDeclarations) - { - 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) - { - 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<Storage::Synchronization::ExportedTypeView>( - 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) { - 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) { - 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) { - 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) - { - 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); - } + TypeId typeId); + + QVarLengthArray<PropertyDeclarationId, 128> fetchPropertyDeclarationIds(TypeId baseTypeId) const; + + PropertyDeclarationId fetchNextPropertyDeclarationId(TypeId baseTypeId, + Utils::SmallStringView propertyName) const; + + PropertyDeclarationId fetchPropertyDeclarationId(TypeId typeId, + Utils::SmallStringView propertyName) const; + + PropertyDeclarationId fetchNextDefaultPropertyDeclarationId(TypeId baseTypeId) const; + + PropertyDeclarationId fetchDefaultPropertyDeclarationId(TypeId typeId) const; void synchronizePropertyDeclarationsInsertProperty( - const Storage::Synchronization::PropertyDeclaration &value, SourceId sourceId, TypeId typeId) - { - auto propertyImportedTypeNameId = fetchImportedTypeNameId(value.typeName, sourceId); - auto propertyTypeId = fetchTypeId(propertyImportedTypeNameId); - - if (!propertyTypeId) - throw TypeNameDoesNotExists{fetchImportedTypeName(propertyImportedTypeNameId)}; - - auto propertyDeclarationId = insertPropertyDeclarationStatement.template value<PropertyDeclarationId>( - typeId, value.name, propertyTypeId, value.traits, propertyImportedTypeNameId); - - auto nextPropertyDeclarationId = selectPropertyDeclarationIdPrototypeChainDownStatement - .template value<PropertyDeclarationId>(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) - { - auto last = updatedAliasPropertyDeclarations.emplace_back(view.typeId, - view.id, - fetchImportedTypeNameId(value.typeName, - sourceId), - value.aliasPropertyName, - value.aliasPropertyNameTail, - view.aliasId); - } + SourceId sourceId); - auto synchronizePropertyDeclarationsUpdateProperty( + Sqlite::UpdateChange synchronizePropertyDeclarationsUpdateProperty( const Storage::Synchronization::PropertyDeclarationView &view, const Storage::Synchronization::PropertyDeclaration &value, SourceId sourceId, - PropertyDeclarationIds &propertyDeclarationIds) - { - auto propertyImportedTypeNameId = fetchImportedTypeNameId(value.typeName, sourceId); - - auto propertyTypeId = fetchTypeId(propertyImportedTypeNameId); - - if (!propertyTypeId) - throw TypeNameDoesNotExists{fetchImportedTypeName(propertyImportedTypeNameId)}; - - 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); - return Sqlite::UpdateChange::Update; - } + PropertyDeclarationIds &propertyDeclarationIds); void synchronizePropertyDeclarations( TypeId typeId, @@ -1801,270 +706,78 @@ 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<Storage::Synchronization::PropertyDeclarationView>(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) { - auto nextPropertyDeclarationId = selectPropertyDeclarationIdPrototypeChainDownStatement - .template value<PropertyDeclarationId>(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); - void resetRemovedAliasPropertyDeclarationsToNull(Storage::Synchronization::Type &type, - PropertyDeclarationIds &propertyDeclarationIds) + class AliasPropertyDeclarationView { - if (type.changeLevel == Storage::Synchronization::ChangeLevel::Minimal) - return; - - Storage::Synchronization::PropertyDeclarations &aliasDeclarations = type.propertyDeclarations; + public: + explicit AliasPropertyDeclarationView(Utils::SmallStringView name, + PropertyDeclarationId id, + PropertyDeclarationId aliasId) + : name{name} + , id{id} + , aliasId{aliasId} + {} - class AliasPropertyDeclarationView + template<typename String> + friend void convertToString(String &string, + const AliasPropertyDeclarationView &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; - }); - - auto range = selectPropertyDeclarationsWithAliasForTypeIdStatement - .template range<AliasPropertyDeclarationView>(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) { - updatePropertyDeclarationAliasIdToNullStatement.write(view.id); - propertyDeclarationIds.push_back(view.id); - }; - - Sqlite::insertUpdateDelete(range, aliasDeclarations, compareKey, insert, update, remove); - } + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("name", aliasPropertyDeclarationView.name), + keyValue("id", aliasPropertyDeclarationView.id), + keyValue("alias id", aliasPropertyDeclarationView.aliasId)); - void resetRemovedAliasPropertyDeclarationsToNull( - Storage::Synchronization::Types &types, - AliasPropertyDeclarations &relinkableAliasPropertyDeclarations) - { - NanotraceHR::Tracer tracer{"reset removed alias properties to null"_t, - projectStorageCategory()}; + convertToString(string, dict); + } - PropertyDeclarationIds propertyDeclarationIds; - propertyDeclarationIds.reserve(types.size()); + public: + Utils::SmallStringView name; + PropertyDeclarationId id; + PropertyDeclarationId aliasId; + }; - for (auto &&type : types) - resetRemovedAliasPropertyDeclarationsToNull(type, propertyDeclarationIds); + void resetRemovedAliasPropertyDeclarationsToNull(Storage::Synchronization::Type &type, + PropertyDeclarationIds &propertyDeclarationIds); - removeRelinkableEntries(relinkableAliasPropertyDeclarations, - propertyDeclarationIds, - PropertyCompare<AliasPropertyDeclaration>{}); - } + void resetRemovedAliasPropertyDeclarationsToNull( + Storage::Synchronization::Types &types, + AliasPropertyDeclarations &relinkableAliasPropertyDeclarations); + + void handlePrototypesWithSourceIdAndPrototypeId(SourceId sourceId, + TypeId prototypeId, + Prototypes &relinkablePrototypes); + void handlePrototypesAndExtensionsWithSourceId(SourceId sourceId, + TypeId prototypeId, + TypeId extensionId, + Prototypes &relinkablePrototypes, + Prototypes &relinkableExtensions); + void handleExtensionsWithSourceIdAndExtensionId(SourceId sourceId, + TypeId extensionId, + Prototypes &relinkableExtensions); ImportId insertDocumentImport(const Storage::Import &import, Storage::Synchronization::ImportKind importKind, ModuleId sourceModuleId, - ImportId parentImportId) - { - if (import.version.minor) { - return insertDocumentImportWithVersionStatement - .template value<ImportId>(import.sourceId, - import.moduleId, - sourceModuleId, - importKind, - import.version.major.value, - import.version.minor.value, - parentImportId); - } else if (import.version.major) { - return insertDocumentImportWithMajorVersionStatement - .template value<ImportId>(import.sourceId, - import.moduleId, - sourceModuleId, - importKind, - import.version.major.value, - parentImportId); - } else { - return insertDocumentImportWithoutVersionStatement.template value<ImportId>( - import.sourceId, import.moduleId, sourceModuleId, importKind, parentImportId); - } - } + ImportId parentImportId, + Relink forceRelink, + Prototypes &relinkablePrototypes, + Prototypes &relinkableExtensions); 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); - }); - - auto range = selectDocumentImportForSourceIdStatement - .template range<Storage::Synchronization::ImportView>(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) { - 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; - - insertDocumentImport(additionImport, exportedImportKind, import.moduleId, importId); - }; - - selectModuleExportedImportsForModuleIdStatement.readCallback(callback, - import.moduleId, - import.version.major.value, - import.version.minor.value); - }; - - auto update = [](const Storage::Synchronization::ImportView &, const Storage::Import &) { - return Sqlite::UpdateChange::No; - }; - - auto remove = [&](const Storage::Synchronization::ImportView &view) { - 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) - { - 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("}"); - } - } + Storage::Synchronization::ImportKind importKind, + Relink forceRelink, + Prototypes &relinkablePrototypes, + Prototypes &relinkableExtensions); - json.append("]"); - - return json; - } + static Utils::PathString createJson(const Storage::Synchronization::ParameterDeclarations ¶meters); TypeId fetchTypeIdByModuleIdAndExportedName(ModuleId moduleId, - Utils::SmallStringView name) const override - { - return selectTypeIdByModuleIdAndExportedNameStatement.template value<TypeId>(moduleId, name); - } + Utils::SmallStringView name) const override; - void addTypeIdToPropertyEditorQmlPaths(Storage::Synchronization::PropertyEditorQmlPaths &paths) - { - for (auto &path : paths) - path.typeId = fetchTypeIdByModuleIdAndExportedName(path.moduleId, path.typeName); - } + void addTypeIdToPropertyEditorQmlPaths(Storage::Synchronization::PropertyEditorQmlPaths &paths); class PropertyEditorQmlPathView { @@ -2075,6 +788,19 @@ private: , directoryId{directoryId} {} + template<typename String> + 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; @@ -2082,279 +808,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<PropertyEditorQmlPathView>( - toIntegers(updatedPropertyEditorQmlPathsSourceIds)); - - auto compareKey = [](const PropertyEditorQmlPathView &view, - const PropertyEditorQmlPath &value) -> long long { - return view.typeId - value.typeId; - }; - - auto insert = [&](const PropertyEditorQmlPath &path) { - if (path.typeId) - insertPropertyEditorPathStatement.write(path.typeId, path.pathId, path.directoryId); - }; - - auto update = [&](const PropertyEditorQmlPathView &view, const PropertyEditorQmlPath &value) { - if (value.pathId != view.pathId || value.directoryId != view.directoryId) { - updatePropertyEditorPathsStatement.write(value.typeId, value.pathId, value.directoryId); - return Sqlite::UpdateChange::Update; - } - return Sqlite::UpdateChange::No; - }; - - auto remove = [&](const PropertyEditorQmlPathView &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<Storage::Synchronization::FunctionDeclarationView>(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) { - 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) { - Utils::PathString signature{createJson(value.parameters)}; - - if (value.returnTypeName == view.returnTypeName) - return Sqlite::UpdateChange::No; - - updateFunctionDeclarationStatement.write(view.id, value.returnTypeName); - - return Sqlite::UpdateChange::Update; - }; - - auto remove = [&](const Storage::Synchronization::FunctionDeclarationView &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<Storage::Synchronization::SignalDeclarationView>(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) { - 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) { - 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) - { - 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 enumeation 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<Storage::Synchronization::EnumerationDeclarationView>(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) { - Utils::PathString signature{createJson(value.enumeratorDeclarations)}; - - insertEnumerationDeclarationStatement.write(typeId, value.name, signature); - }; - - auto update = [&](const Storage::Synchronization::EnumerationDeclarationView &view, - const Storage::Synchronization::EnumerationDeclaration &value) { - Utils::PathString enumeratorDeclarations{createJson(value.enumeratorDeclarations)}; - - if (enumeratorDeclarations == view.enumeratorDeclarations) - return Sqlite::UpdateChange::No; - - updateEnumerationDeclarationStatement.write(view.id, enumeratorDeclarations); - - return Sqlite::UpdateChange::Update; - }; - - auto remove = [&](const Storage::Synchronization::EnumerationDeclarationView &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); - } - - TypeId declareType(Storage::Synchronization::Type &type) - { - if (type.typeName.isEmpty()) { - type.typeId = selectTypeIdBySourceIdStatement.template value<TypeId>(type.sourceId); - - return type.typeId; - } + Storage::Synchronization::ExportedTypes &exportedTypes); - type.typeId = insertTypeStatement.template value<TypeId>(type.sourceId, type.typeName); - - if (!type.typeId) - type.typeId = selectTypeIdBySourceIdAndNameStatement.template value<TypeId>(type.sourceId, - type.typeName); - - 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<typename Relinkable, typename Ids, typename Compare> void removeRelinkableEntries(std::vector<Relinkable> &relinkables, Ids &ids, Compare compare) @@ -2381,23 +861,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<PropertyDeclaration>{}); - } + PropertyDeclarations &relinkablePropertyDeclarations); class TypeWithDefaultPropertyView { @@ -2407,240 +871,55 @@ private: , defaultPropertyId{defaultPropertyId} {} + template<typename String> + 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; }; - void syncDefaultProperties(Storage::Synchronization::Types &types) - { - NanotraceHR::Tracer tracer{"synchronize default properties"_t, projectStorageCategory()}; - - auto range = selectTypesWithDefaultPropertyStatement.template range<TypeWithDefaultPropertyView>(); - - 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) { - PropertyDeclarationId valueDefaultPropertyId; - if (value.defaultPropertyName.size()) - valueDefaultPropertyId = fetchPropertyDeclarationByTypeIdAndNameUngarded( - value.typeId, value.defaultPropertyName) - .propertyDeclarationId; + void syncDefaultProperties(Storage::Synchronization::Types &types); - if (compareInvalidAreTrue(valueDefaultPropertyId, view.defaultPropertyId)) - return Sqlite::UpdateChange::No; + void resetDefaultPropertiesIfChanged(Storage::Synchronization::Types &types); - updateDefaultPropertyIdStatement.write(value.typeId, valueDefaultPropertyId); + void checkForPrototypeChainCycle(TypeId typeId) const; - 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<TypeWithDefaultPropertyView>(); - - 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) { - 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{}); - - return Sqlite::UpdateChange::Update; - }; - - auto remove = [&](const TypeWithDefaultPropertyView &) {}; - - Sqlite::insertUpdateDelete(range, types, compareKey, insert, update, remove); - } - - void checkForPrototypeChainCycle(TypeId typeId) const - { - auto callback = [=](TypeId currentTypeId) { - if (typeId == currentTypeId) - throw PrototypeChainCycle{}; - }; - - selectTypeIdsForPrototypeChainIdStatement.readCallback(callback, typeId); - } - - void checkForAliasChainCycle(PropertyDeclarationId propertyDeclarationId) const - { - auto callback = [=](PropertyDeclarationId currentPropertyDeclarationId) { - if (propertyDeclarationId == currentPropertyDeclarationId) - throw AliasChainCycle{}; - }; - - selectPropertyDeclarationIdsForAliasChainStatement.readCallback(callback, - propertyDeclarationId); - } + void checkForAliasChainCycle(PropertyDeclarationId propertyDeclarationId) const; std::pair<TypeId, ImportedTypeNameId> fetchImportedTypeNameIdAndTypeId( - 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); - - if (!typeId) - throw TypeNameDoesNotExists{fetchImportedTypeName(typeNameId)}; - } - - return {typeId, typeNameId}; - } + const Storage::Synchronization::ImportedTypeName &typeName, SourceId sourceId); - void syncPrototypeAndExtension(Storage::Synchronization::Type &type, TypeIds &typeIds) - { - if (type.changeLevel == Storage::Synchronization::ChangeLevel::Minimal) - return; - - 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); - } + void syncPrototypeAndExtension(Storage::Synchronization::Type &type, TypeIds &typeIds); void syncPrototypesAndExtensions(Storage::Synchronization::Types &types, Prototypes &relinkablePrototypes, - Prototypes &relinkableExtensions) - { - NanotraceHR::Tracer tracer{"synchronize prototypes"_t, projectStorageCategory()}; - - TypeIds typeIds; - typeIds.reserve(types.size()); + Prototypes &relinkableExtensions); - for (auto &type : types) - syncPrototypeAndExtension(type, typeIds); - - removeRelinkableEntries(relinkablePrototypes, typeIds, TypeCompare<Prototype>{}); - removeRelinkableEntries(relinkableExtensions, typeIds, TypeCompare<Prototype>{}); - } - - ImportId fetchImportId(SourceId sourceId, const Storage::Import &import) const - { - if (import.version) { - return selectImportIdBySourceIdAndModuleIdAndVersionStatement.template value<ImportId>( - sourceId, import.moduleId, import.version.major.value, import.version.minor.value); - } - - if (import.version.major) { - return selectImportIdBySourceIdAndModuleIdAndMajorVersionStatement - .template value<ImportId>(sourceId, import.moduleId, import.version.major.value); - } - - return selectImportIdBySourceIdAndModuleIdStatement.template value<ImportId>(sourceId, - import.moduleId); - } + 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) - { - return storage.fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::Exported, - sourceId, - importedType.name); - } - - auto operator()(const Storage::Synchronization::QualifiedImportedType &importedType) - { - ImportId importId = storage.fetchImportId(sourceId, importedType.import); - - return storage.fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind::QualifiedExported, - importId, - importedType.name); - } - - ProjectStorage &storage; - SourceId sourceId; - }; - - return std::visit(Inspect{*this, sourceId}, name); - } + SourceId sourceId); template<typename Id> ImportedTypeNameId fetchImportedTypeNameId(Storage::Synchronization::TypeNameKind kind, Id id, - Utils::SmallStringView typeName) - { - auto importedTypeNameId = selectImportedTypeNameIdStatement - .template value<ImportedTypeNameId>(kind, id, typeName); - - if (importedTypeNameId) - return importedTypeNameId; - - return insertImportedTypeNameIdStatement.template value<ImportedTypeNameId>(kind, id, typeName); - } - - TypeId fetchTypeId(ImportedTypeNameId typeNameId) const - { - auto kind = selectKindFromImportedTypeNamesStatement - .template value<Storage::Synchronization::TypeNameKind>(typeNameId); - - return fetchTypeId(typeNameId, kind); - } + Utils::SmallStringView typeName); - Utils::SmallString fetchImportedTypeName(ImportedTypeNameId typeNameId) const - { - return selectNameFromImportedTypeNamesStatement.template value<Utils::SmallString>(typeNameId); - } + TypeId fetchTypeId(ImportedTypeNameId typeNameId) const; - TypeId fetchTypeId(ImportedTypeNameId typeNameId, Storage::Synchronization::TypeNameKind kind) const - { - if (kind == Storage::Synchronization::TypeNameKind::QualifiedExported) { - return selectTypeIdForQualifiedImportedTypeNameNamesStatement.template value<TypeId>( - typeNameId); - } + Utils::SmallString fetchImportedTypeName(ImportedTypeNameId typeNameId) const; + SourceId fetchTypeSourceId(TypeId typeId) const; - return selectTypeIdForImportedTypeNameNamesStatement.template value<TypeId>(typeNameId); - } + TypeId fetchTypeId(ImportedTypeNameId typeNameId, + Storage::Synchronization::TypeNameKind kind) const; class FetchPropertyDeclarationResult { @@ -2653,1303 +932,65 @@ private: , propertyTraits{propertyTraits} {} + template<typename String> + 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", result.propertyTraits)); + + convertToString(string, dict); + } + public: TypeId propertyTypeId; PropertyDeclarationId propertyDeclarationId; Storage::PropertyDeclarationTraits propertyTraits; }; - auto fetchOptionalPropertyDeclarationByTypeIdAndNameUngarded(TypeId typeId, - Utils::SmallStringView name) - { - return selectPropertyDeclarationByTypeIdAndNameStatement - .template optionalValue<FetchPropertyDeclarationResult>(typeId, name); - } + std::optional<FetchPropertyDeclarationResult> fetchOptionalPropertyDeclarationByTypeIdAndNameUngarded( + TypeId typeId, Utils::SmallStringView name); FetchPropertyDeclarationResult fetchPropertyDeclarationByTypeIdAndNameUngarded( - TypeId typeId, Utils::SmallStringView name) - { - auto propertyDeclaration = fetchOptionalPropertyDeclarationByTypeIdAndNameUngarded(typeId, - name); - - if (propertyDeclaration) - return *propertyDeclaration; - - throw PropertyNameDoesNotExists{}; - } + TypeId typeId, Utils::SmallStringView name); PropertyDeclarationId fetchPropertyDeclarationIdByTypeIdAndNameUngarded(TypeId typeId, - Utils::SmallStringView name) - { - auto propertyDeclarationId = selectPropertyDeclarationIdByTypeIdAndNameStatement - .template value<PropertyDeclarationId>(typeId, name); + Utils::SmallStringView name); - if (propertyDeclarationId) - return propertyDeclarationId; + SourceContextId readSourceContextId(Utils::SmallStringView sourceContextPath); - throw PropertyNameDoesNotExists{}; - } + SourceContextId writeSourceContextId(Utils::SmallStringView sourceContextPath); - SourceContextId readSourceContextId(Utils::SmallStringView sourceContextPath) - { - return selectSourceContextIdFromSourceContextsBySourceContextPathStatement - .template value<SourceContextId>(sourceContextPath); - } + SourceId writeSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName); - SourceContextId writeSourceContextId(Utils::SmallStringView sourceContextPath) - { - insertIntoSourceContextsStatement.write(sourceContextPath); + SourceId readSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName); - return SourceContextId::create(database.lastInsertedRowId()); - } + Storage::Synchronization::ExportedTypes fetchExportedTypes(TypeId typeId); - SourceId writeSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName) - { - insertIntoSourcesStatement.write(sourceContextId, sourceName); + Storage::Synchronization::PropertyDeclarations fetchPropertyDeclarations(TypeId typeId); - return SourceId::create(database.lastInsertedRowId()); - } - - SourceId readSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName) - { - return selectSourceIdFromSourcesBySourceContextIdAndSourceNameStatement - .template value<SourceId>(sourceContextId, sourceName); - } - - auto fetchExportedTypes(TypeId typeId) - { - return selectExportedTypesByTypeIdStatement - .template values<Storage::Synchronization::ExportedType, 12>(typeId); - } - - auto fetchPropertyDeclarations(TypeId typeId) - { - return selectPropertyDeclarationsByTypeIdStatement - .template values<Storage::Synchronization::PropertyDeclaration, 24>(typeId); - } - - auto fetchFunctionDeclarations(TypeId 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<Storage::Synchronization::ParameterDeclaration, - 8>(functionDeclarationId); - }; - - selectFunctionDeclarationsForTypeIdWithoutSignatureStatement.readCallback(callback, typeId); - - return functionDeclarations; - } - - auto fetchSignalDeclarations(TypeId typeId) - { - Storage::Synchronization::SignalDeclarations signalDeclarations; - - auto callback = [&](Utils::SmallStringView name, SignalDeclarationId signalDeclarationId) { - auto &signalDeclaration = signalDeclarations.emplace_back(name); - signalDeclaration.parameters = selectSignalParameterDeclarationsStatement - .template values<Storage::Synchronization::ParameterDeclaration, - 8>(signalDeclarationId); - }; - - selectSignalDeclarationsForTypeIdWithoutSignatureStatement.readCallback(callback, typeId); - - return signalDeclarations; - } - - auto fetchEnumerationDeclarations(TypeId typeId) - { - Storage::Synchronization::EnumerationDeclarations enumerationDeclarations; - - auto callback = [&](Utils::SmallStringView name, - EnumerationDeclarationId enumerationDeclarationId) { - enumerationDeclarations.emplace_back( - name, - selectEnumeratorDeclarationStatement - .template values<Storage::Synchronization::EnumeratorDeclaration, 8>( - enumerationDeclarationId)); - }; - - selectEnumerationDeclarationsForTypeIdWithoutEnumeratorDeclarationsStatement - .readCallback(callback, typeId); - - 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"); - 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({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.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.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.initialize(database); - } + Storage::Synchronization::FunctionDeclarations fetchFunctionDeclarations(TypeId typeId); - 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); - } + Storage::Synchronization::SignalDeclarations fetchSignalDeclarations(TypeId typeId); - 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); - } + Storage::Synchronization::EnumerationDeclarations fetchEnumerationDeclarations(TypeId typeId); - 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); - } + class Initializer; - void createTypeAnnotionsTable(Database &database) - { - Sqlite::StrictTable table; - table.setUseIfNotExists(true); - table.setUseWithoutRowId(true); - table.setName("typeAnnotations"); - auto &typeIdColumn = table.addColumn("typeId", - Sqlite::StrictColumnType::Integer, - {Sqlite::PrimaryKey{}}); - auto &sourceIdColumn = table.addColumn("sourceId", Sqlite::StrictColumnType::Integer); - table.addColumn("iconPath", Sqlite::StrictColumnType::Text); - table.addColumn("itemLibrary", Sqlite::StrictColumnType::Text); - table.addColumn("hints", Sqlite::StrictColumnType::Text); - - table.addUniqueIndex({sourceIdColumn, typeIdColumn}); - - table.initialize(database); - } - }; + struct Statements; public: Database &database; + ProjectStorageErrorNotifierInterface *errorNotifier = nullptr; // cannot be null Sqlite::ExclusiveNonThrowingDestructorTransaction<Database> exclusiveTransaction; - Initializer initializer; + std::unique_ptr<Initializer> initializer; mutable ModuleCache moduleCache{ModuleStorageAdapter{*this}}; - Storage::Info::CommonTypeCache<ProjectStorageInterface> commonTypeCache_{*this}; + Storage::Info::CommonTypeCache<ProjectStorageType> commonTypeCache_{*this}; QVarLengthArray<ProjectStorageObserver *, 24> 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<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 " - " 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 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, level) AS (" - " VALUES(?1, 0) " - " UNION ALL " - " SELECT prototypeId, typeSelection.level+1 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", - 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", - 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<2> updateFunctionDeclarationStatement{ - "UPDATE functionDeclarations SET returnTypeName=?2 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}; - 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" - " 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", - 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 OR target.typeId=?1 OR 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> selectTypeIdsForPrototypeChainIdStatement{ - "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) AS (" - " SELECT prototypeId FROM all_prototype_and_extension WHERE typeId=?" - " UNION ALL " - " SELECT prototypeId FROM all_prototype_and_extension JOIN " - " prototypes USING(typeId)) " - "SELECT typeId FROM prototypes", - 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{ - "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", - 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> 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 " - "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<4, 1> selectInfoTypeByTypeIdStatement{ - "SELECT defaultPropertyId, sourceId, traits, annotationTraits FROM types WHERE typeId=?", - database}; - mutable ReadStatement<1, 1> selectPrototypeIdsForTypeIdInOrderStatement{ - "WITH RECURSIVE " - " all_prototype_and_extension(typeId, prototypeId) AS (" - " 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}; - 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 " - "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<5> insertTypeAnnotationStatement{ - "INSERT INTO typeAnnotations(typeId, sourceId, iconPath, itemLibrary, hints) VALUES(?1, " - "?2, ?3, ?4, ?5)", - database}; - WriteStatement<4> updateTypeAnnotationStatement{ - "UPDATE typeAnnotations SET iconPath=?2, itemLibrary=?3, hints=?4 WHERE typeId=?1", database}; - WriteStatement<1> deleteTypeAnnotationStatement{"DELETE FROM typeAnnotations WHERE typeId=?1", - database}; - mutable ReadStatement<1, 1> selectTypeIconPathStatement{ - "SELECT iconPath FROM typeAnnotations WHERE typeId=?1", database}; - mutable ReadStatement<2, 1> selectTypeHintsStatement{ - "SELECT hints.key, hints.value " - "FROM typeAnnotations, json_each(typeAnnotations.hints) AS hints " - "WHERE typeId=?1", - database}; - mutable ReadStatement<9> selectItemLibraryEntriesStatement{ - "SELECT typeId, i.value->>'$.name', i.value->>'$.iconPath', i.value->>'$.category', " - " i.value->>'$.import', i.value->>'$.toolTip', i.value->>'$.properties', " - " i.value->>'$.extraFilePaths', i.value->>'$.templatePath' " - "FROM typeAnnotations, json_each(typeAnnotations.itemLibrary) AS i", - database}; - mutable ReadStatement<9, 1> selectItemLibraryEntriesByTypeIdStatement{ - "SELECT typeId, i.value->>'$.name', i.value->>'$.iconPath', i.value->>'$.category', " - " i.value->>'$.import', i.value->>'$.toolTip', i.value->>'$.properties', " - " i.value->>'$.extraFilePaths', i.value->>'$.templatePath' " - "FROM typeAnnotations, json_each(typeAnnotations.itemLibrary) AS i " - "WHERE typeId=?1", - database}; - mutable ReadStatement<9, 1> selectItemLibraryEntriesBySourceIdStatement{ - "SELECT typeId, i.value->>'$.name', i.value->>'$.iconPath', i.value->>'$.category', " - " i.value->>'$.import', i.value->>'$.toolTip', i.value->>'$.properties', " - " i.value->>'$.extraFilePaths', i.value->>'$.templatePath' " - "FROM typeAnnotations, json_each(typeAnnotations.itemLibrary) AS i " - "WHERE typeId IN (SELECT DISTINCT typeId " - " FROM documentImports AS di JOIN exportedTypeNames USING(moduleId) " - " WHERE di.sourceId=?)", - database}; - mutable ReadStatement<3, 1> selectItemLibraryPropertiesStatement{ - "SELECT p.value->>0, p.value->>1, p.value->>2 FROM json_each(?1) AS p", database}; - mutable ReadStatement<1, 1> selectItemLibraryExtraFilePathsStatement{ - "SELECT p.value FROM json_each(?1) AS p", database}; - mutable ReadStatement<1, 1> selectTypeIdsByModuleIdStatement{ - "SELECT DISTINCT typeId FROM exportedTypeNames WHERE moduleId=?", database}; - mutable ReadStatement<1, 1> selectHeirTypeIdsStatement{ - "WITH RECURSIVE " - " typeSelection(typeId) AS (" - " SELECT typeId FROM types WHERE prototypeId=?1 OR extensionId=?1" - " UNION ALL " - " SELECT t.typeId " - " FROM types AS t JOIN typeSelection AS ts " - " WHERE prototypeId=ts.typeId OR extensionId=ts.typeId)" - "SELECT typeId FROM typeSelection", - database}; + std::unique_ptr<Statements> s; }; -extern template class ProjectStorage<Sqlite::Database>; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifier.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifier.cpp new file mode 100644 index 0000000000..a4705f5eec --- /dev/null +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifier.cpp @@ -0,0 +1,17 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "projectstorageerrornotifier.h" + +#include "sourcepathcache.h" + +namespace QmlDesigner { + +void ProjectStorageErrorNotifier::typeNameCannotBeResolved(Utils::SmallStringView typeName, + SourceId sourceId) +{ + qDebug() << "Missing type name: " << typeName + << " in file: " << m_pathCache.sourcePath(sourceId).toStringView(); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifier.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifier.h new file mode 100644 index 0000000000..2695e93019 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifier.h @@ -0,0 +1,25 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "projectstorageerrornotifierinterface.h" + +#include <modelfwd.h> + +namespace QmlDesigner { + +class ProjectStorageErrorNotifier final : public ProjectStorageErrorNotifierInterface +{ +public: + ProjectStorageErrorNotifier(PathCacheType &pathCache) + : m_pathCache{pathCache} + {} + + void typeNameCannotBeResolved(Utils::SmallStringView typeName, SourceId souceId) override; + +private: + PathCacheType &m_pathCache; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifierinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifierinterface.h new file mode 100644 index 0000000000..8136c9d599 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifierinterface.h @@ -0,0 +1,27 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "projectstorageids.h" + +#include <utils/smallstringview.h> + +namespace QmlDesigner { + +class ProjectStorageErrorNotifierInterface +{ +public: + ProjectStorageErrorNotifierInterface() = default; + ProjectStorageErrorNotifierInterface(ProjectStorageErrorNotifierInterface &&) = default; + ProjectStorageErrorNotifierInterface &operator=(ProjectStorageErrorNotifierInterface &&) = default; + ProjectStorageErrorNotifierInterface(const ProjectStorageErrorNotifierInterface &) = delete; + ProjectStorageErrorNotifierInterface &operator=(const ProjectStorageErrorNotifierInterface &) = delete; + + virtual void typeNameCannotBeResolved(Utils::SmallStringView typeName, SourceId souceId) = 0; + +protected: + ~ProjectStorageErrorNotifierInterface() = default; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp index efe9bc58f5..a86b78a785 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp @@ -3,87 +3,186 @@ #include "projectstorageexceptions.h" +#include <tracing/qmldesignertracing.h> + 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!"; } -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())})} +{ + 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!"; } -const char *ProjectDataHasInvalidProjectSourceId::what() const noexcept +DirectoryInfoHasInvalidProjectSourceId::DirectoryInfoHasInvalidProjectSourceId() +{ + category().threadEvent("DirectoryInfoHasInvalidProjectSourceId"_t); +} + +const char *DirectoryInfoHasInvalidProjectSourceId::what() const noexcept { return "The project source id is invalid!"; } -const char *ProjectDataHasInvalidSourceId::what() const noexcept +DirectoryInfoHasInvalidSourceId::DirectoryInfoHasInvalidSourceId() +{ + category().threadEvent("DirectoryInfoHasInvalidSourceId"_t); +} + +const char *DirectoryInfoHasInvalidSourceId::what() const noexcept { return "The source id is invalid!"; } -const char *ProjectDataHasInvalidModuleId::what() const noexcept +DirectoryInfoHasInvalidModuleId::DirectoryInfoHasInvalidModuleId() +{ + category().threadEvent("DirectoryInfoHasInvalidModuleId"_t); +} + +const char *DirectoryInfoHasInvalidModuleId::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!"; @@ -110,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 412dd4a9ff..f4f78f714b 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 <exception> namespace QmlDesigner { @@ -13,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: @@ -31,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; }; @@ -79,66 +92,76 @@ 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 { 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 +class QMLDESIGNERCORE_EXPORT DirectoryInfoHasInvalidProjectSourceId : public ProjectStorageError { public: + DirectoryInfoHasInvalidProjectSourceId(); const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidSourceId : public ProjectStorageError +class QMLDESIGNERCORE_EXPORT DirectoryInfoHasInvalidSourceId : public ProjectStorageError { public: + DirectoryInfoHasInvalidSourceId(); const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidModuleId : public ProjectStorageError +class QMLDESIGNERCORE_EXPORT DirectoryInfoHasInvalidModuleId : public ProjectStorageError { public: + DirectoryInfoHasInvalidModuleId(); 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/projectstorage/projectstoragefwd.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragefwd.h index b33c609509..cbb7d4265a 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<typename Database> class ProjectStorage; template<typename Type> diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h index 427c0ff8d6..1d630344ae 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h @@ -7,6 +7,9 @@ #include <sqlite/sqlitevalue.h> #include <utils/smallstring.h> +#include <utils/utility.h> + +#include <QVarLengthArray> #include <array> #include <tuple> @@ -15,19 +18,57 @@ namespace QmlDesigner { -template<typename Enumeration> -constexpr std::underlying_type_t<Enumeration> to_underlying(Enumeration enumeration) noexcept -{ - static_assert(std::is_enum_v<Enumeration>, "to_underlying expect an enumeration"); - return static_cast<std::underlying_type_t<Enumeration>>(enumeration); -} +template<std::size_t size> +using SmallPathStrings = QVarLengthArray<Utils::PathString, size>; enum class FlagIs : unsigned int { False, Set, True }; +template<typename String> +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 { +enum class ModuleKind { QmlLibrary, CppLibrary, PathLibrary }; + +struct Module +{ + Module() = default; + + Module(Utils::SmallStringView name, Storage::ModuleKind kind) + : name{name} + , kind{kind} + {} + + template<typename ModuleType> + Module(const ModuleType &module) + : name{module.name} + , kind{module.kind} + {} + + Utils::PathString name; + Storage::ModuleKind kind = Storage::ModuleKind::QmlLibrary; + + friend bool operator==(const Module &first, const Module &second) + { + return first.name == second.name && first.kind == second.kind; + } + + explicit operator bool() const { return name.size(); } +}; + enum class PropertyDeclarationTraits : int { None = 0, IsReadOnly = 1 << 0, @@ -46,6 +87,18 @@ constexpr bool operator&(PropertyDeclarationTraits first, PropertyDeclarationTra return static_cast<int>(first) & static_cast<int>(second); } +template<typename String> +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 +106,25 @@ enum class TypeTraitsKind : unsigned int { Sequence, }; +template<typename String> +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 +172,35 @@ struct TypeTraits return first.type == second.type && first.annotation == second.annotation; } + template<typename String> + 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,10 +303,22 @@ public: explicit operator bool() const { return major && minor; } + template<typename String> + 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; }; + } // namespace QmlDesigner::Storage namespace QmlDesigner::Storage::Info { @@ -217,6 +330,17 @@ struct TypeHint , expression{expression} {} + template<typename String> + 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; }; @@ -231,6 +355,18 @@ struct ItemLibraryProperty , value{value} {} + template<typename String> + 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; @@ -243,6 +379,7 @@ using ToolTipString = Utils::BasicSmallString<94>; struct ItemLibraryEntry { ItemLibraryEntry(TypeId typeId, + Utils::SmallStringView typeName, Utils::SmallStringView name, Utils::SmallStringView iconPath, Utils::SmallStringView category, @@ -250,6 +387,7 @@ struct ItemLibraryEntry Utils::SmallStringView toolTip, Utils::SmallStringView templatePath) : typeId{typeId} + , typeName{typeName} , name{name} , iconPath{iconPath} , category{category} @@ -259,6 +397,7 @@ struct ItemLibraryEntry {} ItemLibraryEntry(TypeId typeId, + Utils::SmallStringView typeName, Utils::SmallStringView name, Utils::SmallStringView iconPath, Utils::SmallStringView category, @@ -266,6 +405,7 @@ struct ItemLibraryEntry Utils::SmallStringView toolTip, ItemLibraryProperties properties) : typeId{typeId} + , typeName{typeName} , name{name} , iconPath{iconPath} , category{category} @@ -274,7 +414,27 @@ struct ItemLibraryEntry , properties{std::move(properties)} {} + template<typename String> + friend void convertToString(String &string, const ItemLibraryEntry &entry) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("type id", entry.typeId), + keyValue("type name", entry.typeName), + keyValue("name", entry.name), + keyValue("icon path", entry.iconPath), + keyValue("category", entry.category), + 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 typeName; Utils::SmallString name; Utils::PathString iconPath; Utils::SmallString category; @@ -321,6 +481,18 @@ public: < std::tie(second.moduleId, second.name, second.version); } + template<typename String> + 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; @@ -342,6 +514,19 @@ public: , propertyTypeId{propertyTypeId} {} + template<typename String> + 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; @@ -351,22 +536,26 @@ 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} {} - PropertyDeclarationId defaultPropertyId; + template<typename String> + friend void convertToString(String &string, const Type &type) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("source id", type.sourceId), keyValue("traits", type.traits)); + + convertToString(string, dict); + } + SourceId sourceId; TypeTraits traits; }; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h index 266c6ee7ca..4d840d2a5c 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h @@ -31,7 +31,8 @@ public: virtual void addObserver(ProjectStorageObserver *observer) = 0; virtual void removeObserver(ProjectStorageObserver *observer) = 0; - virtual ModuleId moduleId(::Utils::SmallStringView name) const = 0; + virtual ModuleId moduleId(::Utils::SmallStringView name, Storage::ModuleKind kind) const = 0; + virtual QmlDesigner::Storage::Module module(ModuleId moduleId) const = 0; virtual std::optional<Storage::Info::PropertyDeclaration> propertyDeclaration(PropertyDeclarationId propertyDeclarationId) const = 0; virtual TypeId typeId(ModuleId moduleId, @@ -54,7 +55,10 @@ public: virtual PropertyDeclarationId propertyDeclarationId(TypeId typeId, ::Utils::SmallStringView propertyName) const = 0; + virtual PropertyDeclarationId defaultPropertyDeclarationId(TypeId typeId) const = 0; virtual std::optional<Storage::Info::Type> 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; @@ -64,9 +68,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; @@ -76,16 +80,20 @@ public: virtual bool isBasedOn(TypeId, TypeId, TypeId, TypeId, TypeId, TypeId, TypeId, TypeId) const = 0; virtual FileStatus fetchFileStatus(SourceId sourceId) const = 0; - virtual Storage::Synchronization::ProjectDatas fetchProjectDatas(SourceId sourceId) const = 0; - virtual std::optional<Storage::Synchronization::ProjectData> fetchProjectData(SourceId sourceId) const = 0; + virtual Storage::Synchronization::DirectoryInfos fetchDirectoryInfos(SourceId sourceId) const = 0; + virtual Storage::Synchronization::DirectoryInfos fetchDirectoryInfos( + SourceId directorySourceId, Storage::Synchronization::FileType) const + = 0; + virtual std::optional<Storage::Synchronization::DirectoryInfo> fetchDirectoryInfo(SourceId sourceId) const = 0; + virtual SmallSourceIds<32> fetchSubdirectorySourceIds(SourceId directorySourceId) const = 0; virtual SourceId propertyEditorPathId(TypeId typeId) const = 0; - virtual const Storage::Info::CommonTypeCache<ProjectStorageInterface> &commonTypeCache() const = 0; + virtual const Storage::Info::CommonTypeCache<ProjectStorageType> &commonTypeCache() const = 0; - template<const char *moduleName, const char *typeName> + template<const char *moduleName, const char *typeName, Storage::ModuleKind moduleKind = Storage::ModuleKind::QmlLibrary> TypeId commonTypeId() const { - return commonTypeCache().template typeId<moduleName, typeName>(); + return commonTypeCache().template typeId<moduleName, typeName, moduleKind>(); } template<typename BuiltinType> @@ -104,7 +112,7 @@ protected: ProjectStorageInterface() = default; ~ProjectStorageInterface() = default; - virtual ModuleId fetchModuleIdUnguarded(Utils::SmallStringView name) const = 0; + virtual ModuleId fetchModuleIdUnguarded(Utils::SmallStringView name, Storage::ModuleKind moduleKind) const = 0; virtual TypeId fetchTypeIdByModuleIdAndExportedName(ModuleId moduleId, Utils::SmallStringView name) const = 0; }; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatchertypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatchertypes.h index a9185d91e8..04f11096bd 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<typename String> +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<typename String> + 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<ProjectChunkId>; @@ -67,6 +100,16 @@ public: return first.id == second.id && first.sourceIds == second.sourceIds; } + template<typename String> + 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 18c3931249..1592628af5 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h @@ -7,8 +7,10 @@ #include "projectstorageids.h" #include "projectstorageinfotypes.h" +#include <nanotrace/nanotracehr.h> #include <sqlite/sqlitevalue.h> #include <utils/smallstring.h> +#include <utils/utility.h> #include <tuple> #include <variant> @@ -45,6 +47,17 @@ public: < std::tie(second.sourceId, second.moduleId, second.version); } + template<typename String> + 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; @@ -57,11 +70,53 @@ namespace Synchronization { enum class TypeNameKind { Exported = 1, QualifiedExported = 2 }; -enum class FileType : char { QmlTypes, QmlDocument }; +template<typename String> +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, Directory }; + +template<typename String> +void convertToString(String &string, const FileType &type) +{ + switch (type) { + case FileType::QmlTypes: + convertToString(string, "QmlTypes"); + break; + case FileType::QmlDocument: + convertToString(string, "QmlDocument"); + break; + case FileType::Directory: + convertToString(string, "Directory"); + break; + } +} enum class IsQualified : int { No, Yes }; -inline int operator-(IsQualified first, IsQualified second) +template<typename String> +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<int>(first) - static_cast<int>(second); } @@ -78,6 +133,25 @@ enum class ImportKind : char { ModuleExportedModuleDependency }; +template<typename String> +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: @@ -97,6 +171,19 @@ public: && first.version == second.version; } + template<typename String> + 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; @@ -106,9 +193,22 @@ public: enum class IsAutoVersion : char { No, Yes }; +template<typename String> +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); + return Utils::to_underlying(first) < Utils::to_underlying(second); } class ModuleExportedImport @@ -137,6 +237,19 @@ public: < std::tie(second.moduleId, second.exportedModuleId, second.isAutoVersion, second.version); } + template<typename String> + 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; @@ -171,6 +284,20 @@ public: && first.version == second.version && first.isAutoVersion == second.isAutoVersion; } + template<typename String> + 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; @@ -192,6 +319,16 @@ public: return first.name == second.name; } + template<typename String> + 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; }; @@ -210,6 +347,17 @@ public: return first.name == second.name && first.import == second.import; } + template<typename String> + 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; @@ -264,6 +412,19 @@ public: < std::tie(second.moduleId, second.name, second.version); } + template<typename String> + 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; @@ -295,6 +456,20 @@ public: , exportedTypeNameId{exportedTypeNameId} {} + template<typename String> + 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; @@ -305,6 +480,43 @@ public: using ImportedTypeName = std::variant<ImportedType, QualifiedImportedType>; +template<typename String> +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)); + + 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: @@ -325,6 +537,18 @@ public: && first.hasValue == second.hasValue; } + template<typename String> + 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; @@ -349,6 +573,18 @@ public: && first.enumeratorDeclarations == second.enumeratorDeclarations; } + template<typename String> + 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; @@ -368,6 +604,20 @@ public: , id{id} {} + template<typename String> + 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; @@ -392,6 +642,18 @@ public: && first.traits == second.traits; } + template<typename String> + 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; @@ -418,6 +680,17 @@ public: return first.name == second.name && first.parameters == second.parameters; } + template<typename String> + 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; @@ -437,6 +710,18 @@ public: , id{id} {} + template<typename String> + 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; @@ -467,6 +752,18 @@ public: && first.parameters == second.parameters; } + template<typename String> + 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; @@ -489,6 +786,19 @@ public: , id{id} {} + template<typename String> + 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; @@ -498,6 +808,19 @@ public: enum class PropertyKind { Property, Alias }; +template<typename String> +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: @@ -567,6 +890,24 @@ public: && first.traits == second.traits && first.kind == second.kind; } + template<typename String> + 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", propertyDeclaration.kind)); + + convertToString(string, dict); + } + public: ::Utils::SmallString name; ImportedTypeName typeName; @@ -597,6 +938,21 @@ public: , aliasId{aliasId} {} + template<typename String> + 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 = {}; @@ -608,6 +964,22 @@ public: enum class ChangeLevel : char { Full, Minimal, ExcludeExportedTypes }; +template<typename String> +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: @@ -717,6 +1089,27 @@ public: && first.sourceId == second.sourceId; } + template<typename String> + 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", type.changeLevel), + keyValue("default property name", type.defaultPropertyName)); + + convertToString(string, dict); + } + public: TypeNameString typeName; ::Utils::SmallString defaultPropertyName; @@ -747,6 +1140,20 @@ public: , moduleId{moduleId} {} + template<typename String> + 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; @@ -757,39 +1164,55 @@ public: using PropertyEditorQmlPaths = std::vector<class PropertyEditorQmlPath>; -class ProjectData +class DirectoryInfo { public: - ProjectData(SourceId projectSourceId, SourceId sourceId, ModuleId moduleId, FileType fileType) - : projectSourceId{projectSourceId} + DirectoryInfo(SourceId directorySourceId, SourceId sourceId, ModuleId moduleId, FileType fileType) + : directorySourceId{directorySourceId} , sourceId{sourceId} , moduleId{moduleId} , fileType{fileType} {} - friend bool operator==(const ProjectData &first, const ProjectData &second) + friend bool operator==(const DirectoryInfo &first, const DirectoryInfo &second) { - return first.projectSourceId == second.projectSourceId && first.sourceId == second.sourceId + return first.directorySourceId == second.directorySourceId && first.sourceId == second.sourceId && first.moduleId.internalId() == second.moduleId.internalId() && first.fileType == second.fileType; } + template<typename String> + friend void convertToString(String &string, const DirectoryInfo &directoryInfo) + { + using NanotraceHR::dictonary; + using NanotraceHR::keyValue; + auto dict = dictonary(keyValue("project source id", directoryInfo.directorySourceId), + keyValue("source id", directoryInfo.sourceId), + keyValue("module id", directoryInfo.moduleId), + keyValue("file type", directoryInfo.fileType)); + + convertToString(string, dict); + } + public: - SourceId projectSourceId; + SourceId directorySourceId; SourceId sourceId; ModuleId moduleId; FileType fileType; }; -using ProjectDatas = std::vector<ProjectData>; +using DirectoryInfos = std::vector<DirectoryInfo>; 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, @@ -803,8 +1226,26 @@ public: , sourceId{sourceId} , moduleId{moduleId} , traits{traits} + , directorySourceId{directorySourceId} {} + template<typename String> + 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; @@ -814,6 +1255,7 @@ public: SourceId sourceId; ModuleId moduleId; TypeTraits traits; + SourceId directorySourceId; }; using TypeAnnotations = std::vector<TypeAnnotation>; @@ -853,9 +1295,9 @@ public: , fileStatuses(std::move(fileStatuses)) {} - SynchronizationPackage(SourceIds updatedProjectSourceIds, ProjectDatas projectDatas) - : projectDatas(std::move(projectDatas)) - , updatedProjectSourceIds(std::move(updatedProjectSourceIds)) + SynchronizationPackage(SourceIds updatedDirectoryInfoSourceIds, DirectoryInfos directoryInfos) + : directoryInfos(std::move(directoryInfos)) + , updatedDirectoryInfoSourceIds(std::move(updatedDirectoryInfoSourceIds)) {} public: @@ -864,8 +1306,8 @@ public: SourceIds updatedSourceIds; SourceIds updatedFileStatusSourceIds; FileStatuses fileStatuses; - ProjectDatas projectDatas; - SourceIds updatedProjectSourceIds; + DirectoryInfos directoryInfos; + SourceIds updatedDirectoryInfoSourceIds; Imports moduleDependencies; SourceIds updatedModuleDependencySourceIds; ModuleExportedImports moduleExportedImports; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp index 62fcf310f6..a0e7bba3c5 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp @@ -11,8 +11,11 @@ #include "qmltypesparserinterface.h" #include "sourcepath.h" #include "sourcepathcache.h" +#include "typeannotationreader.h" #include <sqlitedatabase.h> +#include <tracing/qmldesignertracing.h> +#include <utils/set_algorithm.h> #include <QDirIterator> #include <QRegularExpression> @@ -21,6 +24,26 @@ #include <functional> namespace QmlDesigner { +constexpr auto category = ProjectStorageTracing::projectStorageUpdaterCategory; +using NanotraceHR::keyValue; +using Tracer = ProjectStorageTracing::Category::TracerType; + +template<typename String> +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) @@ -79,6 +102,8 @@ ProjectStorageUpdater::Components createComponents( } for (const QmlDirParser::Component &qmlDirParserComponent : qmlDirParserComponents) { + if (qmlDirParserComponent.fileName.contains('/')) + continue; components.push_back(ProjectStorageUpdater::Component{qmlDirParserComponent.fileName, qmlDirParserComponent.typeName, moduleId, @@ -110,10 +135,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::DirectoryInfos &directoryInfos, + TracerLiteral message, + Tracer &tracer) { - for (const auto &projectData : projectDatas) - sourceIds.push_back(projectData.sourceId); + for (const auto &directoryInfo : directoryInfos) { + tracer.tick(message, keyValue("source id", directoryInfo.sourceId)); + sourceIds.push_back(directoryInfo.sourceId); + } } Storage::Version convertVersion(LanguageUtils::ComponentVersion version) @@ -131,34 +161,84 @@ Storage::Synchronization::IsAutoVersion convertToIsAutoVersion(QmlDirParser::Imp void addDependencies(Storage::Imports &dependencies, SourceId sourceId, const QList<QmlDirParser::Import> &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); + ModuleId moduleId = projectStorage.moduleId(Utils::PathString{qmldirDependency.module}, + Storage::ModuleKind::CppLibrary); + auto &import = dependencies.emplace_back(moduleId, Storage::Version{}, sourceId); + tracer.tick(message, keyValue("import", import)); } } +void addModuleExportedImport(Storage::Synchronization::ModuleExportedImports &imports, + ModuleId moduleId, + ModuleId exportedModuleId, + Storage::Version version, + Storage::Synchronization::IsAutoVersion isAutoVersion, + std::string_view moduleName, + Storage::ModuleKind moduleKind, + std::string_view exportedModuleName) +{ + NanotraceHR::Tracer tracer{"add module exported imports"_t, + category(), + keyValue("module id", moduleId), + keyValue("exported module id", exportedModuleId), + keyValue("version", version), + keyValue("is auto version", isAutoVersion), + keyValue("module name", moduleName), + keyValue("module kind", moduleKind), + keyValue("exported module name", exportedModuleName)}; + + imports.emplace_back(moduleId, exportedModuleId, version, isAutoVersion); +} + +bool isOptionalImport(QmlDirParser::Import::Flags flags) +{ + return flags & QmlDirParser::Import::Optional && !(flags & QmlDirParser::Import::OptionalDefault); +} + void addModuleExportedImports(Storage::Synchronization::ModuleExportedImports &imports, ModuleId moduleId, ModuleId cppModuleId, + std::string_view moduleName, const QList<QmlDirParser::Import> &qmldirImports, ProjectStorageInterface &projectStorage) { + NanotraceHR::Tracer tracer{"add module exported imports"_t, + category(), + keyValue("cpp module id", cppModuleId), + keyValue("module id", moduleId)}; + 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)); + if (isOptionalImport(qmldirImport.flags)) + continue; - ModuleId exportedCppModuleId = projectStorage.moduleId( - Utils::PathString{qmldirImport.module} + "-cppnative"); - imports.emplace_back(cppModuleId, - exportedCppModuleId, - Storage::Version{}, - Storage::Synchronization::IsAutoVersion::No); + Utils::PathString exportedModuleName{qmldirImport.module}; + using Storage::ModuleKind; + ModuleId exportedModuleId = projectStorage.moduleId(exportedModuleName, + ModuleKind::QmlLibrary); + addModuleExportedImport(imports, + moduleId, + exportedModuleId, + convertVersion(qmldirImport.version), + convertToIsAutoVersion(qmldirImport.flags), + moduleName, + ModuleKind::QmlLibrary, + exportedModuleName); + + ModuleId exportedCppModuleId = projectStorage.moduleId(exportedModuleName, + ModuleKind::CppLibrary); + addModuleExportedImport(imports, + cppModuleId, + exportedCppModuleId, + Storage::Version{}, + Storage::Synchronization::IsAutoVersion::No, + moduleName, + ModuleKind::CppLibrary, + exportedModuleName); } } @@ -182,8 +262,14 @@ std::vector<IdPaths> 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(), + keyValue("directories", directories), + keyValue("qml types paths", qmlTypesPaths)}; + Storage::Synchronization::SynchronizationPackage package; WatchedSourceIdsIds watchedSourceIds{Utils::span{directories}.size()}; NotUpdatedSourceIds notUpdatedSourceIds{Utils::span{directories}.size()}; @@ -191,6 +277,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)); @@ -198,7 +285,13 @@ 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 (const TypeNameDoesNotExists &exception) { + qDebug() << "missing type: " << exception.what(); + } catch (...) { + qWarning() << "Project storage could not been updated!"; + } m_pathWatcher.updateIdPaths(createIdPaths(watchedSourceIds, m_projectPartId)); } @@ -211,25 +304,30 @@ void ProjectStorageUpdater::updateQmlTypes(const QStringList &qmlTypesPaths, if (qmlTypesPaths.empty()) return; - ModuleId moduleId = m_projectStorage.moduleId("QML-cppnative"); + NanotraceHR::Tracer tracer{"update qmltypes file"_t, category()}; + + ModuleId moduleId = m_projectStorage.moduleId("QML", Storage::ModuleKind::CppLibrary); 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, - moduleId, - Storage::Synchronization::FileType::QmlTypes}; + Storage::Synchronization::DirectoryInfo directoryInfo{ + sourceId, sourceId, moduleId, Storage::Synchronization::FileType::QmlTypes}; - FileState state = parseTypeInfo(projectData, + FileState state = parseTypeInfo(directoryInfo, Utils::PathString{qmlTypesPath}, package, notUpdatedSourceIds); if (state == FileState::Changed) { - package.projectDatas.push_back(std::move(projectData)); - package.updatedProjectSourceIds.push_back(sourceId); + tracer.tick("append project data"_t, keyValue("project data", directoryInfo)); + package.directoryInfos.push_back(std::move(directoryInfo)); + tracer.tick("append updated project source ids"_t, keyValue("source id", sourceId)); + package.updatedDirectoryInfoSourceIds.push_back(sourceId); } } } @@ -246,24 +344,199 @@ 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); + } + + using Storage::ModuleKind; + Utils::PathString moduleName{parser.typeNamespace()}; + ModuleId moduleId = m_projectStorage.moduleId(moduleName, ModuleKind::QmlLibrary); + ModuleId cppModuleId = m_projectStorage.moduleId(moduleName, ModuleKind::CppLibrary); + ModuleId pathModuleId = m_projectStorage.moduleId(directoryPath, ModuleKind::PathLibrary); + + auto imports = filterMultipleEntries(parser.imports()); + + addModuleExportedImports(package.moduleExportedImports, + moduleId, + cppModuleId, + moduleName, + imports, + m_projectStorage); + tracer.tick("append updated module id"_t, keyValue("module id", moduleId)); + package.updatedModuleIds.push_back(moduleId); + + const auto qmlDirectoryInfos = m_projectStorage.fetchDirectoryInfos(directorySourceId); + addSourceIds(package.updatedSourceIds, qmlDirectoryInfos, "append updated source id"_t, tracer); + addSourceIds(package.updatedFileStatusSourceIds, + qmlDirectoryInfos, + "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.updatedDirectoryInfoSourceIds.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); + updateDirectory({directory}, {}, package, notUpdatedSourceIds, watchedSourceIdsIds); +} + +void ProjectStorageUpdater::updateSubdirectories(const Utils::PathString &directoryPath, + SourceId directorySourceId, + FileState directoryState, + const SourceContextIds &subdirectoriesToIgnore, + Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIdsIds) +{ + struct Directory + { + Directory(Utils::SmallStringView path, SourceContextId sourceContextId, SourceId sourceId) + : path{path} + , sourceContextId{sourceContextId} + , sourceId{sourceId} + {} + + bool operator<(const Directory &other) const + { + return sourceContextId < other.sourceContextId; + } + + bool operator==(const Directory &other) const + { + return sourceContextId == other.sourceContextId; + } + + Utils::PathString path; + SourceContextId sourceContextId; + SourceId sourceId; + }; + + struct Compare + { + bool operator()(const Directory &first, const Directory &second) const + { + return first.sourceContextId < second.sourceContextId; + } + + bool operator()(const Directory &first, SourceContextId second) const + { + return first.sourceContextId < second; + } + + bool operator()(SourceContextId first, const Directory &second) const + { + return first < second.sourceContextId; + } + }; + + using Directories = QVarLengthArray<Directory, 32>; + + auto subdirectorySourceIds = m_projectStorage.fetchSubdirectorySourceIds(directorySourceId); + auto subdirectories = Utils::transform<Directories>( + subdirectorySourceIds, [&](SourceId sourceId) -> Directory { + auto sourceContextId = m_pathCache.sourceContextId(sourceId); + auto subdirectoryPath = m_pathCache.sourceContextPath(sourceContextId); + return {subdirectoryPath, sourceContextId, sourceId}; + }); + + auto exisitingSubdirectoryPaths = m_fileSystem.subdirectories(directoryPath.toQString()); + Directories existingSubdirecories; + for (const QString &subdirectory : exisitingSubdirectoryPaths) { + if (subdirectory.endsWith("/designer") || subdirectory.endsWith("/QtQuick/Scene2D") + || subdirectory.endsWith("/QtQuick/Scene3D")) + continue; + Utils::PathString subdirectoryPath = subdirectory; + auto [sourceContextId, sourceId] = m_pathCache.sourceContextAndSourceId( + SourcePath{subdirectoryPath + "/."}); + subdirectories.emplace_back(subdirectoryPath, sourceContextId, sourceId); + existingSubdirecories.emplace_back(subdirectoryPath, sourceContextId, sourceId); + } + + std::sort(subdirectories.begin(), subdirectories.end()); + subdirectories.erase(std::unique(subdirectories.begin(), subdirectories.end()), + subdirectories.end()); + + std::set_difference(subdirectories.begin(), + subdirectories.end(), + subdirectoriesToIgnore.begin(), + subdirectoriesToIgnore.end(), + Utils::make_iterator([&](const Directory &subdirectory) { + updateDirectory(subdirectory.path, + subdirectoriesToIgnore, + package, + notUpdatedSourceIds, + watchedSourceIdsIds); + }), + Compare{}); + + if (directoryState == FileState::Changed) { + for (const auto &[subdirectoryPath, sourceContextId, subdirectorySourceId] : + existingSubdirecories) { + package.directoryInfos.emplace_back(directorySourceId, + subdirectorySourceId, + ModuleId{}, + Storage::Synchronization::FileType::Directory); + } + } } void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPath, + const SourceContextIds &subdirectoriesToIgnore, Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds, WatchedSourceIdsIds &watchedSourceIdsIds) { + 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); @@ -271,82 +544,69 @@ 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: { - parseProjectDatas(m_projectStorage.fetchProjectDatas(directorySourceId), - package, - notUpdatedSourceIds, - watchedSourceIdsIds); + tracer.tick("update directory not changed"_t); + + parseDirectoryInfos(m_projectStorage.fetchDirectoryInfos(directorySourceId), + package, + notUpdatedSourceIds, + watchedSourceIdsIds); break; } case FileState::NotExists: { + tracer.tick("update directory don't exits"_t); + package.updatedFileStatusSourceIds.push_back(directorySourceId); - package.updatedFileStatusSourceIds.push_back(qmlDirSourceId); - package.updatedProjectSourceIds.push_back(directorySourceId); - package.updatedSourceIds.push_back(qmlDirSourceId); - auto qmlProjectDatas = m_projectStorage.fetchProjectDatas(directorySourceId); - for (const Storage::Synchronization::ProjectData &projectData : qmlProjectDatas) { - package.updatedSourceIds.push_back(projectData.sourceId); - package.updatedFileStatusSourceIds.push_back(projectData.sourceId); + package.updatedFileStatusSourceIds.push_back(qmldirSourceId); + package.updatedDirectoryInfoSourceIds.push_back(directorySourceId); + package.updatedSourceIds.push_back(qmldirSourceId); + auto qmlDirectoryInfos = m_projectStorage.fetchDirectoryInfos(directorySourceId); + for (const Storage::Synchronization::DirectoryInfo &directoryInfo : qmlDirectoryInfos) { + tracer.tick("append updated source id"_t, keyValue("source id", directoryInfo.sourceId)); + package.updatedSourceIds.push_back(directoryInfo.sourceId); + tracer.tick("append updated file status source id"_t, + keyValue("source id", directoryInfo.sourceId)); + package.updatedFileStatusSourceIds.push_back(directoryInfo.sourceId); } break; } } + + updateSubdirectories(directoryPath, + directorySourceId, + directoryState, + subdirectoriesToIgnore, + package, + notUpdatedSourceIds, + watchedSourceIdsIds); + + 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( @@ -354,6 +614,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; @@ -368,49 +632,196 @@ void ProjectStorageUpdater::updatePropertyEditorPaths( auto state = fileState(directorySourceId, package, notUpdatedSourceIds); - if (state == FileState::Changed) - updatePropertyEditorPath(pathInfo.filePath(), package, directorySourceId); + if (state == FileState::Changed) { + updatePropertyEditorPath(pathInfo.filePath(), + package, + directorySourceId, + propertyEditorResourcesPath.size() + 1); + } } } +namespace { + +template<typename SourceIds1, typename SourceIds2> +SmallSourceIds<16> mergedSourceIds(const SourceIds1 &sourceIds1, const SourceIds2 &sourceIds2) +{ + 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<SourceId, SmallSourceIds<16>> 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 & /*propertyEditorResourcesPath*/, - Storage::Synchronization::SynchronizationPackage & /*package*/, - NotUpdatedSourceIds & /*notUpdatedSourceIds*/) + const QString &rootDirectoryPath, + Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds, + std::map<SourceId, SmallSourceIds<16>> &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<SourceId, SmallSourceIds<16>> &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) { - // const auto typeAnnotations = dir.entryInfoList({"*.metainfo"}, QDir::Files); + 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( const QString &directoryPath, Storage::Synchronization::SynchronizationPackage &package, - SourceId directorySourceId) + SourceId directorySourceId, + long long pathOffset) { + 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); for (const auto &fileInfo : fileInfos) - updatePropertyEditorFilePath(fileInfo.filePath(), package, directorySourceId); + updatePropertyEditorFilePath(fileInfo.filePath(), package, directorySourceId, pathOffset); } void ProjectStorageUpdater::updatePropertyEditorFilePath( const QString &path, Storage::Synchronization::SynchronizationPackage &package, - SourceId directorySourceId) + SourceId directorySourceId, + long long pathOffset) { - QRegularExpression regex{R"xo(.+\/(\w+)\/(\w+)(Specifics|Pane).qml)xo"}; - auto match = regex.match(path); + NanotraceHR::Tracer tracer{"update property editor file path"_t, + category(), + keyValue("directory path", path), + keyValue("directory source id", directorySourceId)}; + + QRegularExpression regex{R"xo((.+)\/(\w+)(Specifics|Pane).qml)xo"}; + auto match = regex.match(QStringView{path}.mid(pathOffset)); QString oldModuleName; ModuleId moduleId; if (match.hasMatch()) { - auto moduleName = match.capturedView(1); + auto moduleName = match.capturedView(1).toString(); + moduleName.replace('/', '.'); if (oldModuleName != moduleName) { - oldModuleName = moduleName.toString(); - moduleId = m_projectStorage.moduleId(Utils::SmallString{moduleName}); + oldModuleName = moduleName; + moduleId = m_projectStorage.moduleId(Utils::SmallString{moduleName}, + Storage::ModuleKind::QmlLibrary); } 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)); } } @@ -447,6 +858,10 @@ bool contains(const Container &container, Id id) void ProjectStorageUpdater::pathsWithIdsChanged(const std::vector<IdPaths> &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; @@ -486,7 +901,11 @@ void ProjectStorageUpdater::pathsWithIdsChanged(const std::vector<IdPaths> &chan for (auto sourceContextId : directorySourceContextIds) { Utils::PathString directory = m_pathCache.sourceContextPath(sourceContextId); - updateDirectory(directory, package, notUpdatedSourceIds, watchedSourceIds); + updateDirectory(directory, + directorySourceContextIds, + package, + notUpdatedSourceIds, + watchedSourceIds); } for (SourceId sourceId : filterUniqueSourceIds(qmlDocumentSourceIds)) { @@ -498,9 +917,9 @@ void ProjectStorageUpdater::pathsWithIdsChanged(const std::vector<IdPaths> &chan for (SourceId sourceId : filterUniqueSourceIds(std::move(qmltypesSourceIds))) { if (!contains(directorySourceContextIds, m_pathCache.sourceContextId(sourceId))) { auto qmltypesPath = m_pathCache.sourcePath(sourceId); - auto projectData = m_projectStorage.fetchProjectData(sourceId); - if (projectData) - parseTypeInfo(*projectData, qmltypesPath, package, notUpdatedSourceIds); + auto directoryInfo = m_projectStorage.fetchDirectoryInfo(sourceId); + if (directoryInfo) + parseTypeInfo(*directoryInfo, qmltypesPath, package, notUpdatedSourceIds); } } } catch (const QmlDesigner::CannotParseQmlTypesFile &) { @@ -539,72 +958,99 @@ 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( + const auto &directoryInfo = package.directoryInfos.emplace_back( directorySourceId, sourceId, moduleId, Storage::Synchronization::FileType::QmlTypes); + tracer.tick("append project data"_t, keyValue("source id", sourceId)); - parseTypeInfo(projectData, qmltypesPath, package, notUpdatedSourceIds); + parseTypeInfo(directoryInfo, qmltypesPath, package, notUpdatedSourceIds); } } -void ProjectStorageUpdater::parseProjectDatas(const Storage::Synchronization::ProjectDatas &projectDatas, - Storage::Synchronization::SynchronizationPackage &package, - NotUpdatedSourceIds ¬UpdatedSourceIds, - WatchedSourceIdsIds &watchedSourceIds) +void ProjectStorageUpdater::parseDirectoryInfos( + const Storage::Synchronization::DirectoryInfos &directoryInfos, + Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIds) { - for (const Storage::Synchronization::ProjectData &projectData : projectDatas) { - switch (projectData.fileType) { + NanotraceHR::Tracer tracer{"parse project datas"_t, category()}; + + for (const Storage::Synchronization::DirectoryInfo &directoryInfo : directoryInfos) { + switch (directoryInfo.fileType) { case Storage::Synchronization::FileType::QmlTypes: { - watchedSourceIds.qmltypesSourceIds.push_back(projectData.sourceId); + watchedSourceIds.qmltypesSourceIds.push_back(directoryInfo.sourceId); - auto qmltypesPath = m_pathCache.sourcePath(projectData.sourceId); - parseTypeInfo(projectData, qmltypesPath, package, notUpdatedSourceIds); + auto qmltypesPath = m_pathCache.sourcePath(directoryInfo.sourceId); + parseTypeInfo(directoryInfo, qmltypesPath, package, notUpdatedSourceIds); break; } case Storage::Synchronization::FileType::QmlDocument: { - watchedSourceIds.qmlSourceIds.push_back(projectData.sourceId); + watchedSourceIds.qmlSourceIds.push_back(directoryInfo.sourceId); - parseQmlComponent(projectData.sourceId, package, notUpdatedSourceIds); + parseQmlComponent(directoryInfo.sourceId, package, notUpdatedSourceIds); + break; } + case Storage::Synchronization::FileType::Directory: + break; } } } -auto ProjectStorageUpdater::parseTypeInfo(const Storage::Synchronization::ProjectData &projectData, +auto ProjectStorageUpdater::parseTypeInfo(const Storage::Synchronization::DirectoryInfo &directoryInfo, Utils::SmallStringView qmltypesPath, Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds) -> FileState { - auto state = fileState(projectData.sourceId, package, notUpdatedSourceIds); + NanotraceHR::Tracer tracer{"parse type info"_t, + category(), + keyValue("qmltypes path", qmltypesPath)}; + + auto state = fileState(directoryInfo.sourceId, package, notUpdatedSourceIds); switch (state) { case FileState::Changed: { - package.updatedSourceIds.push_back(projectData.sourceId); + tracer.tick("append updated source ids"_t, keyValue("source id", directoryInfo.sourceId)); + package.updatedSourceIds.push_back(directoryInfo.sourceId); const auto content = m_fileSystem.contentAsQString(QString{qmltypesPath}); - m_qmlTypesParser.parse(content, package.imports, package.types, projectData); + m_qmlTypesParser.parse(content, package.imports, package.types, directoryInfo); break; } case FileState::NotChanged: { - notUpdatedSourceIds.sourceIds.push_back(projectData.sourceId); + tracer.tick("append not updated source ids"_t, keyValue("source id", directoryInfo.sourceId)); + notUpdatedSourceIds.sourceIds.push_back(directoryInfo.sourceId); break; } case FileState::NotExists: throw CannotParseQmlTypesFile{}; - break; } + tracer.end(keyValue("state", state)); + return state; } @@ -617,6 +1063,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; @@ -626,16 +1080,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 &directoryInfo = package.directoryInfos.emplace_back( + directorySourceId, sourceId, ModuleId{}, Storage::Synchronization::FileType::QmlDocument); + tracer.tick("append project data"_t, keyValue("project data", directoryInfo)); return; } @@ -649,11 +1105,11 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil break; } - package.projectDatas.emplace_back(directorySourceId, - sourceId, - ModuleId{}, - Storage::Synchronization::FileType::QmlDocument); + const auto &directoryInfo = package.directoryInfos.emplace_back( + directorySourceId, sourceId, ModuleId{}, Storage::Synchronization::FileType::QmlDocument); + tracer.tick("append project data"_t, keyValue("project data", directoryInfo)); + tracer.tick("append updated source id"_t, keyValue("source id", sourceId)); package.updatedSourceIds.push_back(sourceId); type.typeName = SourcePath{qmlFilePath}.name(); @@ -661,6 +1117,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)); } @@ -668,10 +1126,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) @@ -687,6 +1148,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)); } @@ -733,6 +1196,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; }); @@ -760,22 +1229,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 187a2219d0..baecbd6b11 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h @@ -11,10 +11,15 @@ #include "projectstoragetypes.h" #include "sourcepath.h" +#include <modelfwd.h> + +#include <tracing/qmldesignertracing.h> + #include <qmljs/parser/qmldirparser_p.h> #include <QStringList> +#include <map> #include <vector> namespace Sqlite { @@ -27,7 +32,6 @@ class ProjectStorageInterface; template<typename ProjectStorage, typename Mutex> class SourcePathCache; class FileStatusCache; -template<typename Database> class ProjectStorage; class QmlDocumentParserInterface; class QmlTypesParserInterface; @@ -35,10 +39,10 @@ class QmlTypesParserInterface; class ProjectStorageUpdater final : public ProjectStoragePathWatcherNotifierInterface { public: - using PathCache = SourcePathCache<ProjectStorage<Sqlite::Database>, NonLockingMutex>; + using PathCache = SourcePathCache<ProjectStorage, NonLockingMutex>; ProjectStorageUpdater(FileSystemInterface &fileSystem, - ProjectStorageInterface &projectStorage, + ProjectStorageType &projectStorage, FileStatusCache &fileStatusCache, PathCache &pathCache, QmlDocumentParserInterface &qmlDocumentParser, @@ -57,7 +61,8 @@ public: void update(QStringList directories, QStringList qmlTypesPaths, - const QString &propertyEditorResourcesPath); + const QString &propertyEditorResourcesPath, + const QStringList &typeAnnotationPaths); void pathsWithIdsChanged(const std::vector<IdPaths> &idPaths) override; void pathsChanged(const SourceIds &filePathIds) override; @@ -138,22 +143,54 @@ private: WatchedSourceIdsIds &watchedSourceIdsIds); void updateDirectory(const Utils::PathString &directory, + const SourceContextIds &subdirecoriesToIgnore, Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds, WatchedSourceIdsIds &watchedSourceIdsIds); + void updateSubdirectories(const Utils::PathString &directory, + SourceId directorySourceId, + FileState directoryFileState, + const SourceContextIds &subdirecoriesToIgnore, + Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIdsIds); + void updateDirectoryChanged(std::string_view directoryPath, + FileState qmldirState, + SourcePath qmldirSourcePath, + 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, NotUpdatedSourceIds ¬UpdatedSourceIds); - void updateTypeAnnotations(const QString &propertyEditorResourcesPath, + void updateTypeAnnotations(const QString &directoryPath, + Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds, + std::map<SourceId, SmallSourceIds<16>> &updatedSourceIdsDictonary); + void updateTypeAnnotationDirectories(Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds, + std::map<SourceId, SmallSourceIds<16>> &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); + SourceId directorySourceId, + long long pathOffset); void updatePropertyEditorFilePath(const QString &filePath, Storage::Synchronization::SynchronizationPackage &package, - SourceId directorySourceId); + SourceId directorySourceId, + long long pathOffset); void parseTypeInfos(const QStringList &typeInfos, const QList<QmlDirParser::Import> &qmldirDependencies, const QList<QmlDirParser::Import> &qmldirImports, @@ -163,11 +200,11 @@ private: Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds, WatchedSourceIdsIds &watchedSourceIdsIds); - void parseProjectDatas(const Storage::Synchronization::ProjectDatas &projectDatas, - Storage::Synchronization::SynchronizationPackage &package, - NotUpdatedSourceIds ¬UpdatedSourceIds, - WatchedSourceIdsIds &watchedSourceIdsIds); - FileState parseTypeInfo(const Storage::Synchronization::ProjectData &projectData, + void parseDirectoryInfos(const Storage::Synchronization::DirectoryInfos &directoryInfos, + Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIdsIds); + FileState parseTypeInfo(const Storage::Synchronization::DirectoryInfo &directoryInfo, Utils::SmallStringView qmltypesPath, Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds); @@ -197,7 +234,7 @@ private: private: std::vector<IdPaths> 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/qmldocumentparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp index f9eb8080f7..4338da62ce 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp @@ -10,6 +10,8 @@ #include <sqlitedatabase.h> +#include <tracing/qmldesignertracing.h> + #ifdef QDS_BUILD_QMLPARSER #include <private/qqmldomtop_p.h> #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; @@ -60,23 +66,32 @@ Storage::Import createImport(const QmlDom::Import &qmlImport, Utils::SmallStringView directoryPath, QmlDocumentParser::ProjectStorage &storage) { + using Storage::ModuleKind; using QmlUriKind = QQmlJS::Dom::QmlUri::Kind; auto &&uri = qmlImport.uri; - if (uri.kind() == QmlUriKind::RelativePath) { + switch (uri.kind()) { + case QmlUriKind::AbsolutePath: + case QmlUriKind::DirectoryUrl: { + auto moduleId = storage.moduleId(Utils::PathString{uri.toString()}, ModuleKind::PathLibrary); + return Storage::Import(moduleId, convertVersion(qmlImport.version), sourceId); + } + case QmlUriKind::RelativePath: { auto path = createNormalizedPath(directoryPath, uri.localPath()); - auto moduleId = storage.moduleId(createNormalizedPath(directoryPath, uri.localPath())); + auto moduleId = storage.moduleId(createNormalizedPath(directoryPath, uri.localPath()), + ModuleKind::PathLibrary); return Storage::Import(moduleId, Storage::Version{}, sourceId); } - - if (uri.kind() == QmlUriKind::ModuleUri) { - auto moduleId = storage.moduleId(Utils::PathString{uri.moduleUri()}); + case QmlUriKind::ModuleUri: { + auto moduleId = storage.moduleId(Utils::PathString{uri.moduleUri()}, ModuleKind::QmlLibrary); return Storage::Import(moduleId, convertVersion(qmlImport.version), sourceId); } + case QmlUriKind::Invalid: + return Storage::Import{}; + } - auto moduleId = storage.moduleId(Utils::PathString{uri.toString()}); - return Storage::Import(moduleId, convertVersion(qmlImport.version), sourceId); + return Storage::Import{}; } QualifiedImports createQualifiedImports(const QList<QmlDom::Import> &qmlImports, @@ -84,6 +99,11 @@ QualifiedImports createQualifiedImports(const QList<QmlDom::Import> &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 +112,8 @@ QualifiedImports createQualifiedImports(const QList<QmlDom::Import> &qmlImports, createImport(qmlImport, sourceId, directoryPath, storage)); } + tracer.end(keyValue("qualified imports", qualifiedImports)); + return qualifiedImports; } @@ -109,11 +131,13 @@ void addImports(Storage::Imports &imports, } } - auto localDirectoryModuleId = storage.moduleId(directoryPath); + using Storage::ModuleKind; + + auto localDirectoryModuleId = storage.moduleId(directoryPath, ModuleKind::PathLibrary); imports.emplace_back(localDirectoryModuleId, Storage::Version{}, sourceId); ++importCount; - auto qmlModuleId = storage.moduleId("QML"); + auto qmlModuleId = storage.moduleId("QML", ModuleKind::QmlLibrary); imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId); ++importCount; @@ -280,6 +304,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; @@ -335,7 +364,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/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h index b8ab4ec4b1..1b494a2f69 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<Sqlite::Database>; + using ProjectStorage = QmlDesigner::ProjectStorage; using PathCache = QmlDesigner::SourcePathCache<ProjectStorage, NonLockingMutex>; #ifdef QDS_BUILD_QMLPARSER diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp index 3768535299..b3ec4f0024 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp @@ -5,8 +5,12 @@ #include "projectstorage.h" +#include <tracing/qmldesignertracing.h> + #include <sqlitedatabase.h> +#include <utils/span.h> + #ifdef QDS_BUILD_QMLPARSER #include <private/qqmldomtop_p.h> #include <private/qqmljstypedescriptionreader_p.h> @@ -21,6 +25,10 @@ namespace QmlDesigner { #ifdef QDS_BUILD_QMLPARSER +constexpr auto category = ProjectStorageTracing::projectStorageUpdaterCategory; +using NanotraceHR::keyValue; +using Tracer = ProjectStorageTracing::Category::TracerType; +using Storage::ModuleKind; namespace QmlDom = QQmlJS::Dom; namespace { @@ -31,6 +39,8 @@ using Storage::TypeNameString; ComponentWithoutNamespaces createComponentNameWithoutNamespaces(const QList<QQmlJSExportedScope> &objects) { + NanotraceHR::Tracer tracer{"parse qmltypes file"_t, category()}; + ComponentWithoutNamespaces componentWithoutNamespaces; for (const auto &object : objects) { @@ -46,23 +56,24 @@ ComponentWithoutNamespaces createComponentNameWithoutNamespaces(const QList<QQml name); } + tracer.end(keyValue("components without namespace", componentWithoutNamespaces)); + return componentWithoutNamespaces; } -void appendImports(Storage::Imports &imports, - const QString &dependency, - SourceId sourceId, - QmlTypesParser::ProjectStorage &storage) +const Storage::Import &appendImports(Storage::Imports &imports, + const QString &dependency, + SourceId sourceId, + QmlTypesParser::ProjectStorage &storage) { auto spaceFound = std::find_if(dependency.begin(), dependency.end(), [](QChar c) { return c.isSpace(); }); Utils::PathString moduleName{QStringView(dependency.begin(), spaceFound)}; - moduleName.append("-cppnative"); - ModuleId cppModuleId = storage.moduleId(moduleName); + ModuleId cppModuleId = storage.moduleId(moduleName, ModuleKind::CppLibrary); - imports.emplace_back(cppModuleId, Storage::Version{}, sourceId); + return imports.emplace_back(cppModuleId, Storage::Version{}, sourceId); } void addImports(Storage::Imports &imports, @@ -71,13 +82,26 @@ void addImports(Storage::Imports &imports, QmlTypesParser::ProjectStorage &storage, ModuleId cppModuleId) { - for (const QString &dependency : dependencies) - appendImports(imports, dependency, sourceId, storage); + NanotraceHR::Tracer tracer{ + "add imports"_t, + category(), + keyValue("source id", sourceId), + keyValue("module id", cppModuleId), + }; + + for (const QString &dependency : dependencies) { + const auto &import = appendImports(imports, dependency, sourceId, storage); + tracer.tick("append import"_t, keyValue("import", import), keyValue("dependency", dependency)); + } - imports.emplace_back(cppModuleId, Storage::Version{}, sourceId); + const auto &import = imports.emplace_back(cppModuleId, Storage::Version{}, sourceId); + tracer.tick("append import"_t, keyValue("import", import)); - if (ModuleId qmlCppModuleId = storage.moduleId("QML-cppnative"); cppModuleId != qmlCppModuleId) - imports.emplace_back(qmlCppModuleId, Storage::Version{}, sourceId); + if (ModuleId qmlCppModuleId = storage.moduleId("QML", ModuleKind::CppLibrary); + cppModuleId != qmlCppModuleId) { + const auto &import = imports.emplace_back(qmlCppModuleId, Storage::Version{}, sourceId); + tracer.tick("append import"_t, keyValue("import", import)); + } } Storage::TypeTraits createAccessTypeTraits(QQmlJSScope::AccessSemantics accessSematics) @@ -121,7 +145,8 @@ Storage::Synchronization::ExportedTypes createExports(const QList<QQmlJSScope::E for (const QQmlJSScope::Export &qmlExport : qmlExports) { TypeNameString exportedTypeName{qmlExport.type()}; - exportedTypes.emplace_back(storage.moduleId(Utils::SmallString{qmlExport.package()}), + exportedTypes.emplace_back(storage.moduleId(Utils::SmallString{qmlExport.package()}, + ModuleKind::QmlLibrary), std::move(exportedTypeName), createVersion(qmlExport.version())); } @@ -412,6 +437,11 @@ void addType(Storage::Synchronization::Types &types, QmlTypesParser::ProjectStorage &storage, const ComponentWithoutNamespaces &componentNameWithoutNamespace) { + NanotraceHR::Tracer tracer{"add type"_t, + category(), + keyValue("source id", sourceId), + keyValue("module id", cppModuleId)}; + const auto &component = *exportScope.scope; auto [functionsDeclarations, signalDeclarations] = createFunctionAndSignals( @@ -421,7 +451,7 @@ void addType(Storage::Synchronization::Types &types, auto exports = exportScope.exports; auto enumerationTypes = addEnumerationTypes(types, typeName, sourceId, cppModuleId, enumerations); - types.emplace_back( + const auto &type = types.emplace_back( Utils::SmallStringView{typeName}, Storage::Synchronization::ImportedType{TypeNameString{component.baseTypeName()}}, Storage::Synchronization::ImportedType{TypeNameString{component.extensionTypeName()}}, @@ -431,24 +461,61 @@ 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)); +} + +using namespace Qt::StringLiterals; + +constexpr auto skipLists = std::make_tuple( + std::pair{std::pair{"QtQuick.Templates"_sv, ModuleKind::CppLibrary}, std::array{"QQuickItem"_L1}}); + +Utils::span<const QLatin1StringView> getSkipList(const Storage::Module &module) +{ + static constexpr Utils::span<const QLatin1StringView> emptySkipList; + auto currentSkipList = emptySkipList; + + std::apply( + [&](const auto &entry) { + if (entry.first.first == module.name && entry.first.second == module.kind) + currentSkipList = entry.second; + }, + skipLists); + + return currentSkipList; +} + +bool skipType(const QQmlJSExportedScope &object, Utils::span<const QLatin1StringView> 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 Storage::Synchronization::DirectoryInfo &directoryInfo, const QList<QQmlJSExportedScope> &objects, QmlTypesParser::ProjectStorage &storage, const ComponentWithoutNamespaces &componentNameWithoutNamespaces) { + NanotraceHR::Tracer tracer{"add types"_t, category()}; types.reserve(Utils::usize(objects) + types.size()); - for (const auto &object : objects) + const auto skipList = getSkipList(storage.module(directoryInfo.moduleId)); + + for (const auto &object : objects) { + if (skipType(object, skipList)) + continue; + addType(types, - projectData.sourceId, - projectData.moduleId, + directoryInfo.sourceId, + directoryInfo.moduleId, object, storage, componentNameWithoutNamespaces); + } } } // namespace @@ -456,8 +523,10 @@ void addTypes(Storage::Synchronization::Types &types, void QmlTypesParser::parse(const QString &sourceContent, Storage::Imports &imports, Storage::Synchronization::Types &types, - const Storage::Synchronization::ProjectData &projectData) + const Storage::Synchronization::DirectoryInfo &directoryInfo) { + NanotraceHR::Tracer tracer{"qmltypes parser parse"_t, category()}; + QQmlJSTypeDescriptionReader reader({}, sourceContent); QList<QQmlJSExportedScope> components; QStringList dependencies; @@ -467,8 +536,8 @@ void QmlTypesParser::parse(const QString &sourceContent, auto componentNameWithoutNamespaces = createComponentNameWithoutNamespaces(components); - addImports(imports, projectData.sourceId, dependencies, m_storage, projectData.moduleId); - addTypes(types, projectData, components, m_storage, componentNameWithoutNamespaces); + addImports(imports, directoryInfo.sourceId, dependencies, m_storage, directoryInfo.moduleId); + addTypes(types, directoryInfo, components, m_storage, componentNameWithoutNamespaces); } #else @@ -476,7 +545,7 @@ void QmlTypesParser::parse(const QString &sourceContent, void QmlTypesParser::parse([[maybe_unused]] const QString &sourceContent, [[maybe_unused]] Storage::Imports &imports, [[maybe_unused]] Storage::Synchronization::Types &types, - [[maybe_unused]] const Storage::Synchronization::ProjectData &projectData) + [[maybe_unused]] const Storage::Synchronization::DirectoryInfo &directoryInfo) {} #endif diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h index 7c41925f30..c73a429f91 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<typename Database> -class ProjectStorage; - template<typename ProjectStorage, typename Mutex> class SourcePathCache; class QmlTypesParser final : public QmlTypesParserInterface { public: - using ProjectStorage = QmlDesigner::ProjectStorage<Sqlite::Database>; - + using ProjectStorage = QmlDesigner::ProjectStorage; #ifdef QDS_BUILD_QMLPARSER QmlTypesParser(ProjectStorage &storage) : m_storage{storage} @@ -34,7 +31,7 @@ public: void parse(const QString &sourceContent, Storage::Imports &imports, Storage::Synchronization::Types &types, - const Storage::Synchronization::ProjectData &projectData) override; + const Storage::Synchronization::DirectoryInfo &directoryInfo) override; private: #ifdef QDS_BUILD_QMLPARSER diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparserinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparserinterface.h index cdc7cd54d7..c0880cf5c6 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparserinterface.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparserinterface.h @@ -15,7 +15,7 @@ public: virtual void parse(const QString &sourceContent, Storage::Imports &imports, Storage::Synchronization::Types &types, - const Storage::Synchronization::ProjectData &projectData) + const Storage::Synchronization::DirectoryInfo &directoryInfo) = 0; protected: diff --git a/src/plugins/qmldesigner/designercore/projectstorage/sourcepath.h b/src/plugins/qmldesigner/designercore/projectstorage/sourcepath.h index 837e58d48a..fa550a4d52 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/sourcepath.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/sourcepath.h @@ -7,6 +7,8 @@ #include <utils/smallstring.h> +#include <QVarLengthArray> + namespace QmlDesigner { class SourcePath : public Utils::PathString @@ -117,10 +119,17 @@ public: std::ptrdiff_t slashIndex() const { return m_slashIndex; } + template<typename String> + friend void convertToString(String &string, const SourcePath &path) + { + convertToString(string, path.toStringView()); + } + private: std::ptrdiff_t m_slashIndex = -1; }; using SourcePaths = std::vector<SourcePath>; - +template<std::size_t size> +using SmallSourcePaths = QVarLengthArray<SourcePath, size>; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/sourcepathcachetypes.h b/src/plugins/qmldesigner/designercore/projectstorage/sourcepathcachetypes.h index 5feaf30d00..1ef8ba7f21 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<SourceNameAndSourceContextId>; + } // namespace QmlDesigner::Cache diff --git a/src/plugins/qmldesigner/designercore/projectstorage/storagecache.h b/src/plugins/qmldesigner/designercore/projectstorage/storagecache.h index 85c6147d2c..32ecb1c3f7 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/storagecache.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/storagecache.h @@ -313,7 +313,7 @@ private: return entries.end(); } - auto value = *found; + const auto &value = *found; if (value == view) { return found; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp b/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp index b829e9db36..71eba94966 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,8 +178,15 @@ TypeAnnotationReader::ParserSate TypeAnnotationReader::readDocument(const QStrin TypeAnnotationReader::ParserSate TypeAnnotationReader::readMetaInfoRootElement(const QString &name) { if (name == typeElementName) { - m_typeAnnotations.emplace_back(m_sourceId); + auto &annotation = m_typeAnnotations.emplace_back(m_sourceId, m_directorySourceId); + annotation.traits.canBeDroppedInFormEditor = FlagIs::True; + annotation.traits.canBeDroppedInNavigator = FlagIs::True; + annotation.traits.isMovable = FlagIs::True; + annotation.traits.isResizable = FlagIs::True; + annotation.traits.hasFormEditorItem = FlagIs::True; + annotation.traits.visibleInLibrary = FlagIs::True; m_itemLibraryEntries = json::array(); + return ParsingType; } else { addErrorInvalidType(name); @@ -258,7 +265,8 @@ void TypeAnnotationReader::readTypeProperty(QStringView name, const QVariant &va auto [moduleName, typeName] = decomposeTypePath(fullTypeName); m_typeAnnotations.back().typeName = typeName; - m_typeAnnotations.back().moduleId = m_projectStorage.moduleId(moduleName); + m_typeAnnotations.back().moduleId = m_projectStorage.moduleId(moduleName, + ModuleKind::QmlLibrary); } else if (name == "icon"_L1) { m_typeAnnotations.back().iconPath = absoluteFilePathForDocument(value.toString()); @@ -277,7 +285,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) { @@ -304,7 +312,7 @@ QString deEscape(const QString &value) QVariant deEscapeVariant(const QVariant &value) { - if (value.typeId() == QVariant::String) + if (value.typeId() == QMetaType::QString) return deEscape(value.toString()); return value; } @@ -427,8 +435,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; @@ -459,9 +467,9 @@ using json = nlohmann::json; out = json::array({}); out.push_back(property.name); out.push_back(property.type); - if (property.value.type() == QVariant::String) + if (property.value.typeId() == QMetaType::QString) out.push_back(Utils::PathString{property.value.toString()}); - else if (property.value.type() == QVariant::Int || property.value.type() == QVariant::LongLong) + else if (property.value.typeId() == QMetaType::Int || property.value.typeId() == QMetaType::LongLong) out.push_back(property.value.toLongLong()); else out.push_back(property.value.toDouble()); diff --git a/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.h b/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.h index 9332d5bed9..a320493ee2 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/designercore/tracing/qmldesignertracing.cpp b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp index d49c6156a6..cbe7b0ec38 100644 --- a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp +++ b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp @@ -3,40 +3,48 @@ #include "qmldesignertracing.h" +#include <sqlitebasestatement.h> + namespace QmlDesigner { + +using namespace NanotraceHR::Literals; + namespace Tracing { namespace { using TraceFile = NanotraceHR::TraceFile<tracingStatus()>; -TraceFile &traceFile() +auto &traceFile() { - static TraceFile traceFile{"qml_designer.json"}; - return traceFile; + if constexpr (std::is_same_v<Sqlite::TraceFile, TraceFile>) { + return Sqlite::traceFile(); + } else { + static TraceFile traceFile{"tracing.json"}; + return traceFile; + } } } // namespace EventQueue &eventQueue() { - thread_local NanotraceHR::EventQueueData<NanotraceHR::StringViewTraceEvent, 10000, tracingStatus()> - stringViewEventQueueData(traceFile()); + thread_local NanotraceHR::EventQueue<NanotraceHR::StringViewTraceEvent, tracingStatus()> + stringViewEventQueue(traceFile()); - return stringViewEventQueueData; + return stringViewEventQueue; } EventQueueWithStringArguments &eventQueueWithStringArguments() { - thread_local NanotraceHR:: - EventQueueData<NanotraceHR::StringViewWithStringArgumentsTraceEvent, 1000, tracingStatus()> - stringViewWithStringArgumentsEventQueueData(traceFile()); + thread_local NanotraceHR::EventQueue<NanotraceHR::StringViewWithStringArgumentsTraceEvent, tracingStatus()> + stringViewWithStringArgumentsEventQueue(traceFile()); - return stringViewWithStringArgumentsEventQueueData; + return stringViewWithStringArgumentsEventQueue; } StringEventQueue &stringEventQueue() { - thread_local NanotraceHR::EventQueueData<NanotraceHR::StringTraceEvent, 1000, tracingStatus()> eventQueue( + thread_local NanotraceHR::EventQueue<NanotraceHR::StringTraceEvent, tracingStatus()> eventQueue( traceFile()); return eventQueue; @@ -46,7 +54,6 @@ StringEventQueue &stringEventQueue() namespace ModelTracing { namespace { -using namespace NanotraceHR::Literals; thread_local Category category_{"model"_t, Tracing::stringEventQueue(), category}; @@ -58,4 +65,36 @@ Category &category() } } // namespace ModelTracing + +namespace ProjectStorageTracing { + +Category &projectStorageCategory() +{ + thread_local Category category{"project storage"_t, + Tracing::eventQueueWithStringArguments(), + projectStorageCategory}; + + return category; +} + +Category &projectStorageUpdaterCategory() +{ + thread_local Category category{"project storage updater"_t, + Tracing::eventQueueWithStringArguments(), + projectStorageCategory}; + + return category; +} + +} // namespace ProjectStorageTracing + +namespace MetaInfoTracing { +Category &category() +{ + thread_local Category category_{"meta info"_t, Tracing::eventQueueWithStringArguments(), category}; + + return category_; +} +} // namespace MetaInfoTracing + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h index 31058260d6..899ceb6cd2 100644 --- a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h +++ b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h @@ -44,4 +44,38 @@ 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 +} + +using Category = NanotraceHR::StringViewWithStringArgumentsCategory<projectStorageTracingStatus()>; + +[[gnu::pure]] Category &projectStorageCategory(); + +[[gnu::pure]] Category &projectStorageUpdaterCategory(); + +} // namespace ProjectStorageTracing + +namespace MetaInfoTracing { +constexpr NanotraceHR::Tracing tracingStatus() +{ +#ifdef ENABLE_METAINFO_TRACING + return NanotraceHR::Tracing::IsEnabled; +#else + return NanotraceHR::Tracing::IsDisabled; +#endif +} + +using Category = NanotraceHR::StringViewWithStringArgumentsCategory<tracingStatus()>; + +[[gnu::pure]] Category &category(); + +} // namespace MetaInfoTracing } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/uniquename.cpp b/src/plugins/qmldesigner/designercore/uniquename.cpp new file mode 100644 index 0000000000..d7506164db --- /dev/null +++ b/src/plugins/qmldesigner/designercore/uniquename.cpp @@ -0,0 +1,165 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "uniquename.h" + +#include <utils/span.h> + +#include <QFileInfo> +#include <QRegularExpression> + +namespace QmlDesigner::UniqueName { + +using namespace Qt::Literals; + +constexpr QLatin1StringView keywords[] { + "anchors"_L1, "as"_L1, "baseState"_L1, + "border"_L1, "bottom"_L1, "break"_L1, + "case"_L1, "catch"_L1, "clip"_L1, + "color"_L1, "continue"_L1, "data"_L1, + "debugger"_L1, "default"_L1, "delete"_L1, + "do"_L1, "else"_L1, "enabled"_L1, + "finally"_L1, "flow"_L1, "focus"_L1, + "font"_L1, "for"_L1, "function"_L1, + "height"_L1, "if"_L1, "import"_L1, + "in"_L1, "instanceof"_L1, "item"_L1, + "layer"_L1, "left"_L1, "margin"_L1, + "new"_L1, "opacity"_L1, "padding"_L1, + "parent"_L1, "print"_L1, "rect"_L1, + "return"_L1, "right"_L1, "scale"_L1, + "shaderInfo"_L1, "source"_L1, "sprite"_L1, + "spriteSequence"_L1, "state"_L1, "switch"_L1, + "text"_L1, "this"_L1, "throw"_L1, + "top"_L1, "try"_L1, "typeof"_L1, + "var"_L1, "visible"_L1, "void"_L1, + "while"_L1, "with"_L1, "x"_L1, + "y"_L1 +}; + +namespace { + +QString toCamelCase(const QString &input) +{ + QString result = input.at(0).toLower(); + bool capitalizeNext = false; + + for (const QChar &c : Utils::span{input}.subspan(1)) { + bool isValidChar = c.isLetterOrNumber() || c == '_'; + if (isValidChar) + result += capitalizeNext ? c.toUpper() : c; + + capitalizeNext = !isValidChar; + } + + return result; +} + +} // namespace + +/** + * @brief Generates a unique name based on the provided name. + * + * This method iteratively generates a name by appending suffixes until a unique name is found. + * The uniqueness of the generated name is determined by the provided predicate function. + * + * @param name The original name to be made unique. + * @param predicate A function that checks if a name exists. Returns true if the name exists, + * false if name is unique. + * @return A unique name derived from the provided name. + */ +QString generate(const QString &name, std::function<bool(const QString &)> predicate) +{ + if (!predicate(name)) + return name; + + // match prefix and number (including zero padding) parts + static QRegularExpression rgx("(\\D*?)(\\d+)$"); + QRegularExpressionMatch match = rgx.match(name); + + QString prefix; + int number = 0; + int padding = 0; + + if (match.hasMatch()) { + // Split the name into prefix and number + prefix = match.captured(1); + QString numberStr = match.captured(2); + number = numberStr.toInt(); + padding = numberStr.size(); + } else { + prefix = name; + } + + QString nameTemplate = "%1%2"; + QString newName; + do { + newName = nameTemplate.arg(prefix).arg(++number, padding, 10, QChar('0')); + } while (predicate(newName)); + + return newName; +} + +/** + * @brief Generates a unique path based on the provided path. If the path belongs to a file, the + * filename or if it's a directory, the directory name will be adjusted to ensure uniqueness. + * + * This method appends a numerical suffix (or increment it if it exists) to the filename or + * directory name if necessary to make it unique. + * + * @param path The original path to be made unique. + * @return A unique path derived from the provided path. + */ +QString generatePath(const QString &path) +{ + // Remove the trailing slash if it exists (otherwise QFileInfo::path() returns empty) + QString adjustedPath = path; + if (adjustedPath.endsWith('/')) + adjustedPath.chop(1); + + QFileInfo fileInfo = QFileInfo(adjustedPath); + QString baseName = fileInfo.baseName(); + QString suffix = fileInfo.completeSuffix(); + if (!suffix.isEmpty()) + suffix.prepend('.'); + + QString parentDir = fileInfo.path(); + QString pathTemplate = parentDir + "/%1" + suffix; + + QString uniqueBaseName = UniqueName::generate(baseName, [&] (const QString &currName) { + return QFileInfo::exists(pathTemplate.arg(currName)); + }); + + return pathTemplate.arg(uniqueBaseName); +} + +/** + * @brief Generates a unique ID based on the provided id + * + * This works similar to get() with additional restrictions: + * - Removes non-Latin1 characters + * - Removes spaces + * - Ensures the first letter is lowercase + * - Converts spaces to camel case + * - Prepends an underscore if id starts with a number or is a reserved word + * + * @param id The original id to be made unique. + * @return A unique Id (when predicate() returns false) + */ +QString generateId(const QString &id, std::function<bool(const QString &)> predicate) +{ + // remove non word (non A-Z, a-z, 0-9) or space characters + QString newId = id.trimmed(); + + newId = toCamelCase(newId); + + // prepend _ if starts with a digit or invalid id (such as reserved words) + if (newId.at(0).isDigit() || std::binary_search(std::begin(keywords), std::end(keywords), newId)) + newId.prepend('_'); + + if (!predicate) + return newId; + + return UniqueName::generate(newId, predicate); +} + +} // namespace QmlDesigner::UniqueName diff --git a/src/plugins/qmldesigner/designercore/uniquename.h b/src/plugins/qmldesigner/designercore/uniquename.h new file mode 100644 index 0000000000..85927c4514 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/uniquename.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 <qmldesignercorelib_exports.h> + +#include <QString> + +namespace QmlDesigner::UniqueName { + +QString generate(const QString &name, std::function<bool(const QString &)> predicate); +QString generatePath(const QString &path); +QMLDESIGNERCORE_EXPORT QString generateId(const QString &id, + std::function<bool(const QString &)> predicate = {}); + +} // namespace QmlDesigner::UniqueName diff --git a/src/plugins/qmldesigner/designmodecontext.cpp b/src/plugins/qmldesigner/designmodecontext.cpp index 43fb0d9d75..796261935a 100644 --- a/src/plugins/qmldesigner/designmodecontext.cpp +++ b/src/plugins/qmldesigner/designmodecontext.cpp @@ -3,7 +3,6 @@ #include "designmodecontext.h" #include "assetslibrarywidget.h" -#include "collectionwidget.h" #include "designmodewidget.h" #include "edit3dwidget.h" #include "formeditorwidget.h" @@ -98,15 +97,4 @@ void TextEditorContext::contextHelp(const HelpCallback &callback) const qobject_cast<TextEditorWidget *>(m_widget)->contextHelp(callback); } -CollectionEditorContext::CollectionEditorContext(QWidget *widget) - : IContext(widget) -{ - setWidget(widget); - setContext(Core::Context(Constants::C_QMLCOLLECTIONEDITOR, Constants::C_QT_QUICK_TOOLS_MENU)); -} - -void CollectionEditorContext::contextHelp(const HelpCallback &callback) const -{ - qobject_cast<CollectionWidget *>(m_widget)->contextHelp(callback); -} } // namespace QmlDesigner::Internal diff --git a/src/plugins/qmldesigner/designmodecontext.h b/src/plugins/qmldesigner/designmodecontext.h index 12f0113d97..1d146deb7d 100644 --- a/src/plugins/qmldesigner/designmodecontext.h +++ b/src/plugins/qmldesigner/designmodecontext.h @@ -73,14 +73,5 @@ public: TextEditorContext(QWidget *widget); void contextHelp(const Core::IContext::HelpCallback &callback) const override; }; - -class CollectionEditorContext : public Core::IContext -{ - Q_OBJECT - -public: - CollectionEditorContext(QWidget *widget); - void contextHelp(const Core::IContext::HelpCallback &callback) const override; -}; } // namespace Internal } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designmodewidget.h b/src/plugins/qmldesigner/designmodewidget.h index 464994b7e3..881335da75 100644 --- a/src/plugins/qmldesigner/designmodewidget.h +++ b/src/plugins/qmldesigner/designmodewidget.h @@ -11,7 +11,6 @@ #include <QWidget> #include <QMainWindow> -#include <QScopedPointer> #include <advanceddockingsystem/dockmanager.h> #include <annotationeditor/globalannotationeditor.h> diff --git a/src/plugins/qmldesigner/documentmanager.cpp b/src/plugins/qmldesigner/documentmanager.cpp index fcaac762ba..92d80680a9 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); } @@ -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<DesignDocument>( - m_projectManager.projectStorageDependencies(), m_externalDependencies); + editor->document()->filePath().toString(), + m_projectManager.projectStorageDependencies(), + m_externalDependencies); m_currentDesignDocument = inserted.get(); m_currentDesignDocument->setEditor(editor); } else { @@ -266,6 +268,11 @@ void DocumentManager::resetPossibleImports() } } +const GeneratedComponentUtils &DocumentManager::generatedComponentUtils() const +{ + return m_generatedComponentUtils; +} + bool DocumentManager::goIntoComponent(const ModelNode &modelNode) { QImage image = QmlDesignerPlugin::instance()->viewManager().takeFormEditorScreenshot(); @@ -537,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; } diff --git a/src/plugins/qmldesigner/documentmanager.h b/src/plugins/qmldesigner/documentmanager.h index 090630e5fe..6447694339 100644 --- a/src/plugins/qmldesigner/documentmanager.h +++ b/src/plugins/qmldesigner/documentmanager.h @@ -5,6 +5,8 @@ #include "qmldesigner_global.h" +#include <generatedcomponentutils.h> + #include <QObject> #include <QList> #include <QLoggingCategory> @@ -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<DesignDocument> m_currentDesignDocument; QmlDesignerProjectManager &m_projectManager; ExternalDependenciesInterface &m_externalDependencies; + GeneratedComponentUtils m_generatedComponentUtils; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/documentwarningwidget.cpp b/src/plugins/qmldesigner/documentwarningwidget.cpp index 9fb2b87635..a1cc12d825 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")) { @@ -93,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()); @@ -150,8 +149,8 @@ bool DocumentWarningWidget::eventFilter(QObject *object, QEvent *event) void DocumentWarningWidget::showEvent(QShowEvent *event) { - const QColor backgroundColor = Utils::creatorTheme()->color(Utils::Theme::DScontrolBackground); - const QColor outlineColor = Utils::creatorTheme()->color(Utils::Theme::DScontrolOutline); + const QColor backgroundColor = Utils::creatorColor(Utils::Theme::DScontrolBackground); + const QColor outlineColor = Utils::creatorColor(Utils::Theme::DScontrolOutline); QPalette pal = palette(); pal.setColor(QPalette::ToolTipBase, backgroundColor); pal.setColor(QPalette::ToolTipText, outlineColor); diff --git a/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp b/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp index 6e8a2b6024..7819c6b49d 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"); diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index db63d894fe..28f3ed6097 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -19,7 +19,6 @@ inline constexpr char C_QMLNAVIGATOR[] = "QmlDesigner::Navigator"; inline constexpr char C_QMLTEXTEDITOR[] = "QmlDesigner::TextEditor"; inline constexpr char C_QMLMATERIALBROWSER[] = "QmlDesigner::MaterialBrowser"; inline constexpr char C_QMLASSETSLIBRARY[] = "QmlDesigner::AssetsLibrary"; -inline constexpr char C_QMLCOLLECTIONEDITOR[] = "QmlDesigner::CollectionEditor"; // Special context for preview menu, shared b/w designer and text editor inline constexpr char C_QT_QUICK_TOOLS_MENU[] = "QmlDesigner::ToolsMenu"; @@ -51,6 +50,7 @@ inline constexpr char EDIT3D_EDIT_CAMERA[] = "QmlDesigner.Editor3D.EditCameraTog inline constexpr char EDIT3D_ORIENTATION[] = "QmlDesigner.Editor3D.OrientationToggle"; inline constexpr char EDIT3D_EDIT_LIGHT[] = "QmlDesigner.Editor3D.EditLightToggle"; inline constexpr char EDIT3D_EDIT_SHOW_GRID[] = "QmlDesigner.Editor3D.ToggleGrid"; +inline constexpr char EDIT3D_EDIT_SHOW_LOOKAT[] = "QmlDesigner.Editor3D.ToggleLookAt"; inline constexpr char EDIT3D_EDIT_SELECT_BACKGROUND_COLOR[] = "QmlDesigner.Editor3D.SelectBackgroundColor"; inline constexpr char EDIT3D_EDIT_SELECT_GRID_COLOR[] = "QmlDesigner.Editor3D.SelectGridColor"; @@ -78,15 +78,28 @@ 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 BUNDLE_JSON_FILENAME[] = "bundle.json"; +inline constexpr char COMPONENT_BUNDLES_TYPE[] = "Bundles"; +inline constexpr char COMPONENT_BUNDLES_MATERIAL_BUNDLE_TYPE[] = "Materials"; +inline constexpr char COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE[] = "Effects"; +inline constexpr char COMPONENT_BUNDLES_USER_MATERIAL_BUNDLE_TYPE[] = "UserMaterials"; +inline constexpr char COMPONENT_BUNDLES_USER_EFFECT_BUNDLE_TYPE[] = "UserEffects"; +inline constexpr char COMPONENT_BUNDLES_USER_3D_BUNDLE_TYPE[] = "User3D"; +inline constexpr char GENERATED_COMPONENTS_FOLDER[] = "Generated"; inline constexpr char COMPONENT_BUNDLES_ASSET_REF_FILE[] = "_asset_ref.json"; -inline constexpr char QUICK_3D_ASSETS_FOLDER[] = "/Quick3DAssets"; +inline constexpr char OLD_QUICK_3D_ASSETS_FOLDER[] = "Quick3DAssets"; +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"; 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 OLD_COMPONENT_BUNDLES_TYPE[] = "ComponentBundles"; +inline constexpr char OLD_COMPONENT_BUNDLES_MATERIAL_BUNDLE_TYPE[] = "MaterialBundle"; +inline constexpr char OLD_COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE[] = "EffectBundle"; +inline constexpr char COMPOSED_EFFECTS_TYPE[] = "Effects"; inline constexpr char MATERIAL_LIB_ID[] = "__materialLibrary__"; inline constexpr char MIME_TYPE_ITEM_LIBRARY_INFO[] @@ -94,7 +107,7 @@ inline constexpr char MIME_TYPE_ITEM_LIBRARY_INFO[] inline constexpr char MIME_TYPE_ASSETS[] = "application/vnd.qtdesignstudio.assets"; inline constexpr char MIME_TYPE_MATERIAL[] = "application/vnd.qtdesignstudio.material"; inline constexpr char MIME_TYPE_TEXTURE[] = "application/vnd.qtdesignstudio.texture"; -inline constexpr char MIME_TYPE_BUNDLE_EFFECT[] = "application/vnd.qtdesignstudio.bundleeffect"; +inline constexpr char MIME_TYPE_BUNDLE_ITEM[] = "application/vnd.qtdesignstudio.bundleitem"; inline constexpr char MIME_TYPE_BUNDLE_MATERIAL[] = "application/vnd.qtdesignstudio.bundlematerial"; inline constexpr char MIME_TYPE_BUNDLE_TEXTURE[] = "application/vnd.qtdesignstudio.bundletexture"; inline constexpr char MIME_TYPE_ASSET_IMAGE[] = "application/vnd.qtdesignstudio.asset.image"; @@ -174,7 +187,6 @@ inline constexpr char OBJECT_NAME_EFFECT_COMPOSER[] = "QQuickWidgetEffectCompose inline constexpr char OBJECT_NAME_MATERIAL_BROWSER[] = "QQuickWidgetMaterialBrowser"; inline constexpr char OBJECT_NAME_MATERIAL_EDITOR[] = "QQuickWidgetMaterialEditor"; inline constexpr char OBJECT_NAME_PROPERTY_EDITOR[] = "QQuickWidgetPropertyEditor"; -inline constexpr char OBJECT_NAME_COLLECTION_EDITOR[] = "QQuickWidgetQDSCollectionEditor"; inline constexpr char OBJECT_NAME_STATES_EDITOR[] = "QQuickWidgetStatesEditor"; inline constexpr char OBJECT_NAME_TEXTURE_EDITOR[] = "QQuickWidgetTextureEditor"; inline constexpr char OBJECT_NAME_TOP_TOOLBAR[] = "QQuickWidgetTopToolbar"; diff --git a/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp b/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp index 321d95197f..97b2b46e7d 100644 --- a/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp +++ b/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp @@ -56,6 +56,11 @@ QUrl ExternalDependencies::projectUrl() const return {}; } +QString ExternalDependencies::projectName() const +{ + return QmlDesignerPlugin::instance()->documentManager().currentProjectName(); +} + QString ExternalDependencies::currentProjectDirPath() const { return QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath().toString(); diff --git a/src/plugins/qmldesigner/qmldesignerexternaldependencies.h b/src/plugins/qmldesigner/qmldesignerexternaldependencies.h index b4908c2383..6a49e4b551 100644 --- a/src/plugins/qmldesigner/qmldesignerexternaldependencies.h +++ b/src/plugins/qmldesigner/qmldesignerexternaldependencies.h @@ -21,6 +21,7 @@ public: QString qmlPuppetFallbackDirectory() const override; QString defaultPuppetToplevelBuildDirectory() const override; QUrl projectUrl() const override; + QString projectName() const override; QString currentProjectDirPath() const override; QUrl currentResourcePath() const override; void parseItemLibraryDescriptions() override; diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index 601cf96681..9b6fb50425 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -4,7 +4,6 @@ #include "qmldesignerplugin.h" #include "qmldesignertr.h" -#include "collectioneditor/collectionview.h" #include "coreplugin/iwizardfactory.h" #include "designmodecontext.h" #include "designmodewidget.h" @@ -298,7 +297,6 @@ bool QmlDesignerPlugin::initialize(const QStringList & /*arguments*/, QString *e //TODO Move registering those types out of the property editor, since they are used also in the states editor Quick2PropertyEditorView::registerQmlTypes(); - CollectionView::registerDeclarativeType(); StudioQuickWidget::registerDeclarativeType(); QmlDesignerBase::WindowManager::registerDeclarativeType(); @@ -392,7 +390,6 @@ void QmlDesignerPlugin::integrateIntoQtCreator(QWidget *modeWidget) Core::Context qmlDesignerNavigatorContext(Constants::C_QMLNAVIGATOR); Core::Context qmlDesignerMaterialBrowserContext(Constants::C_QMLMATERIALBROWSER); Core::Context qmlDesignerAssetsLibraryContext(Constants::C_QMLASSETSLIBRARY); - Core::Context qmlDesignerCollectionEditorContext(Constants::C_QMLCOLLECTIONEDITOR); context->context().add(qmlDesignerMainContext); context->context().add(qmlDesignerFormEditorContext); @@ -400,7 +397,6 @@ void QmlDesignerPlugin::integrateIntoQtCreator(QWidget *modeWidget) context->context().add(qmlDesignerNavigatorContext); context->context().add(qmlDesignerMaterialBrowserContext); context->context().add(qmlDesignerAssetsLibraryContext); - context->context().add(qmlDesignerCollectionEditorContext); context->context().add(ProjectExplorer::Constants::QMLJS_LANGUAGE_ID); d->shortCutManager.registerActions(qmlDesignerMainContext, qmlDesignerFormEditorContext, @@ -625,12 +621,14 @@ void QmlDesignerPlugin::enforceDelayedInitialize() return; // adding default path to item library plugins - const QString postfix = Utils::HostOsInfo::isMacHost() ? QString("/QmlDesigner") - : QString("/qmldesigner"); - const QStringList pluginPaths = Utils::transform(ExtensionSystem::PluginManager::pluginPaths(), - [postfix](const QString &p) { - return QString(p + postfix); - }); + const QString postfix = Utils::HostOsInfo::isMacHost() + ? QString("QmlDesigner") + : QString("qmldesigner"); + const QStringList pluginPaths = + Utils::transform(ExtensionSystem::PluginManager::pluginPaths(), + [postfix](const Utils::FilePath &p) { + return (p / postfix).toFSPathString(); + }); #ifndef QDS_USE_PROJECTSTORAGE MetaInfo::initializeGlobal(pluginPaths, d->externalDependencies); diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp index 7e28849fbb..730a557d12 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp @@ -12,15 +12,16 @@ #include <projectstorage/filesystem.h> #include <projectstorage/nonlockingmutex.h> #include <projectstorage/projectstorage.h> +#include <projectstorage/projectstorageerrornotifier.h> #include <projectstorage/projectstoragepathwatcher.h> #include <projectstorage/projectstorageupdater.h> #include <projectstorage/qmldocumentparser.h> #include <projectstorage/qmltypesparser.h> #include <projectstorage/sourcepathcache.h> -#include <sqlitedatabase.h> #include <qmlprojectmanager/qmlproject.h> #include <qtsupport/baseqtversion.h> #include <qtsupport/qtkitaspect.h> +#include <sqlitedatabase.h> #include <asynchronousexplicitimagecache.h> #include <asynchronousimagecache.h> @@ -41,6 +42,7 @@ #include <QDirIterator> #include <QFileSystemWatcher> +#include <QLibraryInfo> #include <QQmlEngine> using namespace std::chrono; @@ -180,7 +182,8 @@ public: pathCache.sourceId(SourcePath{project->projectDirectory().toString() + "/."}).internalId())} {} Sqlite::Database database; - ProjectStorage<Sqlite::Database> storage{database, database.isInitialized()}; + ProjectStorageErrorNotifier errorNotifier{pathCache}; + ProjectStorage storage{database, errorNotifier, database.isInitialized()}; PathCacheType pathCache{storage}; FileSystem fileSystem{pathCache}; FileStatusCache fileStatusCache{fileSystem}; @@ -237,30 +240,34 @@ QmlDesignerProjectManager::QmlDesignerProjectManager(ExternalDependenciesInterfa , m_externalDependencies{externalDependencies} { auto editorManager = ::Core::EditorManager::instance(); - QObject::connect(editorManager, &::Core::EditorManager::editorOpened, [&](auto *editor) { + QObject::connect(editorManager, &::Core::EditorManager::editorOpened, &dummy, [&](auto *editor) { editorOpened(editor); }); - QObject::connect(editorManager, &::Core::EditorManager::currentEditorChanged, [&](auto *editor) { - currentEditorChanged(editor); - }); - QObject::connect(editorManager, &::Core::EditorManager::editorsClosed, [&](const auto &editors) { - editorsClosed(editors); - }); + QObject::connect(editorManager, + &::Core::EditorManager::currentEditorChanged, + &dummy, + [&](auto *editor) { currentEditorChanged(editor); }); + QObject::connect(editorManager, + &::Core::EditorManager::editorsClosed, + &dummy, + [&](const auto &editors) { editorsClosed(editors); }); auto sessionManager = ::ProjectExplorer::ProjectManager::instance(); QObject::connect(sessionManager, &::ProjectExplorer::ProjectManager::projectAdded, + &dummy, [&](auto *project) { projectAdded(project); }); QObject::connect(sessionManager, &::ProjectExplorer::ProjectManager::aboutToRemoveProject, + &dummy, [&](auto *project) { aboutToRemoveProject(project); }); QObject::connect(sessionManager, &::ProjectExplorer::ProjectManager::projectRemoved, + &dummy, [&](auto *project) { projectRemoved(project); }); - QObject::connect(&m_previewImageCacheData->timer, - &QTimer::timeout, - this, - &QmlDesignerProjectManager::generatePreview); + QObject::connect(&m_previewImageCacheData->timer, &QTimer::timeout, &dummy, [&]() { + generatePreview(); + }); } QmlDesignerProjectManager::~QmlDesignerProjectManager() = default; @@ -281,7 +288,7 @@ AsynchronousImageCache &QmlDesignerProjectManager::asynchronousImageCache() } namespace { -[[maybe_unused]] ProjectStorage<Sqlite::Database> *dummyProjectStorage() +[[maybe_unused]] ProjectStorage *dummyProjectStorage() { return nullptr; } @@ -336,82 +343,36 @@ Utils::FilePath qmlPath(::ProjectExplorer::Target *target) return {}; } -template<typename... Path> -bool skipDirectoriesWith(const QStringView directoryPath, const Path &...paths) -{ - return (directoryPath.contains(paths) || ...); -} - -template<typename... Path> -bool skipDirectoriesEndsWith(const QStringView directoryPath, const Path &...paths) -{ - return (directoryPath.endsWith(paths) || ...); -} - -bool skipPath(const QString &directoryPath) -{ - return skipDirectoriesWith(directoryPath, - u"QtApplicationManager", - u"QtInterfaceFramework", - u"QtOpcUa", - u"Qt3D", - u"Scene2D", - u"Scene3D", - u"QtWayland", - u"Qt5Compat", - u"QtCharts", - u"QtLocation", - u"QtPositioning", - u"MaterialEditor", - u"QtTextToSpeech", - u"QtWebEngine", - u"Qt/labs", - u"QtDataVisualization") - || skipDirectoriesEndsWith(directoryPath, u"designer"); -} - -void collectQmldirPaths(const QString &path, QStringList &qmldirPaths) -{ - QDirIterator dirIterator{path, QDir::Dirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories}; - - while (dirIterator.hasNext()) { - auto directoryPath = dirIterator.next(); - - QString qmldirPath = directoryPath + "/qmldir"; - if (!skipPath(directoryPath) && QFileInfo::exists(qmldirPath)) - qmldirPaths.push_back(directoryPath); - } -} - [[maybe_unused]] void projectQmldirPaths(::ProjectExplorer::Target *target, QStringList &qmldirPaths) { ::QmlProjectManager::QmlBuildSystem *buildSystem = getQmlBuildSystem(target); const Utils::FilePath projectDirectoryPath = buildSystem->canonicalProjectDir(); - const QStringList importPaths = buildSystem->importPaths(); - const QDir projectDirectory(projectDirectoryPath.toString()); - for (const QString &importPath : importPaths) - collectQmldirPaths(importPath, qmldirPaths); + qmldirPaths.push_back(projectDirectoryPath.path()); } [[maybe_unused]] void qtQmldirPaths(::ProjectExplorer::Target *target, QStringList &qmldirPaths) { - if constexpr (useProjectStorage()) - collectQmldirPaths(qmlPath(target).toString(), qmldirPaths); + if constexpr (useProjectStorage()) { + auto qmlRootPath = qmlPath(target).toString(); + qmldirPaths.push_back(qmlRootPath + "/QtQml"); + qmldirPaths.push_back(qmlRootPath + "/QtQuick"); + qmldirPaths.push_back(qmlRootPath + "/QtQuick3D"); + qmldirPaths.push_back(qmlRootPath + "/Qt5Compat"); + } } -[[maybe_unused]] void qtQmldirPathsForLiteDesigner(::ProjectExplorer::Target *target, - QStringList &qmldirPaths) +[[maybe_unused]] void qtQmldirPathsForLiteDesigner(QStringList &qmldirPaths) { if constexpr (useProjectStorage()) { - auto qmlRootPath = qmlPath(target).toString(); - collectQmldirPaths(qmlRootPath + "/QtQml", qmldirPaths); - collectQmldirPaths(qmlRootPath + "/QtQuick", qmldirPaths); + auto qmlRootPath = QLibraryInfo::path(QLibraryInfo::QmlImportsPath); + qmldirPaths.push_back(qmlRootPath + "/QtQml"); + qmldirPaths.push_back(qmlRootPath + "/QtQuick"); } } -QStringList directories(::ProjectExplorer::Target *target) +[[maybe_unused]] QStringList directories(::ProjectExplorer::Target *target) { if (!target) return {}; @@ -419,12 +380,21 @@ 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()); + + return qmldirPaths; +} + +[[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()); @@ -432,7 +402,7 @@ QStringList directories(::ProjectExplorer::Target *target) return qmldirPaths; } -QStringList qmlTypes(::ProjectExplorer::Target *target) +[[maybe_unused]] QStringList qmlTypes(::ProjectExplorer::Target *target) { if (!target) return {}; @@ -440,10 +410,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(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(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()); @@ -461,6 +447,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) @@ -533,12 +524,12 @@ QmlDesignerProjectManager::ImageCacheData *QmlDesignerProjectManager::imageCache m_imageCacheData->nodeInstanceCollector.setTarget(project->activeTarget()); QObject::connect(project, &ProjectExplorer::Project::activeTargetChanged, - this, + &dummy, setTargetInImageCache); } QObject::connect(ProjectExplorer::ProjectManager::instance(), &ProjectExplorer::ProjectManager::startupProjectChanged, - this, + &dummy, [=](ProjectExplorer::Project *project) { setTargetInImageCache(activeTarget(project)); }); @@ -594,9 +585,17 @@ 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(), + {qtCreatorItemLibraryPath()}); + } else { + m_projectData->projectStorageData->updater.update(directories(m_projectData->activeTarget), + qmlTypes(m_projectData->activeTarget), + propertyEditorResourcesPath(), + {qtCreatorItemLibraryPath()}); + } } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.h b/src/plugins/qmldesigner/qmldesignerprojectmanager.h index bd45bf16c9..1e5cec2e3e 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.h +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.h @@ -28,10 +28,8 @@ namespace QmlDesigner { class ExternalDependenciesInterface; -class QmlDesignerProjectManager : public QObject +class QmlDesignerProjectManager { - Q_OBJECT - class QmlDesignerProjectManagerProjectData; class PreviewImageCacheData; class ImageCacheData; @@ -70,5 +68,6 @@ private: std::unique_ptr<PreviewImageCacheData> m_previewImageCacheData; std::unique_ptr<QmlDesignerProjectManagerProjectData> m_projectData; ExternalDependenciesInterface &m_externalDependencies; + QObject dummy; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewplugin.cpp b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewplugin.cpp index d5036c939c..34cd4338ac 100644 --- a/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewplugin.cpp +++ b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewplugin.cpp @@ -165,7 +165,7 @@ void QmlPreviewWidgetPlugin::setLanguageLocale(const QString &locale) QObject *QmlPreviewWidgetPlugin::getPreviewPlugin() { - const QVector<ExtensionSystem::PluginSpec *> &specs = ExtensionSystem::PluginManager::plugins(); + const ExtensionSystem::PluginSpecs &specs = ExtensionSystem::PluginManager::plugins(); const auto pluginIt = std::find_if(specs.cbegin(), specs.cend(), [](const ExtensionSystem::PluginSpec *p) { return p->name() == "QmlPreview"; diff --git a/src/plugins/qmldesigner/qtquickplugin/images/frame-animation-16px.png b/src/plugins/qmldesigner/qtquickplugin/images/frame-animation-16px.png Binary files differnew file mode 100644 index 0000000000..5171fdd79d --- /dev/null +++ b/src/plugins/qmldesigner/qtquickplugin/images/frame-animation-16px.png diff --git a/src/plugins/qmldesigner/qtquickplugin/images/frame-animation-24px.png b/src/plugins/qmldesigner/qtquickplugin/images/frame-animation-24px.png Binary files differnew file mode 100644 index 0000000000..2fba147700 --- /dev/null +++ b/src/plugins/qmldesigner/qtquickplugin/images/frame-animation-24px.png diff --git a/src/plugins/qmldesigner/qtquickplugin/images/frame-animation-24px@2x.png b/src/plugins/qmldesigner/qtquickplugin/images/frame-animation-24px@2x.png Binary files differnew file mode 100644 index 0000000000..170c113bbd --- /dev/null +++ b/src/plugins/qmldesigner/qtquickplugin/images/frame-animation-24px@2x.png diff --git a/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc b/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc index 34838797d4..c2a1ff5929 100644 --- a/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc +++ b/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc @@ -127,5 +127,8 @@ <file>images/extended-view3d-16px.png</file> <file>images/extended-view3d-24px.png</file> <file>images/extended-view3d-24px@2x.png</file> + <file>images/frame-animation-16px.png</file> + <file>images/frame-animation-24px.png</file> + <file>images/frame-animation-24px@2x.png</file> </qresource> </RCC> diff --git a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo index 3f3cdb7910..63d390dded 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" } } @@ -551,6 +549,26 @@ MetaInfo { } Type { + name: "QtQuick.FrameAnimation" + icon: ":/qtquickplugin/images/frame-animation-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeContainer: false + } + + ItemLibraryEntry { + name: "Frame Animation" + category: "d.Qt Quick - Animation" + libraryIcon: ":/qtquickplugin/images/frame-animation-24px.png" + version: "2.0" + toolTip: qsTr("Triggers a handler at every animation frame update.") + } + } + + Type { name: "QtQml.Timer" icon: ":/qtquickplugin/images/timer-16px.png" diff --git a/src/plugins/qmldesigner/settingspage.cpp b/src/plugins/qmldesigner/settingspage.cpp index 344e2700d9..df17f005a2 100644 --- a/src/plugins/qmldesigner/settingspage.cpp +++ b/src/plugins/qmldesigner/settingspage.cpp @@ -39,7 +39,8 @@ namespace Internal { static QStringList puppetModes() { - static QStringList puppetModeList{"", "all", "editormode", "rendermode", "previewmode", "bakelightsmode"}; + static QStringList puppetModeList{"", "all", "editormode", "rendermode", "previewmode", + "bakelightsmode", "import3dmode"}; return puppetModeList; } @@ -135,7 +136,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 +168,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")); diff --git a/src/plugins/qmldesigner/shortcutmanager.cpp b/src/plugins/qmldesigner/shortcutmanager.cpp index 961aa1c952..661ff3f271 100644 --- a/src/plugins/qmldesigner/shortcutmanager.cpp +++ b/src/plugins/qmldesigner/shortcutmanager.cpp @@ -144,7 +144,8 @@ void ShortCutManager::registerActions(const Core::Context &qmlDesignerMainContex Core::ActionContainer *exportMenu = Core::ActionManager::actionContainer( QmlProjectManager::Constants::EXPORT_MENU); - exportMenu->addAction(command, QmlProjectManager::Constants::G_EXPORT_CONVERT); + if (exportMenu) + exportMenu->addAction(command, QmlProjectManager::Constants::G_EXPORT_CONVERT); //Close Editor Core::ActionManager::registerAction(&m_closeCurrentEditorAction, Core::Constants::CLOSE, qmlDesignerMainContext); @@ -231,12 +232,10 @@ void ShortCutManager::registerActions(const Core::Context &qmlDesignerMainContex connect(Core::ICore::instance(), &Core::ICore::contextChanged, this, [&](const Core::Context &context) { isMatBrowserActive = context.contains(Constants::C_QMLMATERIALBROWSER); isAssetsLibraryActive = context.contains(Constants::C_QMLASSETSLIBRARY); - isCollectionEditorActive = context.contains(Constants::C_QMLCOLLECTIONEDITOR); if (!context.contains(Constants::C_QMLFORMEDITOR) && !context.contains(Constants::C_QMLEDITOR3D) && !context.contains(Constants::C_QMLNAVIGATOR)) { - m_deleteAction.setEnabled(isMatBrowserActive || isAssetsLibraryActive - || isCollectionEditorActive); + m_deleteAction.setEnabled(isMatBrowserActive || isAssetsLibraryActive); m_cutAction.setEnabled(false); m_copyAction.setEnabled(false); m_pasteAction.setEnabled(false); @@ -293,8 +292,6 @@ void ShortCutManager::deleteSelected() actionManager.view()->emitCustomNotification("delete_selected_material"); else if (isAssetsLibraryActive) actionManager.view()->emitCustomNotification("delete_selected_assets"); - else if (isCollectionEditorActive) - actionManager.view()->emitCustomNotification("delete_selected_collection"); else if (currentDesignDocument()) currentDesignDocument()->deleteSelected(); } diff --git a/src/plugins/qmldesigner/shortcutmanager.h b/src/plugins/qmldesigner/shortcutmanager.h index 8714bb5fbc..70b019217c 100644 --- a/src/plugins/qmldesigner/shortcutmanager.h +++ b/src/plugins/qmldesigner/shortcutmanager.h @@ -64,7 +64,6 @@ private: bool isMatBrowserActive = false; bool isAssetsLibraryActive = false; - bool isCollectionEditorActive = false; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/utils/asset.cpp b/src/plugins/qmldesigner/utils/asset.cpp index 2984a4d890..ec1e4312e4 100644 --- a/src/plugins/qmldesigner/utils/asset.cpp +++ b/src/plugins/qmldesigner/utils/asset.cpp @@ -1,15 +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 <QImageReader> - #include "asset.h" +#include "hdrimage.h" + +#include <QImageReader> +#include <QPixmap> + 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(); @@ -105,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; @@ -175,6 +192,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 cb09f3a5ee..a5c5899f34 100644 --- a/src/plugins/qmldesigner/utils/asset.h +++ b/src/plugins/qmldesigner/utils/asset.h @@ -3,8 +3,11 @@ #pragma once +#include <QSize> #include <QString> +QT_FORWARD_DECLARE_CLASS(QPixmap) + namespace QmlDesigner { class Asset @@ -37,7 +40,9 @@ public: const QString suffix() const; const QString id() const; + const QString fileName() const; bool hasSuffix() const; + QPixmap pixmap(const QSize &size = {}) const; Type type() const; bool isImage() const; @@ -58,6 +63,7 @@ private: void resolveType(); QString m_filePath; + QString m_fileName; QString m_suffix; Type m_type = Unknown; }; diff --git a/src/plugins/qmldesigner/utils/imageutils.cpp b/src/plugins/qmldesigner/utils/imageutils.cpp index 8fa3131cd3..42df6184b9 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<QSize, qint64> 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<QSize, qint64> 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 a4036614a3..dae64423bb 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<QSize, qint64> imageInfo(const QString &path); + static QString imageInfoString(const QString &path); + static QString imageInfoString(const QSize &dimensions, qint64 sizeInBytes); }; } // namespace QmlDesigner |