diff options
author | Tim Jenssen <[email protected]> | 2023-05-22 19:48:42 +0200 |
---|---|---|
committer | Tim Jenssen <[email protected]> | 2023-05-22 21:13:40 +0200 |
commit | 1b6c0ff56c8d48c5b5bb8044b113cd580f6d5ee3 (patch) | |
tree | 25878f9091506fd282a338a2c5f98b357d47134e /src/plugins/qmldesigner | |
parent | 3dcdbe9069c452e2f0eacb925aa7412e63dc4762 (diff) | |
parent | df7398e2c5f3c1595f32c7484ac1e804d83a01ca (diff) |
Merge remote-tracking branch 'origin/qds/dev'
Conflicts:
coin/instructions/build.yaml
doc/qtcreator/src/projects/creator-only/creator-projects-creating.qdoc
share/qtcreator/qmldesigner/studio_templates/projects/common/CMakeLists.main.txt.tpl
src/libs/CMakeLists.txt
src/libs/advanceddockingsystem/dockmanager.cpp
src/libs/advanceddockingsystem/workspacedialog.cpp
src/plugins/coreplugin/manhattanstyle.cpp
src/plugins/qmldesigner/CMakeLists.txt
src/plugins/qmldesigner/components/formeditor/toolbox.cpp
src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp
src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp
src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h
src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp
src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp
src/plugins/qmldesignerbase/qmldesignerbaseplugin.h
src/plugins/qmlprojectmanager/qmlproject.cpp
src/tools/qml2puppet/CMakeLists.txt
tests/unit/unittest/CMakeLists.txt
Change-Id: I2c5f18c4fca49471d02713ce5859032232cf7756
Diffstat (limited to 'src/plugins/qmldesigner')
168 files changed, 4155 insertions, 1943 deletions
diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 74fcd863302..fc160cdf528 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -1,16 +1,5 @@ #only if the plugin is requested by qtc_plugin_enabled continue if not stop as early as possible -add_qtc_plugin(QmlDesigner - PLUGIN_RECOMMENDS QmlPreview - CONDITION Qt6_VERSION VERSION_GREATER_EQUAL 6.2.0 AND TARGET Qt::QuickWidgets AND TARGET Qt::Svg - PROPERTIES COMPILE_WARNING_AS_ERROR ON - PLUGIN_DEPENDS - Core ProjectExplorer QmlDesignerBase QmlJSEditor QmakeProjectManager QmlProjectManager - QtSupport -) -qtc_plugin_enabled(_qmlDesignerEnabled QmlDesigner) -if (NOT _qmlDesignerEnabled) - return() -endif() + find_package(Qt6 COMPONENTS QmlDomPrivate QmlCompilerPrivate) @@ -19,13 +8,10 @@ if (APPLE) set(QmlDesignerPluginInstallPrefix "${IDE_PLUGIN_PATH}/QmlDesigner") endif() - -env_with_default("QDS_USE_PROJECTSTORAGE" ENV_QDS_USE_PROJECTSTORAGE OFF) -option(USE_PROJECTSTORAGE "Use ProjectStorage" ${ENV_QDS_USE_PROJECTSTORAGE}) -add_feature_info("ProjectStorage" ${USE_PROJECTSTORAGE} "") +add_compile_options("$<$<COMPILE_LANG_AND_ID:CXX,Clang>:-Wno-error=maybe-uninitialized>") +add_compile_options("$<$<COMPILE_LANG_AND_ID:CXX,GNU>:-Wno-error=maybe-uninitialized>") add_qtc_library(QmlDesignerUtils STATIC - PROPERTIES COMPILE_WARNING_AS_ERROR ON DEPENDS Qt::Gui Utils Qt::QmlPrivate DEFINES QMLDESIGNERUTILS_LIBRARY @@ -44,8 +30,15 @@ add_qtc_library(QmlDesignerUtils STATIC qmldesignerutils_global.h ) +extend_qtc_library(QmlDesignerUtils + CONDITION NOT DISABLE_COMPILE_WARNING_AS_ERROR + PROPERTIES COMPILE_WARNING_AS_ERROR ON +) + add_qtc_library(QmlDesignerCore STATIC + CONDITION Qt6_VERSION VERSION_GREATER_EQUAL 6.4.3 AND TARGET Qt6::QmlPrivate AND TARGET Qt6::QmlDomPrivate AND TARGET Qt6::QmlCompilerPrivate EXCLUDE_FROM_INSTALL + PROPERTIES SKIP_AUTOUIC ON DEPENDS Threads::Threads Qt::CorePrivate @@ -53,6 +46,9 @@ add_qtc_library(QmlDesignerCore STATIC Utils Qt::Widgets Qt::Qml + Qt::QmlPrivate + Qt6::QmlDomPrivate + Qt6::QmlCompilerPrivate Core ProjectExplorer QmakeProjectManager @@ -79,25 +75,27 @@ add_qtc_library(QmlDesignerCore STATIC rewritertransaction.h ) -extend_qtc_library(QmlDesignerCore - CONDITION TARGET Qt6::QmlDomPrivate AND TARGET Qt6::QmlCompilerPrivate AND Qt6_VERSION VERSION_GREATER_EQUAL 6.5.0 - DEPENDS Qt6::QmlDomPrivate Qt6::QmlCompilerPrivate - DEFINES QDS_HAS_QMLDOM -) +if(TARGET QmlDesignerCore) + env_with_default("QDS_USE_PROJECTSTORAGE" ENV_QDS_USE_PROJECTSTORAGE OFF) + option(USE_PROJECTSTORAGE "Use ProjectStorage" ${ENV_QDS_USE_PROJECTSTORAGE}) + add_feature_info("ProjectStorage" ${USE_PROJECTSTORAGE} "") +endif() + extend_qtc_library(QmlDesignerCore - CONDITION UNIX AND NOT APPLE - PUBLIC_DEPENDS rt + CONDITION NOT DISABLE_COMPILE_WARNING_AS_ERROR + PROPERTIES COMPILE_WARNING_AS_ERROR ON ) extend_qtc_library(QmlDesignerCore - INCLUDES ${CMAKE_CURRENT_BINARY_DIR} - SOURCES - ${UI_SOURCES} - ${UI_FILES} + CONDITION Qt6_VERSION VERSION_GREATER_EQUAL 6.5.0 AND Qt6_VERSION VERSION_LESS 6.6.0 + PUBLIC_DEFINES QDS_BUILD_QMLPARSER +) +extend_qtc_library(QmlDesignerCore + CONDITION UNIX AND NOT APPLE + PUBLIC_DEPENDS rt ) -set_source_files_properties(${UI_FILES} PROPERTIES SKIP_AUTOUIC ON) extend_qtc_library(QmlDesignerCore INCLUDES ${CMAKE_CURRENT_LIST_DIR}/designercore/exceptions @@ -221,6 +219,7 @@ extend_qtc_library(QmlDesignerCore metainfo.h metainforeader.h model.h + modelfwd.h modelmerger.h modelnode.h modelnodepositionstorage.h @@ -241,6 +240,7 @@ extend_qtc_library(QmlDesignerCore qmlchangeset.h qmlconnections.h qmldesignercorelib_global.h + qmldesignercorelib_exports.h qmlitemnode.h qmlmodelnodefacade.h qmlobjectnode.h @@ -262,6 +262,7 @@ extend_qtc_library(QmlDesignerCore INCLUDES ${CMAKE_CURRENT_LIST_DIR}/designercore/metainfo SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/designercore/metainfo + DEFINES SHARE_QML_PATH="${CMAKE_CURRENT_SOURCE_DIR}/../../../share/qtcreator/qmldesigner" SOURCES itemlibraryinfo.cpp metainfo.cpp @@ -326,8 +327,11 @@ extend_qtc_library(QmlDesignerCore modelnodepositionrecalculator.cpp modelnodepositionrecalculator.h modelnodepositionstorage.cpp + modelresourcemanagementinterface.h modeltotextmerger.cpp modeltotextmerger.h + modelutils.cpp + modelutils.h nodeabstractproperty.cpp nodelistproperty.cpp nodeproperty.cpp @@ -354,6 +358,7 @@ extend_qtc_library(QmlDesignerCore rewriteactioncompressor.h rewriterview.cpp signalhandlerproperty.cpp + skipiterator.h stylesheetmerger.cpp textmodifier.cpp texttomodelmerger.cpp @@ -374,6 +379,7 @@ extend_qtc_library(QmlDesignerCore extend_qtc_library(QmlDesignerCore SOURCES_PREFIX designercore/projectstorage PUBLIC_INCLUDES designercore/projectstorage + SOURCES_PROPERTIES SKIP_AUTOMOC ON SOURCES commontypecache.h directorypathcompressor.h @@ -381,6 +387,7 @@ extend_qtc_library(QmlDesignerCore filesystem.cpp filesystem.h filestatus.h filestatuscache.cpp filestatuscache.h + modulescanner.cpp modulescanner.h nonlockingmutex.h projectstorageexceptions.cpp projectstorageexceptions.h projectstorageinterface.h @@ -409,18 +416,17 @@ extend_qtc_library(QmlDesignerCore qmldocumentparser.cpp qmldocumentparser.h ) -file(GLOB PROJECTSTORAGE_EXCLUDED_SOURCES designercore/projectstorage/*.cpp) -set_property(SOURCE ${PROJECTSTORAGE_EXCLUDED_SOURCES} PROPERTY SKIP_AUTOMOC ON) - -extend_qtc_plugin(QmlDesigner - PUBLIC_DEPENDS - QmlDesignerUtils - QmlDesignerBase - QmlPuppetCommunication +add_qtc_plugin(QmlDesigner + PLUGIN_RECOMMENDS QmlPreview + CONDITION Qt6_VERSION VERSION_GREATER_EQUAL 6.4.3 AND TARGET QmlDesignerCore AND TARGET Qt::QuickWidgets AND TARGET Qt::Svg + PLUGIN_DEPENDS + Core ProjectExplorer QmlDesignerBase QmlJSEditor QmakeProjectManager QmlProjectManager + QtSupport DEPENDS - QmlDesignerCore - QmlJS LanguageUtils QmlEditorWidgets AdvancedDockingSystem Sqlite - Qt::QuickWidgets Qt::CorePrivate Qt::Xml Qt::Svg + QmlJS LanguageUtils QmlEditorWidgets AdvancedDockingSystem + Qt::QuickWidgets Qt::CorePrivate Qt::Xml Qt::Svg QmlDesignerCore Sqlite + PUBLIC_DEPENDS + QmlDesignerUtils QmlPuppetCommunication QmlDesignerBase DEFINES IDE_LIBRARY_BASENAME=\"${IDE_LIBRARY_BASE_PATH}\" SHARE_QML_PATH="${CMAKE_CURRENT_SOURCE_DIR}/../../../share/qtcreator/qmldesigner" @@ -474,6 +480,11 @@ extend_qtc_plugin(QmlDesigner QMLDESIGNER_PLUGIN_PATH "${QmlDesignerPluginInstallPrefix}" ) +extend_qtc_plugin(QmlDesigner + CONDITION NOT DISABLE_COMPILE_WARNING_AS_ERROR + PROPERTIES COMPILE_WARNING_AS_ERROR ON +) + function(get_and_add_as_subdirectory name repository git_tag build_dir source_dir source_subdir) # make the configuration in the build dir file(MAKE_DIRECTORY ${build_dir}/${name}) @@ -521,67 +532,6 @@ if (QTC_STATIC_BUILD AND TARGET QmlDesigner) extend_qtc_target(QmlDesigner PUBLIC_DEPENDS TextEditor) endif() -add_qtc_plugin(assetexporterplugin - PLUGIN_CLASS AssetExporterPlugin - CONDITION TARGET QmlDesigner - DEPENDS Core ProjectExplorer QmlDesigner Utils Qt::Qml Qt::QuickPrivate - PUBLIC_INCLUDES assetexporterplugin - SOURCES - assetexporterplugin/assetexportdialog.h assetexporterplugin/assetexportdialog.cpp assetexporterplugin/assetexportdialog.ui - assetexporterplugin/assetexporter.h assetexporterplugin/assetexporter.cpp - assetexporterplugin/assetexporterplugin.h assetexporterplugin/assetexporterplugin.cpp - assetexporterplugin/assetexporterview.h assetexporterplugin/assetexporterview.cpp - assetexporterplugin/assetexportpluginconstants.h - assetexporterplugin/componentexporter.h assetexporterplugin/componentexporter.cpp - assetexporterplugin/exportnotification.h assetexporterplugin/exportnotification.cpp - assetexporterplugin/filepathmodel.h assetexporterplugin/filepathmodel.cpp - assetexporterplugin/dumpers/assetnodedumper.h assetexporterplugin/dumpers/assetnodedumper.cpp - assetexporterplugin/dumpers/itemnodedumper.h assetexporterplugin/dumpers/itemnodedumper.cpp - assetexporterplugin/dumpers/nodedumper.h assetexporterplugin/dumpers/nodedumper.cpp - assetexporterplugin/dumpers/textnodedumper.h assetexporterplugin/dumpers/textnodedumper.cpp - assetexporterplugin/assetexporterplugin.qrc - PLUGIN_PATH ${QmlDesignerPluginInstallPrefix} -) - -add_qtc_plugin(componentsplugin - PLUGIN_CLASS ComponentsPlugin - CONDITION TARGET QmlDesigner - DEPENDS Core QmlDesigner Utils Qt::Qml - DEFINES COMPONENTS_LIBRARY - SOURCES - componentsplugin/addtabdesigneraction.cpp componentsplugin/addtabdesigneraction.h - componentsplugin/addtabtotabviewdialog.cpp componentsplugin/addtabtotabviewdialog.h - componentsplugin/addtabtotabviewdialog.ui - componentsplugin/componentsplugin.cpp componentsplugin/componentsplugin.h - componentsplugin/componentsplugin.qrc - componentsplugin/entertabdesigneraction.cpp componentsplugin/entertabdesigneraction.h - componentsplugin/tabviewindexmodel.cpp componentsplugin/tabviewindexmodel.h - PLUGIN_PATH ${QmlDesignerPluginInstallPrefix} -) - -add_qtc_plugin(qmlpreviewplugin - PLUGIN_CLASS QmlPreviewWidgetPlugin - CONDITION TARGET QmlDesigner - DEPENDS Core ProjectExplorer QmlDesigner Utils Qt::Qml - SOURCES - qmlpreviewplugin/qmlpreviewactions.cpp qmlpreviewplugin/qmlpreviewactions.h - qmlpreviewplugin/qmlpreviewplugin.cpp qmlpreviewplugin/qmlpreviewplugin.h - qmlpreviewplugin/qmlpreviewplugin.qrc - PLUGIN_PATH ${QmlDesignerPluginInstallPrefix} -) - -add_qtc_plugin(qtquickplugin - PLUGIN_CLASS QtQuickPlugin - CONDITION TARGET QmlDesigner - DEPENDS Core QmlDesigner Utils Qt::Qml - DEFINES QTQUICK_LIBRARY - SOURCES - qtquickplugin/qtquickplugin.cpp qtquickplugin/qtquickplugin.h - qtquickplugin/qtquickplugin.qrc - PLUGIN_PATH ${QmlDesignerPluginInstallPrefix} -) - -add_subdirectory(studioplugin) extend_qtc_plugin(QmlDesigner SOURCES_PREFIX components @@ -643,6 +593,9 @@ extend_qtc_plugin(QmlDesigner edit3dactions.cpp edit3dactions.h edit3dvisibilitytogglesmenu.cpp edit3dvisibilitytogglesmenu.h backgroundcolorselection.cpp backgroundcolorselection.h + bakelights.cpp bakelights.h + bakelightsdatamodel.cpp bakelightsdatamodel.h + bakelightsconnectionmanager.cpp bakelightsconnectionmanager.h edit3d.qrc ) @@ -1129,3 +1082,106 @@ extend_qtc_plugin(QmlDesigner CONDITION TARGET Nanotrace DEPENDS Nanotrace ) + +add_qtc_plugin(assetexporterplugin + PLUGIN_CLASS AssetExporterPlugin + CONDITION TARGET QmlDesigner + PLUGIN_DEPENDS + Core ProjectExplorer QmlDesigner + DEPENDS Utils Qt::Qml Qt::QuickPrivate + PUBLIC_INCLUDES assetexporterplugin + PLUGIN_PATH ${QmlDesignerPluginInstallPrefix} +) + +extend_qtc_plugin(assetexporterplugin + CONDITION NOT DISABLE_COMPILE_WARNING_AS_ERROR + PROPERTIES COMPILE_WARNING_AS_ERROR ON +) + +extend_qtc_plugin(assetexporterplugin + SOURCES_PREFIX assetexporterplugin + SOURCES + assetexportdialog.h assetexportdialog.cpp assetexportdialog.ui + assetexporter.h assetexporter.cpp + assetexporterplugin.h assetexporterplugin.cpp + assetexporterview.h assetexporterview.cpp + assetexportpluginconstants.h + componentexporter.h componentexporter.cpp + exportnotification.h exportnotification.cpp + filepathmodel.h filepathmodel.cpp + dumpers/assetnodedumper.h dumpers/assetnodedumper.cpp + dumpers/itemnodedumper.h dumpers/itemnodedumper.cpp + dumpers/nodedumper.h dumpers/nodedumper.cpp + dumpers/textnodedumper.h dumpers/textnodedumper.cpp + assetexporterplugin.qrc +) + +add_qtc_plugin(componentsplugin + PLUGIN_CLASS ComponentsPlugin + CONDITION TARGET QmlDesigner + PLUGIN_DEPENDS Core QmlDesigner + DEPENDS Utils Qt::Qml + DEFINES COMPONENTS_LIBRARY + + PLUGIN_PATH ${QmlDesignerPluginInstallPrefix} +) + +extend_qtc_plugin(componentsplugin + CONDITION NOT DISABLE_COMPILE_WARNING_AS_ERROR + PROPERTIES COMPILE_WARNING_AS_ERROR ON +) + +extend_qtc_plugin(componentsplugin + SOURCES_PREFIX componentsplugin + SOURCES + addtabdesigneraction.cpp addtabdesigneraction.h + addtabtotabviewdialog.cpp addtabtotabviewdialog.h + addtabtotabviewdialog.ui + componentsplugin.cpp componentsplugin.h + componentsplugin.qrc + entertabdesigneraction.cpp entertabdesigneraction.h + tabviewindexmodel.cpp tabviewindexmodel.h +) + +add_qtc_plugin(qmlpreviewplugin + PLUGIN_CLASS QmlPreviewWidgetPlugin + CONDITION TARGET QmlDesigner + PLUGIN_DEPENDS Core ProjectExplorer QmlDesigner + DEPENDS Utils Qt::Qml + PLUGIN_PATH ${QmlDesignerPluginInstallPrefix} +) + +extend_qtc_plugin(qmlpreviewplugin + CONDITION NOT DISABLE_COMPILE_WARNING_AS_ERROR + PROPERTIES COMPILE_WARNING_AS_ERROR ON +) + +extend_qtc_plugin(qmlpreviewplugin + SOURCES_PREFIX qmlpreviewplugin + SOURCES + qmlpreviewactions.cpp qmlpreviewactions.h + qmlpreviewplugin.cpp qmlpreviewplugin.h + qmlpreviewplugin.qrc +) + +add_qtc_plugin(qtquickplugin + PLUGIN_CLASS QtQuickPlugin + CONDITION TARGET QmlDesigner + PLUGIN_DEPENDS Core QmlDesigner + DEPENDS Utils Qt::Qml + DEFINES QTQUICK_LIBRARY + PLUGIN_PATH ${QmlDesignerPluginInstallPrefix} +) + +extend_qtc_plugin(qtquickplugin + CONDITION NOT DISABLE_COMPILE_WARNING_AS_ERROR + PROPERTIES COMPILE_WARNING_AS_ERROR ON +) + +extend_qtc_plugin(qtquickplugin + SOURCES_PREFIX qtquickplugin + SOURCES + qtquickplugin.cpp qtquickplugin.h + qtquickplugin.qrc +) +add_subdirectory(studioplugin) diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp index faf4f1eb549..71cde5f0ba8 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp @@ -136,7 +136,7 @@ bool AssetsLibraryModel::renameFolder(const QString &folderPath, const QString & return dir.rename(oldName, newName); } -bool AssetsLibraryModel::addNewFolder(const QString &folderPath) +QString AssetsLibraryModel::addNewFolder(const QString &folderPath) { QString iterPath = folderPath; QDir dir{folderPath}; @@ -147,7 +147,7 @@ bool AssetsLibraryModel::addNewFolder(const QString &folderPath) dir.setPath(iterPath); } - return dir.mkpath(iterPath); + return dir.mkpath(iterPath) ? iterPath : ""; } bool AssetsLibraryModel::urlPathExistsInModel(const QUrl &url) const diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h index f003ef2c541..e37bf8a109a 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h @@ -45,7 +45,7 @@ public: Q_INVOKABLE bool requestDeleteFiles(const QStringList &filePaths); Q_INVOKABLE void deleteFiles(const QStringList &filePaths, bool dontAskAgain); Q_INVOKABLE bool renameFolder(const QString &folderPath, const QString &newName); - Q_INVOKABLE bool addNewFolder(const QString &folderPath); + Q_INVOKABLE QString addNewFolder(const QString &folderPath); Q_INVOKABLE bool deleteFolderRecursively(const QModelIndex &folderIndex); Q_INVOKABLE bool allFilePathsAreTextures(const QStringList &filePaths) const; diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp index 5112cc3f860..6cd3e814929 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp @@ -94,7 +94,7 @@ void BindingEditor::setBackendValue(const QVariant &backendValue) m_backendValueTypeName = node.metaInfo() .property(propertyEditorValue->name()) .propertyType() - .typeName(); + .simplifiedTypeName(); QString nodeId = node.id(); if (nodeId.isEmpty()) @@ -187,7 +187,7 @@ void BindingEditor::prepareBindings() for (const auto &objnode : allNodes) { BindingEditorDialog::BindingOption binding; for (const auto &property : objnode.metaInfo().properties()) { - const TypeName &propertyTypeName = property.propertyType().typeName(); + const TypeName &propertyTypeName = property.propertyType().simplifiedTypeName(); if (skipTypeFiltering || (m_backendValueTypeName == propertyTypeName) diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.cpp index 14f55efa3f8..90bcc54fddc 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.cpp @@ -95,7 +95,7 @@ void DesignerActionManagerView::nodeOrderChanged(const NodeListProperty &) setupContext(SelectionContext::UpdateMode::NodeHierachy); } -void DesignerActionManagerView::importsChanged(const QList<Import> &, const QList<Import> &) +void DesignerActionManagerView::importsChanged(const Imports &, const Imports &) { setupContext(); } diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.h b/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.h index 41210602b38..9ccfe4e7379 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.h +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.h @@ -37,7 +37,7 @@ public: void selectedNodesChanged(const QList<ModelNode> &, const QList<ModelNode> &) override; void nodeOrderChanged(const NodeListProperty &) override; - void importsChanged(const QList<Import> &, const QList<Import> &) override; + void importsChanged(const Imports &, const Imports &) override; void signalHandlerPropertiesChanged(const QVector<SignalHandlerProperty> &/*propertyList*/, PropertyChangeFlags /*propertyChange*/) override; void variantPropertiesChanged(const QList<VariantProperty>& propertyList, PropertyChangeFlags propertyChangeFlag) override; void bindingPropertiesChanged(const QList<BindingProperty>& propertyList, PropertyChangeFlags propertyChangeFlag) override; diff --git a/src/plugins/qmldesigner/components/componentcore/designericons.h b/src/plugins/qmldesigner/components/componentcore/designericons.h index f460707b915..749e4e1cce4 100644 --- a/src/plugins/qmldesigner/components/componentcore/designericons.h +++ b/src/plugins/qmldesigner/components/componentcore/designericons.h @@ -53,6 +53,7 @@ public: AnchorsIcon, AnnotationIcon, ArrangeIcon, + BackspaceIcon, CameraIcon, CameraOrthographicIcon, CameraPerspectiveIcon, diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index 10386425fe7..258ae0ba925 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -1650,7 +1650,11 @@ void openEffectMaker(const QString &filePath) const QtSupport::QtVersion *baseQtVersion = QtSupport::QtKitAspect::qtVersion(target->kit()); if (baseQtVersion) { + Utils::Environment env = Utils::Environment::systemEnvironment(); + auto effectMakerPath = baseQtVersion->binPath().pathAppended("qqem").withExecutableSuffix(); + if (!effectMakerPath.exists() && env.osType() == Utils::OsTypeMac) + effectMakerPath = baseQtVersion->binPath().pathAppended("qqem.app/Contents/MacOS/qqem"); if (!effectMakerPath.exists()) { qWarning() << __FUNCTION__ << "Cannot find EffectMaker app"; return; @@ -1663,7 +1667,6 @@ void openEffectMaker(const QString &filePath) arguments << "--create"; arguments << "--exportpath" << effectResPath.toString(); - Utils::Environment env = Utils::Environment::systemEnvironment(); if (env.osType() == Utils::OsTypeMac) env.appendOrSet("QSG_RHI_BACKEND", "metal"); diff --git a/src/plugins/qmldesigner/components/componentcore/qmleditormenu.cpp b/src/plugins/qmldesigner/components/componentcore/qmleditormenu.cpp index 173838f170d..e59fca7353e 100644 --- a/src/plugins/qmldesigner/components/componentcore/qmleditormenu.cpp +++ b/src/plugins/qmldesigner/components/componentcore/qmleditormenu.cpp @@ -149,4 +149,6 @@ QmlEditorStyleObject::QmlEditorStyleObject() QmlEditorMenuPrivate::cascadeRight = DesignerIcons::rotateIcon(downIcon, -90); QmlEditorMenuPrivate::tick = DesignerActionManager::instance() .contextIcon(DesignerIcons::SimpleCheckIcon); + QmlEditorMenuPrivate::backspaceIcon = DesignerActionManager::instance() + .contextIcon(DesignerIcons::BackspaceIcon); } diff --git a/src/plugins/qmldesigner/components/componentcore/viewmanager.h b/src/plugins/qmldesigner/components/componentcore/viewmanager.h index c54cf139e78..4201065f99f 100644 --- a/src/plugins/qmldesigner/components/componentcore/viewmanager.h +++ b/src/plugins/qmldesigner/components/componentcore/viewmanager.h @@ -67,7 +67,7 @@ public: void disableWidgets(); void enableWidgets(); - void pushFileOnCrumbleBar(const Utils::FilePath &fileName); + void pushFileOnCrumbleBar(const ::Utils::FilePath &fileName); void pushInFileComponentOnCrumbleBar(const ModelNode &modelNode); void nextFileIsCalledInternally(); diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index b3eb7542f8a..25a5756c28e 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -38,10 +38,7 @@ QStringList propertyNameListToStringList(const QmlDesigner::PropertyNameList &pr bool isConnection(const QmlDesigner::ModelNode &modelNode) { - return (modelNode.type() == "Connections" - || modelNode.type() == "QtQuick.Connections" - || modelNode.type() == "Qt.Connections" - || modelNode.type() == "QtQml.Connections"); + return (modelNode.metaInfo().simplifiedTypeName() == "Connections"); } } //namespace diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp index 39e05a898ca..56fcc7ef6bf 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp @@ -183,7 +183,7 @@ void ConnectionView::auxiliaryDataChanged([[maybe_unused]] const ModelNode &node selectionModel->clearSelection(); } -void ConnectionView::importsChanged(const QList<Import> & /*addedImports*/, const QList<Import> & /*removedImports*/) +void ConnectionView::importsChanged(const Imports & /*addedImports*/, const Imports & /*removedImports*/) { backendModel()->resetModel(); } diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.h b/src/plugins/qmldesigner/components/connectioneditor/connectionview.h index dcf61ad79d9..89c6c489105 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.h @@ -50,7 +50,7 @@ public: AuxiliaryDataKeyView key, const QVariant &data) override; - void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override; + void importsChanged(const Imports &addedImports, const Imports &removedImports) override; void currentStateChanged(const ModelNode &node) override; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp index 127ecb4225f..f272a4121df 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp @@ -7,6 +7,8 @@ #include "contentlibrarymaterial.h" #include "contentlibrarymaterialscategory.h" #include "contentlibrarywidget.h" + +#include <designerpaths.h> #include "filedownloader.h" #include "fileextractor.h" #include "multifiledownloader.h" @@ -30,8 +32,7 @@ ContentLibraryMaterialsModel::ContentLibraryMaterialsModel(ContentLibraryWidget : QAbstractListModel(parent) , m_widget(parent) { - m_downloadPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) - + "/QtDesignStudio/bundles/Materials"; + m_downloadPath = Paths::bundlesPathSetting() + "/Materials"; m_baseUrl = QmlDesignerPlugin::settings() .value(DesignerSettingsKey::DOWNLOADABLE_BUNDLES_URL) diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp index 0d74be596f5..5cf88ab6fcd 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp @@ -4,8 +4,8 @@ #include "contentlibrarytexturesmodel.h" #include "contentlibrarytexturescategory.h" -#include "qmldesignerplugin.h" +#include <designerpaths.h> #include <qmldesignerbase/qmldesignerbaseplugin.h> #include <utils/algorithm.h> @@ -119,8 +119,10 @@ void ContentLibraryTexturesModel::loadTextureBundle(const QString &remoteUrl, co for (const QFileInfo &tex : texFiles) { QString fullRemoteUrl = QString("%1/%2/%3.zip").arg(remoteUrl, dir.fileName(), tex.baseName()); - QString localDownloadPath = QString("%1/%2/%3").arg(QmlDesignerBasePlugin::bundlesPathSetting(), - m_category, dir.fileName()); + QString localDownloadPath = QString("%1/%2/%3") + .arg(Paths::bundlesPathSetting(), + m_category, + dir.fileName()); QString key = QString("%1/%2/%3").arg(m_category, dir.fileName(), tex.baseName()); QString fileExt; QSize dimensions; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp index 9ce7a4e7377..3bb579273f6 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp @@ -144,7 +144,7 @@ void ContentLibraryView::modelAboutToBeDetached(Model *model) AbstractView::modelAboutToBeDetached(model); } -void ContentLibraryView::importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) +void ContentLibraryView::importsChanged(const Imports &addedImports, const Imports &removedImports) { Q_UNUSED(addedImports) Q_UNUSED(removedImports) diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h index 1d27f6e260c..137034dd955 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h @@ -31,7 +31,7 @@ public: // AbstractView void modelAttached(Model *model) override; void modelAboutToBeDetached(Model *model) override; - void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override; + void importsChanged(const Imports &addedImports, const Imports &removedImports) override; void active3DSceneChanged(qint32 sceneId) override; void selectedNodesChanged(const QList<ModelNode> &selectedNodeList, const QList<ModelNode> &lastSelectedNodeList) override; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp index a1dfff5ef53..9cc9576bc6c 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp @@ -12,6 +12,7 @@ #include "utils/fileextractor.h" #include <coreplugin/icore.h> +#include <designerpaths.h> #include <qmldesignerconstants.h> #include <qmldesignerplugin.h> @@ -117,13 +118,12 @@ ContentLibraryWidget::ContentLibraryWidget() m_baseUrl = QmlDesignerPlugin::settings() .value(DesignerSettingsKey::DOWNLOADABLE_BUNDLES_URL).toString() - + "/textures/v1"; + + "/textures"; m_texturesUrl = m_baseUrl + "/Textures"; m_environmentsUrl = m_baseUrl + "/Environments"; - m_downloadPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) - + "/QtDesignStudio/bundles"; + m_downloadPath = Paths::bundlesPathSetting(); loadTextureBundle(); @@ -436,18 +436,4 @@ QPointer<ContentLibraryTexturesModel> ContentLibraryWidget::environmentsModel() return m_environmentsModel; } -bool ContentLibraryWidget::markTextureDownloading() -{ - if (m_anyTextureBeingDownloaded) - return false; - - m_anyTextureBeingDownloaded = true; - return true; // let the caller know it can begin download -} - -void ContentLibraryWidget::markNoTextureDownloading() -{ - m_anyTextureBeingDownloaded = false; // allow other textures to be downloaded -} - } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h index 519d6fcf780..6d4ba51a300 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h @@ -61,8 +61,6 @@ public: Q_INVOKABLE void addTexture(QmlDesigner::ContentLibraryTexture *tex); Q_INVOKABLE void addLightProbe(QmlDesigner::ContentLibraryTexture *tex); Q_INVOKABLE void updateSceneEnvState(); - Q_INVOKABLE bool markTextureDownloading(); - Q_INVOKABLE void markNoTextureDownloading(); signals: void bundleMaterialDragStarted(QmlDesigner::ContentLibraryMaterial *bundleMat); @@ -102,7 +100,6 @@ private: bool m_hasMaterialLibrary = false; bool m_hasQuick3DImport = false; bool m_isDragging = false; - bool m_anyTextureBeingDownloaded = false; QString m_baseUrl; QString m_texturesUrl; QString m_environmentsUrl; diff --git a/src/plugins/qmldesigner/components/debugview/debugview.cpp b/src/plugins/qmldesigner/components/debugview/debugview.cpp index 2888575e44c..2a62d7a6b2b 100644 --- a/src/plugins/qmldesigner/components/debugview/debugview.cpp +++ b/src/plugins/qmldesigner/components/debugview/debugview.cpp @@ -65,7 +65,7 @@ void DebugView::modelAboutToBeDetached(Model *model) AbstractView::modelAboutToBeDetached(model); } -void DebugView::importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) +void DebugView::importsChanged(const Imports &addedImports, const Imports &removedImports) { if (isDebugViewEnabled()) { QString message; diff --git a/src/plugins/qmldesigner/components/debugview/debugview.h b/src/plugins/qmldesigner/components/debugview/debugview.h index 885030545cf..e80c619c818 100644 --- a/src/plugins/qmldesigner/components/debugview/debugview.h +++ b/src/plugins/qmldesigner/components/debugview/debugview.h @@ -24,7 +24,7 @@ public: void modelAttached(Model *model) override; void modelAboutToBeDetached(Model *model) override; - void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override; + void importsChanged(const Imports &addedImports, const Imports &removedImports) override; void nodeCreated(const ModelNode &createdNode) override; void nodeAboutToBeRemoved(const ModelNode &removedNode) override; diff --git a/src/plugins/qmldesigner/components/edit3d/bakelights.cpp b/src/plugins/qmldesigner/components/edit3d/bakelights.cpp new file mode 100644 index 00000000000..53c7e53df9d --- /dev/null +++ b/src/plugins/qmldesigner/components/edit3d/bakelights.cpp @@ -0,0 +1,430 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#include "bakelights.h" + +#include "abstractview.h" +#include "auxiliarydataproperties.h" +#include "bakelightsdatamodel.h" +#include "bakelightsconnectionmanager.h" +#include "bindingproperty.h" +#include "documentmanager.h" +#include "modelnode.h" +#include "nodeabstractproperty.h" +#include "nodeinstanceview.h" +#include "nodemetainfo.h" +#include "plaintexteditmodifier.h" +#include "rewriterview.h" +#include "variantproperty.h" + +#include <coreplugin/icore.h> + +#include <qmljs/qmljsmodelmanagerinterface.h> + +#include <utils/algorithm.h> +#include <utils/environment.h> +#include <utils/filepath.h> +#include <utils/qtcassert.h> + +#include <QEvent> +#include <QQmlContext> +#include <QQmlEngine> +#include <QQuickView> +#include <QSaveFile> +#include <QTextCursor> +#include <QTextDocument> +#include <QTimer> +#include <QVariant> + +namespace QmlDesigner { + +static QString propertyEditorResourcesPath() +{ +#ifdef SHARE_QML_PATH + if (Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) + return QLatin1String(SHARE_QML_PATH) + "/propertyEditorQmlSources"; +#endif + return Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources").toString(); +} + +static QString qmlSourcesPath() +{ +#ifdef SHARE_QML_PATH + if (Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) + return QLatin1String(SHARE_QML_PATH) + "/edit3dQmlSource"; +#endif + return Core::ICore::resourcePath("qmldesigner/edit3dQmlSource").toString(); +} + +BakeLights::BakeLights(AbstractView *view) + : QObject(view) + , m_view(view) +{ + m_view3dId = resolveView3dId(view); + + if (m_view3dId.isEmpty()) { + // It should never get here, baking controls should be disabled in this case + qWarning() << __FUNCTION__ << "Active scene is not View3D"; + deleteLater(); + return; + } + + showSetupDialog(); +} + +BakeLights::~BakeLights() +{ + cleanup(); +} + +ModelNode BakeLights::resolveView3dNode(AbstractView *view) +{ + if (!view || !view->model()) + return {}; + + ModelNode activeView3D; + ModelNode activeScene = view->active3DSceneNode(); + + if (activeScene.isValid()) { + if (activeScene.metaInfo().isQtQuick3DView3D()) { + activeView3D = activeScene; + } else { + ModelNode sceneParent = activeScene.parentProperty().parentModelNode(); + if (sceneParent.metaInfo().isQtQuick3DView3D()) + activeView3D = sceneParent; + } + return activeView3D; + } + + return {}; +} + +QString BakeLights::resolveView3dId(AbstractView *view) +{ + ModelNode activeView3D = resolveView3dNode(view); + + if (activeView3D.isValid()) + return activeView3D.id(); + + return {}; +} + +void BakeLights::raiseDialog() +{ + if (m_progressDialog) + m_progressDialog->raise(); +} + +bool BakeLights::manualMode() const +{ + return m_manualMode; +} + +void BakeLights::setManualMode(bool enabled) +{ + if (m_manualMode != enabled) { + m_manualMode = enabled; + emit manualModeChanged(); + } +} + +void BakeLights::bakeLights() +{ + if (!m_view || !m_view->model()) + return; + + m_setupDialog->hide(); + showProgressDialog(); + + // Start baking process + m_connectionManager = new BakeLightsConnectionManager; + m_rewriterView = new RewriterView{m_view->externalDependencies(), RewriterView::Amend}; + m_nodeInstanceView = new NodeInstanceView{*m_connectionManager, m_view->externalDependencies()}; + + m_model = QmlDesigner::Model::create("QtQuick/Item", 2, 1); + m_model->setFileUrl(m_view->model()->fileUrl()); + + // Take the current unsaved state of the main model and apply it to our copy + auto textDocument = std::make_unique<QTextDocument>( + m_view->model()->rewriterView()->textModifier()->textDocument()->toRawText()); + + auto modifier = std::make_unique<NotIndentingTextEditModifier>(textDocument.get(), + QTextCursor{textDocument.get()}); + + m_rewriterView->setTextModifier(modifier.get()); + m_model->setRewriterView(m_rewriterView); + + auto rootModelNodeMetaInfo = m_rewriterView->rootModelNode().metaInfo(); + bool is3DRoot = m_rewriterView->errors().isEmpty() + && (rootModelNodeMetaInfo.isQtQuick3DNode() + || rootModelNodeMetaInfo.isQtQuick3DMaterial()); + + if (!m_rewriterView->errors().isEmpty() + || (!m_rewriterView->rootModelNode().metaInfo().isGraphicalItem() && !is3DRoot)) { + emit progress(tr("Invalid root node, baking aborted.")); + emit finished(); + m_progressDialog->raise(); + return; + } + + m_nodeInstanceView->setTarget(m_view->nodeInstanceView()->target()); + + auto progressCallback = [this](const QString &msg) { + emit progress(msg); + }; + + auto finishedCallback = [this](const QString &msg) { + m_progressDialog->raise(); + emit progress(msg); + emit finished(); + + // Puppet reset is needed to update baking results to current views + m_view->resetPuppet(); + }; + + auto crashCallback = [this]() { + m_progressDialog->raise(); + emit progress(tr("Baking process crashed, baking aborted.")); + emit finished(); + }; + + m_connectionManager->setProgressCallback(std::move(progressCallback)); + m_connectionManager->setFinishedCallback(std::move(finishedCallback)); + m_nodeInstanceView->setCrashCallback(std::move(crashCallback)); + + m_model->setNodeInstanceView(m_nodeInstanceView); + + // InternalIds are not guaranteed to match between normal model and our copy of it, so + // we identify the View3D by its qml id. + m_nodeInstanceView->view3DAction(View3DActionType::SetBakeLightsView3D, m_view3dId); +} + +void BakeLights::apply() +{ + // Uninitialized QVariant stored instead of false to remove the aux property in that case + m_dataModel->view3dNode().setAuxiliaryData(bakeLightsManualProperty, + m_manualMode ? QVariant{true} : QVariant{}); + + if (!m_manualMode) + m_dataModel->apply(); + + // Create folders for lightmaps if they do not exist + PropertyName loadPrefixPropName{"loadPrefix"}; + const QList<ModelNode> bakedLightmapNodes = m_view->allModelNodesOfType( + m_view->model()->qtQuick3DBakedLightmapMetaInfo()); + Utils::FilePath currentPath = DocumentManager::currentFilePath().absolutePath(); + QSet<Utils::FilePath> pathSet; + for (const ModelNode &node : bakedLightmapNodes) { + if (node.hasVariantProperty(loadPrefixPropName)) { + QString prefix = node.variantProperty(loadPrefixPropName).value().toString(); + Utils::FilePath fp = Utils::FilePath::fromString(prefix); + if (fp.isRelativePath()) { + fp = currentPath.pathAppended(prefix); + if (!fp.exists()) + pathSet.insert(fp); + } + } + } + for (const Utils::FilePath &fp : std::as_const(pathSet)) + fp.createDir(); +} + +void BakeLights::rebake() +{ + QTimer::singleShot(0, this, [this]() { + cleanup(); + showSetupDialog(); + }); +} + +void BakeLights::exposeModelsAndLights(const QString &nodeId) +{ + ModelNode compNode = m_view->modelNodeForId(nodeId); + if (!compNode.isValid() || !compNode.isComponent() + || compNode.metaInfo().componentFileName().isEmpty()) { + return; + } + + RewriterView rewriter{m_view->externalDependencies(), RewriterView::Amend}; + ModelPointer compModel = QmlDesigner::Model::create("QtQuick/Item", 2, 1); + const QString compFile = compNode.metaInfo().componentFileName(); + const Utils::FilePath compFilePath = Utils::FilePath::fromString(compFile); + QByteArray src = compFilePath.fileContents().value(); + + compModel->setFileUrl(QUrl::fromLocalFile(compFile)); + + auto textDocument = std::make_unique<QTextDocument>(QString::fromUtf8(src)); + auto modifier = std::make_unique<IndentingTextEditModifier>( + textDocument.get(), QTextCursor{textDocument.get()}); + + rewriter.setTextModifier(modifier.get()); + compModel->setRewriterView(&rewriter); + + if (!rewriter.rootModelNode().isValid() || !rewriter.errors().isEmpty()) + return; + + QString originalText = modifier->text(); + QStringList idList; + + rewriter.executeInTransaction(__FUNCTION__, [&]() { + QList<ModelNode> nodes = rewriter.rootModelNode().allSubModelNodes(); + for (ModelNode &node : nodes) { + if (node.metaInfo().isQtQuick3DModel() || node.metaInfo().isQtQuick3DLight()) { + QString idStr = node.id(); + if (idStr.isEmpty()) { + const QString type = node.metaInfo().isQtQuick3DModel() ? "model" : "light"; + idStr = compModel->generateNewId(type); + node.setIdWithoutRefactoring(idStr); + } + idList.append(idStr); + } + } + }); + + rewriter.executeInTransaction(__FUNCTION__, [&]() { + for (const QString &id : std::as_const(idList)) { + ModelNode node = rewriter.modelNodeForId(id); + if (!node.isValid()) + continue; + rewriter.rootModelNode().bindingProperty(id.toUtf8()) + .setDynamicTypeNameAndExpression("alias", id); + } + }); + + rewriter.forceAmend(); + + QString newText = modifier->text(); + if (newText != originalText) { + QSaveFile saveFile(compFile); + if (saveFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + saveFile.write(newText.toUtf8()); + saveFile.commit(); + } else { + qWarning() << __FUNCTION__ << "Failed to save changes to:" << compFile; + } + } + + QmlJS::ModelManagerInterface *modelManager = QmlJS::ModelManagerInterface::instance(); + QmlJS::Document::Ptr doc = rewriter.document()->ptr(); + modelManager->updateDocument(doc); + + m_view->model()->rewriterView()->forceAmend(); + + compModel->setRewriterView({}); + + // Rebake to relaunch setup dialog with updated properties + rebake(); +} + +void BakeLights::showSetupDialog() +{ + if (!m_dataModel) + m_dataModel = new BakeLightsDataModel(m_view); + + m_dataModel->reset(); + + auto data = m_dataModel->view3dNode().auxiliaryData(bakeLightsManualProperty); + if (data) + m_manualMode = data->toBool(); + + if (!m_setupDialog) { + // Show non-modal progress dialog with cancel button + QString path = qmlSourcesPath() + "/BakeLightsSetupDialog.qml"; + + m_setupDialog = new QQuickView; + m_setupDialog->setTitle(tr("Lights Baking Setup")); + m_setupDialog->setResizeMode(QQuickView::SizeRootObjectToView); + m_setupDialog->setMinimumSize({550, 200}); + m_setupDialog->setWidth(550); + m_setupDialog->setHeight(400); + m_setupDialog->setFlags(Qt::Dialog); + m_setupDialog->setModality(Qt::ApplicationModal); + m_setupDialog->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); + + m_setupDialog->rootContext()->setContextProperties({ + {"rootView", QVariant::fromValue(this)}, + {"sceneId", QVariant::fromValue(m_view3dId)}, + {"bakeModel", QVariant::fromValue(m_dataModel.data())} + }); + m_setupDialog->setSource(QUrl::fromLocalFile(path)); + m_setupDialog->installEventFilter(this); + } + m_setupDialog->show(); +} + +void BakeLights::showProgressDialog() +{ + if (!m_progressDialog) { + // Show non-modal progress dialog with cancel button + QString path = qmlSourcesPath() + "/BakeLightsProgressDialog.qml"; + + m_progressDialog = new QQuickView; + m_progressDialog->setTitle(tr("Lights Baking Progress")); + m_progressDialog->setResizeMode(QQuickView::SizeRootObjectToView); + m_progressDialog->setMinimumSize({150, 100}); + m_progressDialog->setWidth(800); + m_progressDialog->setHeight(400); + m_progressDialog->setFlags(Qt::Dialog); + m_progressDialog->setModality(Qt::NonModal); + m_progressDialog->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); + + m_progressDialog->rootContext()->setContextProperties({ + {"rootView", QVariant::fromValue(this)}, + {"sceneId", QVariant::fromValue(m_view3dId)} + }); + m_progressDialog->setSource(QUrl::fromLocalFile(path)); + m_progressDialog->installEventFilter(this); + } + m_progressDialog->show(); +} + +void BakeLights::cleanup() +{ + if (m_connectionManager) { + m_connectionManager->setProgressCallback({}); + m_connectionManager->setFinishedCallback({}); + m_nodeInstanceView->setCrashCallback({}); + } + + if (m_model) { + m_model->setNodeInstanceView({}); + m_model->setRewriterView({}); + m_model.reset(); + } + + delete m_setupDialog; + delete m_progressDialog; + delete m_rewriterView; + delete m_nodeInstanceView; + delete m_connectionManager; + delete m_dataModel; + + m_manualMode = false; +} + +void BakeLights::cancel() +{ + if (!m_setupDialog.isNull() && m_setupDialog->isVisible()) + m_setupDialog->close(); + if (!m_progressDialog.isNull() && m_progressDialog->isVisible()) + m_progressDialog->close(); + + deleteLater(); +} + +bool BakeLights::eventFilter(QObject *obj, QEvent *event) +{ + if (obj == m_progressDialog || obj == m_setupDialog) { + if (event->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event); + if (keyEvent->key() == Qt::Key_Escape) + cancel(); + } else if (event->type() == QEvent::Close) { + cancel(); + } + } + + return QObject::eventFilter(obj, event); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/bakelights.h b/src/plugins/qmldesigner/components/edit3d/bakelights.h new file mode 100644 index 00000000000..65f00acc7b0 --- /dev/null +++ b/src/plugins/qmldesigner/components/edit3d/bakelights.h @@ -0,0 +1,74 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 +#pragma once + +#include "modelnode.h" +#include "qmldesignercorelib_global.h" + +#include <QObject> +#include <QPointer> + +QT_BEGIN_NAMESPACE +class QQuickView; +QT_END_NAMESPACE + +namespace QmlDesigner { + +class AbstractView; +class BakeLightsConnectionManager; +class NodeInstanceView; +class RewriterView; +class BakeLightsDataModel; + +class BakeLights : public QObject +{ + Q_OBJECT + + Q_PROPERTY(bool manualMode READ manualMode WRITE setManualMode NOTIFY manualModeChanged) + +public: + BakeLights(AbstractView *view); + ~BakeLights(); + + Q_INVOKABLE void cancel(); + Q_INVOKABLE void bakeLights(); + Q_INVOKABLE void apply(); + Q_INVOKABLE void rebake(); + Q_INVOKABLE void exposeModelsAndLights(const QString &nodeId); + + void raiseDialog(); + + bool manualMode() const; + void setManualMode(bool enabled); + + static ModelNode resolveView3dNode(AbstractView *view); + static QString resolveView3dId(AbstractView *view); + +signals: + void finished(); + void progress(const QString &msg); + void manualModeChanged(); + +protected: + bool eventFilter(QObject *obj, QEvent *event) override; + +private: + void showSetupDialog(); + void showProgressDialog(); + void cleanup(); + + // Separate dialogs for setup and progress, as setup needs to be modal + QPointer<QQuickView> m_setupDialog; + QPointer<QQuickView> m_progressDialog; + + QPointer<BakeLightsConnectionManager> m_connectionManager; + QPointer<NodeInstanceView> m_nodeInstanceView; + QPointer<RewriterView> m_rewriterView; + QPointer<AbstractView> m_view; + QPointer<BakeLightsDataModel> m_dataModel; + ModelPointer m_model; + QString m_view3dId; + bool m_manualMode = false; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/bakelightsconnectionmanager.cpp b/src/plugins/qmldesigner/components/edit3d/bakelightsconnectionmanager.cpp new file mode 100644 index 00000000000..d2a6f3d332d --- /dev/null +++ b/src/plugins/qmldesigner/components/edit3d/bakelightsconnectionmanager.cpp @@ -0,0 +1,48 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#include "bakelightsconnectionmanager.h" + +#include <puppettocreatorcommand.h> + +namespace QmlDesigner { + +BakeLightsConnectionManager::BakeLightsConnectionManager() +{ + connections().emplace_back("Bake lights", "bakelightsmode"); +} + +void BakeLightsConnectionManager::setProgressCallback(Callback callback) +{ + m_progressCallback = std::move(callback); +} + +void BakeLightsConnectionManager::setFinishedCallback(Callback callback) +{ + m_finishedCallback = std::move(callback); +} + +void BakeLightsConnectionManager::dispatchCommand(const QVariant &command, + ConnectionManagerInterface::Connection &) +{ + static const int commandType = QMetaType::type("PuppetToCreatorCommand"); + + if (command.userType() == commandType) { + auto cmd = command.value<PuppetToCreatorCommand>(); + switch (cmd.type()) { + case PuppetToCreatorCommand::BakeLightsProgress: + m_progressCallback(cmd.data().toString()); + break; + case PuppetToCreatorCommand::BakeLightsAborted: + m_finishedCallback(tr("Baking aborted: %1").arg(cmd.data().toString())); + break; + case PuppetToCreatorCommand::BakeLightsFinished: + m_finishedCallback(tr("Baking finished!")); + break; + default: + break; + } + } +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/bakelightsconnectionmanager.h b/src/plugins/qmldesigner/components/edit3d/bakelightsconnectionmanager.h new file mode 100644 index 00000000000..2af5e56c114 --- /dev/null +++ b/src/plugins/qmldesigner/components/edit3d/bakelightsconnectionmanager.h @@ -0,0 +1,28 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "connectionmanager.h" + +namespace QmlDesigner { + +class BakeLightsConnectionManager : public ConnectionManager +{ +public: + using Callback = std::function<void(const QString &)>; + + BakeLightsConnectionManager(); + + void setProgressCallback(Callback callback); + void setFinishedCallback(Callback callback); + +protected: + void dispatchCommand(const QVariant &command, Connection &connection) override; + +private: + Callback m_progressCallback; + Callback m_finishedCallback; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp b/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp new file mode 100644 index 00000000000..36d192a9244 --- /dev/null +++ b/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp @@ -0,0 +1,387 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#include "bakelightsdatamodel.h" + +#include "abstractview.h" +#include "bakelights.h" +#include "bindingproperty.h" +#include "enumeration.h" +#include "externaldependenciesinterface.h" +#include "model.h" +#include "modelnode.h" +#include "nodelistproperty.h" +#include "nodemetainfo.h" +#include "qmlobjectnode.h" +#include "variantproperty.h" + +#include <utils/expected.h> +#include <utils/filepath.h> +#include <utils/qtcassert.h> + +#include <algorithm> + +namespace QmlDesigner { + + +BakeLightsDataModel::BakeLightsDataModel(AbstractView *view) + : QAbstractListModel(view) + , m_view(view) +{ +} + +BakeLightsDataModel::~BakeLightsDataModel() +{ +} + +int BakeLightsDataModel::rowCount(const QModelIndex &) const +{ + return m_dataList.count(); +} + +QHash<int, QByteArray> BakeLightsDataModel::roleNames() const +{ + static const QHash<int, QByteArray> roles { + {Qt::UserRole + 1, "displayId"}, + {Qt::UserRole + 2, "nodeId"}, + {Qt::UserRole + 3, "isModel"}, + {Qt::UserRole + 4, "isEnabled"}, + {Qt::UserRole + 5, "inUse"}, + {Qt::UserRole + 6, "isTitle"}, + {Qt::UserRole + 7, "isUnexposed"}, + {Qt::UserRole + 8, "resolution"}, + {Qt::UserRole + 9, "bakeMode"} + }; + return roles; +} + +QVariant BakeLightsDataModel::data(const QModelIndex &index, int role) const +{ + QTC_ASSERT(index.isValid() && index.row() < m_dataList.count(), return {}); + QTC_ASSERT(roleNames().contains(role), return {}); + + QByteArray roleName = roleNames().value(role); + const BakeData &bakeData = m_dataList[index.row()]; + + if (roleName == "displayId") { + const QString id = bakeData.id; + const PropertyName aliasProp = bakeData.aliasProp; + if (aliasProp.isEmpty()) + return id; + else + return QVariant{id + " - " + QString::fromUtf8(aliasProp)}; + return {}; + } + + if (roleName == "nodeId") + return bakeData.id; + + if (roleName == "isModel") + return bakeData.isModel; + + if (roleName == "isEnabled") + return bakeData.enabled; + + if (roleName == "inUse") + return bakeData.inUse; + + if (roleName == "isTitle") + return bakeData.isTitle; + + if (roleName == "isUnexposed") + return bakeData.isUnexposed; + + if (roleName == "resolution") + return bakeData.resolution; + + if (roleName == "bakeMode") + return bakeData.bakeMode; + + return {}; +} + +bool BakeLightsDataModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + QTC_ASSERT(index.isValid() && index.row() < m_dataList.count(), return false); + QTC_ASSERT(roleNames().contains(role), return false); + + QByteArray roleName = roleNames().value(role); + BakeData &bakeData = m_dataList[index.row()]; + + bool changed = false; + + if (roleName == "isEnabled") { + changed = bakeData.enabled != value.toBool(); + bakeData.enabled = value.toBool(); + } else if (roleName == "inUse") { + changed = bakeData.inUse != value.toBool(); + bakeData.inUse = value.toBool(); + } else if (roleName == "resolution") { + changed = bakeData.resolution != value.toInt(); + bakeData.resolution = value.toInt(); + } else if (roleName == "bakeMode") { + changed = bakeData.bakeMode != value.toString(); + bakeData.bakeMode = value.toString(); + } + + if (changed) + emit dataChanged(index, index, {role}); + + return changed; +} + +void BakeLightsDataModel::reset() +{ + if (!m_view || !m_view->model()) + return; + + beginResetModel(); + m_dataList.clear(); + + m_view3dNode = BakeLights::resolveView3dNode(m_view); + + // Find all models and lights in active View3D + QList<ModelNode> nodes = m_view3dNode.allSubModelNodes(); + + if (m_view3dNode.hasBindingProperty("importScene")) + nodes.append(m_view3dNode.bindingProperty("importScene").resolveToModelNode().allSubModelNodesAndThisNode()); + + QList<BakeData> modelList; + QList<BakeData> lightList; + QList<BakeData> compModelList; + QList<BakeData> compLightList; + QList<BakeData> unexposedList; + + // Note: We are always loading base state values for baking. If users want to bake + // differently for different states, they need to setup things manually for now. + // Same goes if they want to use any unusual bindings in baking properties. + for (const auto &node : std::as_const(nodes)) { + if (QmlObjectNode(node).hasError()) + continue; + + BakeData data; + data.id = node.id(); + if (data.id.isEmpty()) + continue; // Skip nodes without id + + if (node.metaInfo().isQtQuick3DModel()) { + data.isModel = true; + if (node.hasBindingProperty("bakedLightmap")) { + ModelNode blm = node.bindingProperty("bakedLightmap").resolveToModelNode(); + if (blm.isValid()) { + if (blm.hasVariantProperty("enabled")) + data.enabled = blm.variantProperty("enabled").value().toBool(); + else + data.enabled = true; + } + } + if (node.hasVariantProperty("lightmapBaseResolution")) + data.resolution = node.variantProperty("lightmapBaseResolution").value().toInt(); + if (node.hasVariantProperty("usedInBakedLighting")) + data.inUse = node.variantProperty("usedInBakedLighting").value().toBool(); + modelList.append(data); + } else if (node.metaInfo().isQtQuick3DLight()) { + if (node.hasVariantProperty("bakeMode")) { + data.bakeMode = node.variantProperty("bakeMode").value() + .value<QmlDesigner::Enumeration>().toString(); + } else { + data.bakeMode = "Light.BakeModeDisabled"; + } + lightList.append(data); + } + + if (node.isComponent()) { + // Every component can expose multiple aliases + // We ignore baking properties defined inside the component (no visibility there) + bool hasExposedProps = false; + const QList<AbstractProperty> props = node.properties(); + PropertyMetaInfos metaInfos = node.metaInfo().properties(); + for (const PropertyMetaInfo &mi : metaInfos) { + if (mi.isValid() && !mi.isPrivate() && mi.isWritable()) { + BakeData propData; + propData.id = data.id; + propData.aliasProp = mi.name(); + if (mi.propertyType().isQtQuick3DModel()) { + hasExposedProps = true; + propData.isModel = true; + PropertyName dotName = mi.name() + '.'; + for (const AbstractProperty &prop : props) { + if (prop.name().startsWith(dotName)) { + PropertyName subName = prop.name().mid(dotName.size()); + if (subName == "bakedLightmap") { + ModelNode blm = prop.toBindingProperty().resolveToModelNode(); + if (blm.isValid()) { + if (blm.hasVariantProperty("enabled")) + propData.enabled = blm.variantProperty("enabled").value().toBool(); + else + propData.enabled = true; + } + } + if (subName == "lightmapBaseResolution") + propData.resolution = prop.toVariantProperty().value().toInt(); + if (subName == "usedInBakedLighting") + propData.inUse = prop.toVariantProperty().value().toBool(); + } + } + compModelList.append(propData); + } else if (mi.propertyType().isQtQuick3DLight()) { + hasExposedProps = true; + PropertyName dotName = mi.name() + '.'; + for (const AbstractProperty &prop : props) { + if (prop.name().startsWith(dotName)) { + PropertyName subName = prop.name().mid(dotName.size()); + if (subName == "bakeMode") { + propData.bakeMode = prop.toVariantProperty().value() + .value<QmlDesigner::Enumeration>() + .toString(); + } + } + } + if (propData.bakeMode.isEmpty()) + propData.bakeMode = "Light.BakeModeDisabled"; + compLightList.append(propData); + } + } + } + + if (!hasExposedProps && node.metaInfo().isFileComponent() + && node.metaInfo().isQtQuick3DNode()) { + const QString compFile = node.metaInfo().componentFileName(); + const QString projPath = m_view->externalDependencies().currentProjectDirPath(); + if (compFile.startsWith(projPath)) { + // Quick and dirty scan of the component source to check if it potentially has + // models or lights. + QByteArray src = Utils::FilePath::fromString(compFile).fileContents().value(); + src = src.mid(src.indexOf('{')); // Skip root element + if (src.contains("Model {") || src.contains("Light {")) { + data.isUnexposed = true; + unexposedList.append(data); + } + } + } + } + } + + auto sortList = [](QList<BakeData> &list) { + std::sort(list.begin(), list.end(), [](const BakeData &a, const BakeData &b) { + return a.id.compare(b.id) < 0; + }); + }; + + sortList(modelList); + sortList(lightList); + sortList(compModelList); + sortList(compLightList); + sortList(unexposedList); + + BakeData titleData; + titleData.isTitle = true; + titleData.id = tr("Lights"); + m_dataList.append(titleData); + m_dataList.append(lightList); + m_dataList.append(compLightList); + titleData.id = tr("Models"); + m_dataList.append(titleData); + m_dataList.append(modelList); + m_dataList.append(compModelList); + + if (!unexposedList.isEmpty()) { + titleData.id = tr("Components with unexposed models and/or lights"); + m_dataList.append(titleData); + m_dataList.append(unexposedList); + } + + endResetModel(); +} + +void BakeLightsDataModel::apply() +{ + if (!m_view || !m_view->model()) + return; + + auto setBakedLightmap = [this](const ModelNode &node, const BakeData &data) { + ModelNode blmNode; + PropertyName propName{"bakedLightmap"}; + if (!data.aliasProp.isEmpty()) + propName.prepend(data.aliasProp + '.'); + if (node.hasBindingProperty(propName)) + blmNode = node.bindingProperty(propName).resolveToModelNode(); + if (!blmNode.isValid() && data.enabled) { + NodeMetaInfo metaInfo = m_view->model()->qtQuick3DBakedLightmapMetaInfo(); + blmNode = m_view->createModelNode("QtQuick3D.BakedLightmap", + metaInfo.majorVersion(), + metaInfo.minorVersion()); + QString idPart; + if (data.aliasProp.isEmpty()) + idPart = data.id; + else + idPart = QStringLiteral("%1_%2").arg(data.id, QString::fromUtf8(data.aliasProp)); + QString newId = m_view->model()->generateNewId(QStringLiteral("blm_%1").arg(idPart)); + blmNode.setIdWithoutRefactoring(newId); + node.defaultNodeListProperty().reparentHere(blmNode); + node.bindingProperty(propName).setExpression(newId); + } + if (blmNode.isValid()) { + VariantProperty enabledProp = blmNode.variantProperty("enabled"); + VariantProperty prefixProp = blmNode.variantProperty("loadPrefix"); + VariantProperty keyProp = blmNode.variantProperty("key"); + enabledProp.setValue(data.enabled); + prefixProp.setValue(commonPrefix()); + keyProp.setValue(blmNode.id()); + } + }; + + auto setVariantProp = [](const ModelNode &node, + const PropertyName &propName, + const PropertyName &aliasProp, + const QVariant &value, + const QVariant &defaultValue) { + PropertyName resolvedName = propName; + if (!aliasProp.isEmpty()) + resolvedName.prepend(aliasProp + '.'); + if (node.hasVariantProperty(resolvedName) || value != defaultValue) + node.variantProperty(resolvedName).setValue(value); + }; + + // Commits changes to scene model + m_view->executeInTransaction(__FUNCTION__, [&]() { + for (const BakeData &data : std::as_const(m_dataList)) { + if (data.isTitle || data.isUnexposed) + continue; + ModelNode node = m_view->modelNodeForId(data.id); + if (data.isModel) { + setBakedLightmap(node, data); + setVariantProp(node, "lightmapBaseResolution", data.aliasProp, data.resolution, 1024); + setVariantProp(node, "usedInBakedLighting", data.aliasProp, data.inUse, false); + } else { + setVariantProp(node, "bakeMode", data.aliasProp, + QVariant::fromValue(QmlDesigner::Enumeration(data.bakeMode)), + QVariant::fromValue(QmlDesigner::Enumeration("Light", "BakeModeDisabled"))); + } + } + }); +} + +QString BakeLightsDataModel::commonPrefix() +{ + static QString prefix = "lightmaps"; + return prefix; +} + +QDebug operator<<(QDebug debug, const BakeLightsDataModel::BakeData &data) +{ + QDebugStateSaver saver(debug); + debug.space() << "(" + << "id:" << data.id + << "aliasProp:" << data.aliasProp + << "isModel:" << data.isModel + << "enabled:" << data.enabled + << "inUse:" << data.inUse + << "resolution:" << data.resolution + << "bakeMode:" << data.bakeMode + << "isTitle:" << data.isTitle + << ")"; + return debug; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.h b/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.h new file mode 100644 index 00000000000..313c09cf459 --- /dev/null +++ b/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.h @@ -0,0 +1,56 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 +#pragma once + +#include "modelnode.h" +#include "qmldesignercorelib_global.h" + +#include <QAbstractListModel> +#include <QObject> +#include <QPointer> + +namespace QmlDesigner { + +class AbstractView; + +class BakeLightsDataModel : public QAbstractListModel +{ + Q_OBJECT + +public: + struct BakeData { + QString id; // node id. Also used as BakedLightmap.key + PropertyName aliasProp; // property id for component exposed models/lights + bool isModel = false; // false means light + bool enabled = false; + bool inUse = false; + bool isTitle = false; // if true, indicates a title row in UI + bool isUnexposed = false; // if true, indicates a component with unexposed models/lights + int resolution = 1024; + QString bakeMode; + }; + + BakeLightsDataModel(AbstractView *view); + ~BakeLightsDataModel() override; + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + QHash<int, QByteArray> roleNames() const override; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + + void reset(); + void apply(); + + ModelNode view3dNode() const { return m_view3dNode; } + +private: + QString commonPrefix(); + + QPointer<AbstractView> m_view; + QList<BakeData> m_dataList; + ModelNode m_view3dNode; +}; + +QDebug operator<<(QDebug debug, const BakeLightsDataModel::BakeData &data); + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp index 61731c578f8..12d2dd05528 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp @@ -2,18 +2,15 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "edit3dactions.h" -#include "edit3dview.h" -#include <viewmanager.h> -#include <nodeinstanceview.h> -#include <nodemetainfo.h> -#include <qmldesignerplugin.h> +#include "bakelights.h" +#include "edit3dview.h" +#include "nodemetainfo.h" +#include "qmldesignerconstants.h" #include "seekerslider.h" #include <utils/algorithm.h> -#include <QDebug> - namespace QmlDesigner { Edit3DActionTemplate::Edit3DActionTemplate(const QString &description, @@ -147,4 +144,33 @@ bool Edit3DParticleSeekerAction::isEnabled(const SelectionContext &) const return m_seeker->isEnabled(); } +Edit3DBakeLightsAction::Edit3DBakeLightsAction(const QIcon &icon, + Edit3DView *view, + SelectionContextOperation selectionAction) + : Edit3DAction(QmlDesigner::Constants::EDIT3D_BAKE_LIGHTS, + View3DActionType::Empty, + QCoreApplication::translate("BakeLights", "Bake Lights"), + QKeySequence(), + false, + false, + icon, + view, + selectionAction, + QCoreApplication::translate("BakeLights", "Bake lights for the current 3D scene.")) + , m_view(view) +{ + +} + +bool Edit3DBakeLightsAction::isVisible(const SelectionContext &) const +{ + return m_view->isBakingLightsSupported(); +} + +bool Edit3DBakeLightsAction::isEnabled(const SelectionContext &) const +{ + return m_view->isBakingLightsSupported() + && !BakeLights::resolveView3dId(m_view).isEmpty(); +} + } diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dactions.h b/src/plugins/qmldesigner/components/edit3d/edit3dactions.h index bad427a25be..4e8be202b9f 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dactions.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dactions.h @@ -122,4 +122,19 @@ private: SeekerSliderAction *m_seeker = nullptr; }; +class Edit3DBakeLightsAction : public Edit3DAction +{ +public: + Edit3DBakeLightsAction(const QIcon &icon, + Edit3DView *view, + SelectionContextOperation selectionAction); + +protected: + bool isVisible(const SelectionContext &) const override; + bool isEnabled(const SelectionContext &) const override; + +private: + Edit3DView *m_view = nullptr; +}; + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index d92e76362b9..10d62a5dc21 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -4,6 +4,7 @@ #include "edit3dview.h" #include "backgroundcolorselection.h" +#include "bakelights.h" #include "designeractionmanager.h" #include "designericons.h" #include "designersettings.h" @@ -20,11 +21,18 @@ #include "qmldesignerplugin.h" #include "qmlvisualnode.h" #include "seekerslider.h" +#include "theme.h" + +#include <model/modelutils.h> #include <coreplugin/icore.h> #include <coreplugin/messagebox.h> -#include <theme.h> +#include <projectexplorer/target.h> +#include <projectexplorer/kit.h> + +#include <qtsupport/qtkitinformation.h> + #include <utils/algorithm.h> #include <utils/qtcassert.h> #include <utils/stylehelper.h> @@ -204,6 +212,12 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState) m_particlesPlayAction->action()->setChecked(sceneState[particlesPlayKey].toBool()); else m_particlesPlayAction->action()->setChecked(true); + + // Selection context change updates visible and enabled states + SelectionContext selectionContext(this); + selectionContext.setUpdateMode(SelectionContext::UpdateMode::Fast); + if (m_bakeLightsAction) + m_bakeLightsAction->currentContextChanged(selectionContext); } void Edit3DView::modelAttached(Model *model) @@ -219,6 +233,13 @@ void Edit3DView::modelAttached(Model *model) edit3DWidget()->canvas()->busyIndicator()->show(); + m_isBakingLightsSupported = false; + ProjectExplorer::Target *target = QmlDesignerPlugin::instance()->currentDesignDocument()->currentTarget(); + if (target && target->kit()) { + if (QtSupport::QtVersion *qtVer = QtSupport::QtKitAspect::qtVersion(target->kit())) + m_isBakingLightsSupported = qtVer->qtVersion() >= QVersionNumber(6, 5, 0); + } + connect(model->metaInfo().itemLibraryInfo(), &ItemLibraryInfo::entriesChanged, this, &Edit3DView::onEntriesChanged, Qt::UniqueConnection); } @@ -279,6 +300,11 @@ void Edit3DView::handleEntriesChanged() void Edit3DView::modelAboutToBeDetached(Model *model) { + m_isBakingLightsSupported = false; + + if (m_bakeLights) + m_bakeLights->cancel(); + // Hide the canvas when model is detached (i.e. changing documents) if (edit3DWidget() && edit3DWidget()->canvas()) { m_canvasCache.insert(model, edit3DWidget()->canvas()->renderImage()); @@ -288,8 +314,8 @@ void Edit3DView::modelAboutToBeDetached(Model *model) AbstractView::modelAboutToBeDetached(model); } -void Edit3DView::importsChanged([[maybe_unused]] const QList<Import> &addedImports, - [[maybe_unused]] const QList<Import> &removedImports) +void Edit3DView::importsChanged([[maybe_unused]] const Imports &addedImports, + [[maybe_unused]] const Imports &removedImports) { checkImports(); } @@ -702,6 +728,17 @@ void Edit3DView::createEdit3DActions() m_seekerAction->action()->setEnabled(!m_particlesPlayAction->action()->isChecked()); }; + SelectionContextOperation bakeLightsTrigger = [this](const SelectionContext &) { + if (!m_isBakingLightsSupported) + return; + + // BakeLights cleans itself up when its dialog is closed + if (!m_bakeLights) + m_bakeLights = new BakeLights(this); + else + m_bakeLights->raiseDialog(); + }; + m_particleViewModeAction = new Edit3DAction( QmlDesigner::Constants::EDIT3D_PARTICLE_MODE, View3DActionType::Edit3DParticleModeToggle, @@ -804,6 +841,11 @@ void Edit3DView::createEdit3DActions() m_seekerAction = createSeekerSliderAction(); + m_bakeLightsAction = new Edit3DBakeLightsAction( + toolbarIcon(Theme::editLightOn_medium), //: TODO placeholder icon + this, + bakeLightsTrigger); + m_leftActions << m_selectionModeAction; m_leftActions << nullptr; // Null indicates separator m_leftActions << nullptr; // Second null after separator indicates an exclusive group @@ -830,6 +872,7 @@ void Edit3DView::createEdit3DActions() m_rightActions << nullptr; m_rightActions << m_seekerAction; m_rightActions << nullptr; + m_rightActions << m_bakeLightsAction; m_rightActions << m_resetAction; m_visibilityToggleActions << m_showGridAction; @@ -870,24 +913,20 @@ Edit3DAction *Edit3DView::edit3DAction(View3DActionType type) const return m_edit3DActions.value(type, nullptr).data(); } +Edit3DBakeLightsAction *Edit3DView::bakeLightsAction() const +{ + return m_bakeLightsAction; +} + void Edit3DView::addQuick3DImport() { DesignDocument *document = QmlDesignerPlugin::instance()->currentDesignDocument(); - if (document && !document->inFileComponentModelActive() && model()) { - const QList<Import> imports = model()->possibleImports(); - for (const auto &import : imports) { - if (import.url() == "QtQuick3D") { - if (!import.version().isEmpty() && import.majorVersion() >= 6) { - // Prefer empty version number in Qt6 and beyond - model()->changeImports({Import::createLibraryImport( - import.url(), {}, import.alias(), - import.importPaths())}, {}); - } else { - model()->changeImports({import}, {}); - } - return; - } - } + if (document && !document->inFileComponentModelActive() && model() + && Utils::addImportWithCheck( + "QtQuick3D", + [](const Import &import) { return !import.hasVersion() || import.majorVersion() >= 6; }, + model())) { + return; } Core::AsynchronousMessageBox::warning(tr("Failed to Add Import"), tr("Could not add QtQuick3D import to project.")); @@ -939,4 +978,9 @@ void Edit3DView::dropAsset(const QString &file, const QPointF &pos) emitView3DAction(View3DActionType::GetNodeAtPos, pos); } +bool Edit3DView::isBakingLightsSupported() const +{ + return m_isBakingLightsSupported; +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h index 2b746cf1af7..cd3e686d3bb 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h @@ -22,8 +22,10 @@ QT_END_NAMESPACE namespace QmlDesigner { +class BakeLights; class Edit3DWidget; class Edit3DAction; +class Edit3DBakeLightsAction; class Edit3DCameraAction; class QMLDESIGNERCOMPONENTS_EXPORT Edit3DView : public AbstractView @@ -42,7 +44,7 @@ public: void updateActiveScene3D(const QVariantMap &sceneState) override; void modelAttached(Model *model) override; void modelAboutToBeDetached(Model *model) override; - void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override; + void importsChanged(const Imports &addedImports, const Imports &removedImports) override; void customNotification(const AbstractView *view, const QString &identifier, const QList<ModelNode> &nodeList, const QList<QVariant> &data) override; void nodeAtPosReady(const ModelNode &modelNode, const QVector3D &pos3d) override; @@ -57,6 +59,7 @@ public: QVector<Edit3DAction *> visibilityToggleActions() const; QVector<Edit3DAction *> backgroundColorActions() const; Edit3DAction *edit3DAction(View3DActionType type) const; + Edit3DBakeLightsAction *bakeLightsAction() const; void addQuick3DImport(); void startContextMenu(const QPoint &pos); @@ -66,6 +69,8 @@ public: void dropComponent(const ItemLibraryEntry &entry, const QPointF &pos); void dropAsset(const QString &file, const QPointF &pos); + bool isBakingLightsSupported() const; + private slots: void onEntriesChanged(); @@ -122,6 +127,7 @@ private: Edit3DAction *m_visibilityTogglesAction = nullptr; Edit3DAction *m_backgrondColorMenuAction = nullptr; Edit3DAction *m_seekerAction = nullptr; + Edit3DBakeLightsAction *m_bakeLightsAction = nullptr; int particlemode; ModelCache<QImage> m_canvasCache; ModelNode m_droppedModelNode; @@ -130,6 +136,8 @@ private: NodeAtPosReqType m_nodeAtPosReqType; QPoint m_contextMenuPos; QTimer m_compressionTimer; + QPointer<BakeLights> m_bakeLights; + bool m_isBakingLightsSupported = false; friend class Edit3DAction; }; diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index 408584e166b..a217e396d9c 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -261,6 +261,12 @@ void Edit3DWidget::createContextMenu() view()->emitView3DAction(View3DActionType::AlignViewToCamera, true); }); + m_bakeLightsAction = m_contextMenu->addAction( + contextIcon(DesignerIcons::LightIcon), // TODO: placeholder icon + tr("Bake Lights"), [&] { + view()->bakeLightsAction()->action()->trigger(); + }); + m_contextMenu->addSeparator(); m_selectParentAction = m_contextMenu->addAction( @@ -466,6 +472,8 @@ void Edit3DWidget::showContextMenu(const QPoint &pos, const ModelNode &modelNode m_alignViewAction->setEnabled(isCamera); m_selectParentAction->setEnabled(selectionExcludingRoot); m_toggleGroupAction->setEnabled(true); + m_bakeLightsAction->setVisible(view()->bakeLightsAction()->action()->isVisible()); + m_bakeLightsAction->setEnabled(view()->bakeLightsAction()->action()->isEnabled()); m_contextMenu->popup(mapToGlobal(pos)); } diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h index 092abd313a5..eecd52345fa 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h @@ -75,6 +75,7 @@ private: QPointer<QMenu> m_visibilityTogglesMenu; QPointer<QMenu> m_backgroundColorMenu; QPointer<QMenu> m_contextMenu; + QPointer<QAction> m_bakeLightsAction; QPointer<QAction> m_editComponentAction; QPointer<QAction> m_editMaterialAction; QPointer<QAction> m_duplicateAction; diff --git a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp index 86951866058..9a51c9f3722 100644 --- a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp +++ b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp @@ -77,8 +77,6 @@ void DragTool::createQmlItemNode(const ItemLibraryEntry &itemLibraryEntry, const QmlItemNode &parentNode, const QPointF &scenePosition) { - MetaInfo metaInfo = MetaInfo::global(); - FormEditorItem *parentItem = scene()->itemForQmlItemNode(parentNode); const QPointF positonInItemSpace = parentItem->qmlItemNode().instanceSceneContentItemTransform().inverted().map(scenePosition); QPointF itemPos = positonInItemSpace; @@ -107,8 +105,6 @@ void DragTool::createQmlItemNodeFromImage(const QString &imagePath, const QPointF &scenePosition) { if (parentNode.isValid()) { - MetaInfo metaInfo = MetaInfo::global(); - FormEditorItem *parentItem = scene()->itemForQmlItemNode(parentNode); QPointF positonInItemSpace = parentItem->qmlItemNode().instanceSceneContentItemTransform().inverted().map(scenePosition); @@ -121,8 +117,6 @@ void DragTool::createQmlItemNodeFromFont(const QString &fontPath, const QPointF &scenePos) { if (parentNode.isValid()) { - MetaInfo metaInfo = MetaInfo::global(); - FormEditorItem *parentItem = scene()->itemForQmlItemNode(parentNode); QPointF positonInItemSpace = parentItem->qmlItemNode().instanceSceneContentItemTransform() .inverted().map(scenePos); diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp index 1f4d5262852..5661e4ff793 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp @@ -253,7 +253,7 @@ void FormEditorView::modelAboutToBeDetached(Model *model) AbstractView::modelAboutToBeDetached(model); } -void FormEditorView::importsChanged(const QList<Import> &/*addedImports*/, const QList<Import> &/*removedImports*/) +void FormEditorView::importsChanged(const Imports &/*addedImports*/, const Imports &/*removedImports*/) { reset(); } diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorview.h b/src/plugins/qmldesigner/components/formeditor/formeditorview.h index fde20427628..f97959acb37 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorview.h +++ b/src/plugins/qmldesigner/components/formeditor/formeditorview.h @@ -48,7 +48,7 @@ public: void modelAttached(Model *model) override; void modelAboutToBeDetached(Model *model) override; - void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override; + void importsChanged(const Imports &addedImports, const Imports &removedImports) override; void nodeCreated(const ModelNode &createdNode) override; void nodeAboutToBeRemoved(const ModelNode &removedNode) override; diff --git a/src/plugins/qmldesigner/components/formeditor/toolbox.cpp b/src/plugins/qmldesigner/components/formeditor/toolbox.cpp index acd0b7401b0..47a9503b2d3 100644 --- a/src/plugins/qmldesigner/components/formeditor/toolbox.cpp +++ b/src/plugins/qmldesigner/components/formeditor/toolbox.cpp @@ -20,6 +20,8 @@ ToolBox::ToolBox(QWidget *parentWidget) , m_rightToolBar(new QToolBar(QLatin1String("RightSidebar"), this)) { Utils::StyleHelper::setPanelWidget(this, false); + Utils::StyleHelper::setPanelWidgetSingleRow(this, false); + setFixedHeight(Theme::toolbarSize()); m_leftToolBar->setFloatable(true); m_leftToolBar->setMovable(true); @@ -29,8 +31,6 @@ ToolBox::ToolBox(QWidget *parentWidget) horizontalLayout->setContentsMargins(0, 0, 0, 0); horizontalLayout->setSpacing(0); - setFixedHeight(Theme::toolbarSize()); - Utils::StyleHelper::setPanelWidget(m_leftToolBar, false); Utils::StyleHelper::setPanelWidgetSingleRow(m_leftToolBar, false); m_leftToolBar->setFixedHeight(Theme::toolbarSize()); diff --git a/src/plugins/qmldesigner/components/integration/designdocumentview.cpp b/src/plugins/qmldesigner/components/integration/designdocumentview.cpp index a587580590d..4a1c92ad0aa 100644 --- a/src/plugins/qmldesigner/components/integration/designdocumentview.cpp +++ b/src/plugins/qmldesigner/components/integration/designdocumentview.cpp @@ -115,6 +115,7 @@ QString DesignDocumentView::toText() const QScopedPointer<RewriterView> rewriterView( new RewriterView(externalDependencies(), RewriterView::Amend)); rewriterView->setCheckSemanticErrors(false); + rewriterView->setPossibleImportsEnabled(false); rewriterView->setTextModifier(&modifier); outputModel->setRewriterView(rewriterView.data()); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryaddimportmodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryaddimportmodel.cpp index 5b46c1d12ef..41a9c6b5f4a 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryaddimportmodel.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryaddimportmodel.cpp @@ -56,14 +56,14 @@ QHash<int, QByteArray> ItemLibraryAddImportModel::roleNames() const return m_roleNames; } -void ItemLibraryAddImportModel::update(const QList<Import> &possibleImports) +void ItemLibraryAddImportModel::update(const Imports &possibleImports) { beginResetModel(); m_importList.clear(); const DesignerMcuManager &mcuManager = DesignerMcuManager::instance(); const bool isQtForMCUs = mcuManager.isMCUProject(); - QList<Import> filteredImports; + Imports filteredImports; if (isQtForMCUs) { const QStringList mcuAllowedList = mcuManager.allowedImports(); const QStringList mcuBannedList = mcuManager.bannedImports(); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryaddimportmodel.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryaddimportmodel.h index 5785b67301a..2989fd51642 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryaddimportmodel.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryaddimportmodel.h @@ -24,7 +24,7 @@ public: QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QHash<int, QByteArray> roleNames() const override; - void update(const QList<Import> &possibleImports); + void update(const Imports &possibleImports); void setSearchText(const QString &searchText); Import getImportAt(int index) const; @@ -33,7 +33,7 @@ public: private: QString m_searchText; - QList<Import> m_importList; + Imports m_importList; QSet<QString> m_importFilterList; QHash<int, QByteArray> m_roleNames; QSet<QString> m_priorityImports; diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp index 635a248152d..d141b88dc85 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp @@ -14,6 +14,8 @@ #include "rewritingexception.h" #include "viewmanager.h" +#include <model/modelutils.h> + #include <qmljs/qmljsmodelmanagerinterface.h> #include <utils/algorithm.h> @@ -300,7 +302,7 @@ bool ItemLibraryAssetImporter::preParseQuick3DAsset(const QString &file, ParseDa if (exitVal == QDialog::Accepted) overwriteFiles = dlg.selectedFiles(); if (!overwriteFiles.isEmpty()) { - overwriteFiles.append(Utils::toList(alwaysOverwrite)); + overwriteFiles.append(::Utils::toList(alwaysOverwrite)); m_overwrittenImports.insert(pd.targetDirPath, overwriteFiles); } else { addWarning(tr("No files selected for overwrite, skipping import: \"%1\".").arg(pd.assetName)); @@ -359,9 +361,8 @@ void ItemLibraryAssetImporter::postParseQuick3DAsset(ParseData &pd) qmlInfo.append("."); qmlInfo.append(pd.assetName); qmlInfo.append('\n'); - m_requiredImports.append(Import::createLibraryImport( - QStringLiteral("%1.%2").arg(pd.targetDir.dirName(), - pd.assetName), version)); + m_requiredImports.append( + QStringLiteral("%1.%2").arg(pd.targetDir.dirName(), pd.assetName)); while (qmlIt.hasNext()) { qmlIt.next(); QFileInfo fi = QFileInfo(qmlIt.filePath()); @@ -417,11 +418,8 @@ void ItemLibraryAssetImporter::postParseQuick3DAsset(ParseData &pd) } // Add quick3D import unless it is already added - if (impVersionMajor > 0 - && m_requiredImports.first().url() != "QtQuick3D") { - m_requiredImports.prepend(Import::createLibraryImport( - "QtQuick3D", impVersionStr)); - } + if (impVersionMajor > 0 && m_requiredImports.first() != "QtQuick3D") + m_requiredImports.prepend("QtQuick3D"); } if (impVersionMajor > 0 && impVersionMajor < 6) { pd.iconFile = iconFileName; @@ -683,66 +681,42 @@ void ItemLibraryAssetImporter::finalizeQuick3DImport() QFuture<void> result; if (modelManager) { QmlJS::PathsAndLanguages pathToScan; - pathToScan.maybeInsert(Utils::FilePath::fromString(m_importPath)); - result = Utils::asyncRun(&QmlJS::ModelManagerInterface::importScan, - modelManager->workingCopy(), pathToScan, - modelManager, true, true, true); + pathToScan.maybeInsert(::Utils::FilePath::fromString(m_importPath)); + result = ::Utils::asyncRun(&QmlJS::ModelManagerInterface::importScan, + modelManager->workingCopy(), + pathToScan, + modelManager, + true, + true, + true); } // First we have to wait a while to ensure qmljs detects new files and updates its - // internal model. Then we make a non-change to the document to trigger qmljs snapshot - // update. There is an inbuilt delay before rewriter change actually updates the data - // model, so we need to wait for another moment to allow the change to take effect. - // Otherwise subsequent subcomponent manager update won't detect new imports properly. + // internal model. Then we force amend on rewriter to trigger qmljs snapshot update. QTimer *timer = new QTimer(parent()); static int counter; counter = 0; timer->callOnTimeout([this, timer, progressTitle, model, result]() { if (!isCancelled()) { - notifyProgress(++counter, progressTitle); - if (counter < 50) { + notifyProgress(++counter * 2, progressTitle); + if (counter < 49) { if (result.isCanceled() || result.isFinished()) - counter = 49; // skip to next step - } else if (counter == 50) { + counter = 48; // skip to next step + } else if (counter == 49) { QmlDesignerPlugin::instance()->documentManager().resetPossibleImports(); - model->rewriterView()->textModifier()->replace(0, 0, {}); - } else if (counter < 100) { + model->rewriterView()->forceAmend(); try { - const QList<Import> posImports = model->possibleImports(); - const QList<Import> currentImports = model->imports(); - QList<Import> newImportsToAdd; - - for (auto &imp : std::as_const(m_requiredImports)) { - const bool isPos = Utils::contains(posImports, [imp](const Import &posImp) { - return posImp.url() == imp.url(); - }); - const bool isCur = Utils::contains(currentImports, [imp](const Import &curImp) { - return curImp.url() == imp.url(); - }); - if (!(isPos || isCur)) - return; - // Check again with 'contains' to ensure we insert latest version - if (!currentImports.contains(imp)) - newImportsToAdd.append(imp); - } - if (counter == 99) + RewriterTransaction transaction = model->rewriterView()->beginRewriterTransaction( + QByteArrayLiteral("ItemLibraryAssetImporter::finalizeQuick3DImport")); + bool success = Utils::addImportsWithCheck(m_requiredImports, model); + if (!success) addError(tr("Failed to insert import statement into qml document.")); - else - counter = 99; - if (!newImportsToAdd.isEmpty()) { - RewriterTransaction transaction - = model->rewriterView()->beginRewriterTransaction( - QByteArrayLiteral("ItemLibraryAssetImporter::finalizeQuick3DImport")); - - model->changeImports(newImportsToAdd, {}); - transaction.commit(); - } + transaction.commit(); } catch (const RewritingException &e) { addError(tr("Failed to update imports: %1").arg(e.description())); - counter = 99; } - } else if (counter >= 100) { + } else if (counter >= 50) { if (!m_overwrittenImports.isEmpty()) model->rewriterView()->emitCustomNotification("asset_import_update"); timer->stop(); @@ -752,7 +726,7 @@ void ItemLibraryAssetImporter::finalizeQuick3DImport() timer->stop(); } }); - timer->start(50); + timer->start(100); } else { notifyFinished(); } diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h index c7952e9b8f6..a53f0c9de1b 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h @@ -107,7 +107,7 @@ private: int m_currentImportId = 0; QHash<int, ParseData> m_parseData; QString m_progressTitle; - QList<Import> m_requiredImports; + QStringList m_requiredImports; QList<int> m_puppetQueue; }; } // QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.cpp index cdaa3e28a9c..304aa5716ff 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.cpp @@ -20,7 +20,7 @@ QQuickImageResponse *ItemLibraryIconImageProvider::requestImageResponse(const QS Utils::StyleHelper::dpiSpecificImageFile(":/ItemLibrary/images/item-default-icon.png")}); m_cache.requestSmallImage( - id, + Utils::PathString{id}, [response = QPointer<ImageCacheImageResponse>(response.get())](const QImage &image) { QMetaObject::invokeMethod( response, diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp index 96b95f7949a..21f2e6aa55d 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp @@ -304,28 +304,6 @@ Import ItemLibraryModel::entryToImport(const ItemLibraryEntry &entry) } -// Returns true if first import version is higher or equal to second import version -static bool compareVersions(const QString &version1, const QString &version2) -{ - if (version2.isEmpty() || version1 == version2) - return true; - const QStringList version1List = version1.split(QLatin1Char('.')); - const QStringList version2List = version2.split(QLatin1Char('.')); - if (version1List.count() == 2 && version2List.count() == 2) { - int major1 = version1List.constFirst().toInt(); - int major2 = version2List.constFirst().toInt(); - if (major1 > major2) { - return true; - } else if (major1 == major2) { - int minor1 = version1List.constLast().toInt(); - int minor2 = version2List.constLast().toInt(); - if (minor1 >= minor2) - return true; - } - } - return false; -} - void ItemLibraryModel::update(ItemLibraryInfo *itemLibraryInfo, Model *model) { if (!model) @@ -343,7 +321,7 @@ void ItemLibraryModel::update(ItemLibraryInfo *itemLibraryInfo, Model *model) materialBundlePrefix.append(".MaterialBundle"); // create import sections - const QList<Import> usedImports = model->usedImports(); + const Imports usedImports = model->usedImports(); QHash<QString, ItemLibraryImport *> importHash; for (const Import &import : model->imports()) { if (import.url() != projectName) { @@ -363,7 +341,7 @@ void ItemLibraryModel::update(ItemLibraryInfo *itemLibraryInfo, Model *model) addNew = false; // add only 1 Quick3DAssets import section } else if (oldImport && oldImport->importEntry().url() == import.url()) { // Retain the higher version if multiples exist - if (compareVersions(oldImport->importEntry().version(), import.version())) + if (oldImport->importEntry().toVersion() >= import.toVersion() || import.hasVersion()) addNew = false; else delete oldImport; @@ -550,7 +528,7 @@ ItemLibraryImport *ItemLibraryModel::importByUrl(const QString &importUrl) const return nullptr; } -void ItemLibraryModel::updateUsedImports(const QList<Import> &usedImports) +void ItemLibraryModel::updateUsedImports(const Imports &usedImports) { // imports in the excludeList are not marked used and thus can always be removed even when in use. const QList<QString> excludeList = {"SimulinkConnector"}; diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h index 4e9c5809c12..212ddf8e040 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h @@ -38,7 +38,7 @@ public: ItemLibraryImport *importByUrl(const QString &importName) const; void update(ItemLibraryInfo *itemLibraryInfo, Model *model); - void updateUsedImports(const QList<Import> &usedImports); + void updateUsedImports(const Imports &usedImports); QMimeData *getMimeData(const ItemLibraryEntry &itemLibraryEntry); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp index b5a0567114a..d370f6d4547 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp @@ -62,7 +62,7 @@ void ItemLibraryView::modelAttached(Model *model) m_widget->setModel(model); updateImports(); if (model) - m_widget->updatePossibleImports(model->possibleImports()); + m_widget->updatePossibleImports(difference(model->possibleImports(), model->imports())); m_hasErrors = !rewriterView()->errors().isEmpty(); m_widget->setFlowMode(QmlItemNode(rootModelNode()).isFlowView()); } @@ -74,13 +74,14 @@ void ItemLibraryView::modelAboutToBeDetached(Model *model) m_widget->setModel(nullptr); } -void ItemLibraryView::importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) +void ItemLibraryView::importsChanged(const Imports &addedImports, const Imports &removedImports) { DesignDocument *document = QmlDesignerPlugin::instance()->currentDesignDocument(); for (const auto &import : addedImports) document->addSubcomponentManagerImport(import); updateImports(); + m_widget->updatePossibleImports(model()->possibleImports()); // TODO: generalize the logic below to allow adding/removing any Qml component when its import is added/removed bool simulinkImportAdded = std::any_of(addedImports.cbegin(), addedImports.cend(), [](const Import &import) { @@ -111,7 +112,7 @@ void ItemLibraryView::importsChanged(const QList<Import> &addedImports, const QL } } -void ItemLibraryView::possibleImportsChanged(const QList<Import> &possibleImports) +void ItemLibraryView::possibleImportsChanged(const Imports &possibleImports) { DesignDocument *document = QmlDesignerPlugin::instance()->currentDesignDocument(); for (const auto &import : possibleImports) @@ -120,7 +121,7 @@ void ItemLibraryView::possibleImportsChanged(const QList<Import> &possibleImport m_widget->updatePossibleImports(possibleImports); } -void ItemLibraryView::usedImportsChanged(const QList<Import> &usedImports) +void ItemLibraryView::usedImportsChanged(const Imports &usedImports) { m_widget->updateUsedImports(usedImports); } diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.h index e28f39318c0..43287ae0e5a 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.h @@ -26,9 +26,9 @@ public: // AbstractView void modelAttached(Model *model) override; void modelAboutToBeDetached(Model *model) override; - void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override; - void possibleImportsChanged(const QList<Import> &possibleImports) override; - void usedImportsChanged(const QList<Import> &usedImports) override; + void importsChanged(const Imports &addedImports, const Imports &removedImports) override; + void possibleImportsChanged(const Imports &possibleImports) override; + void usedImportsChanged(const Imports &usedImports) override; void documentMessagesChanged(const QList<DocumentMessage> &errors, const QList<DocumentMessage> &warnings) override; void updateImport3DSupport(const QVariantMap &supportMap) override; void customNotification(const AbstractView *view, const QString &identifier, diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp index 549f9e69c10..f98284f61d6 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp @@ -18,6 +18,7 @@ #include <itemlibrarymodel.h> #include <metainfo.h> #include <model.h> +#include <model/modelutils.h> #include <rewritingexception.h> #include <qmldesignerconstants.h> #include <qmldesignerplugin.h> @@ -59,7 +60,7 @@ namespace QmlDesigner { static QString propertyEditorResourcesPath() { #ifdef SHARE_QML_PATH - if (Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) + if (::Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) return QLatin1String(SHARE_QML_PATH) + "/propertyEditorQmlSources"; #endif return Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources").toString(); @@ -80,26 +81,16 @@ bool ItemLibraryWidget::eventFilter(QObject *obj, QEvent *event) ItemLibraryEntry entry = m_itemToDrag.value<ItemLibraryEntry>(); // For drag to be handled correctly, we must have the component properly imported // beforehand, so we import the module immediately when the drag starts - if (!entry.requiredImport().isEmpty()) { - // We don't know if required import is library of file import, so try both. - Import libImport = Import::createLibraryImport(entry.requiredImport()); - Import fileImport = Import::createFileImport(entry.requiredImport()); - if (!m_model->hasImport(libImport, true, true) - && !m_model->hasImport(fileImport, true, true)) { - const QList<Import> possImports = m_model->possibleImports(); - for (const auto &possImport : possImports) { - if ((!possImport.url().isEmpty() && possImport.url() == libImport.url()) - || (!possImport.file().isEmpty() && possImport.file() == fileImport.file())) { - m_model->changeImports({possImport}, {}); - break; - } - } - } + if (!entry.requiredImport().isEmpty() + && !Utils::addImportWithCheck(entry.requiredImport(), m_model)) { + qWarning() << __FUNCTION__ << "Required import adding failed:" + << entry.requiredImport(); } if (model) { model->startDrag(m_itemLibraryModel->getMimeData(entry), - Utils::StyleHelper::dpiSpecificImageFile(entry.libraryEntryIconPath())); + ::Utils::StyleHelper::dpiSpecificImageFile( + entry.libraryEntryIconPath())); } m_itemToDrag = {}; @@ -154,7 +145,7 @@ ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache) updateSearch(); setStyleSheet(Theme::replaceCssColors( - QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css")))); + QString::fromUtf8(::Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css")))); m_qmlSourceUpdateShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F5), this); connect(m_qmlSourceUpdateShortcut, &QShortcut::activated, this, &ItemLibraryWidget::reloadQmlSource); @@ -176,10 +167,9 @@ ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache) {"itemLibraryIconHeight", m_itemIconSize.height()}, {"rootView", QVariant::fromValue(this)}, {"widthLimit", HORIZONTAL_LAYOUT_WIDTH_LIMIT}, - {"highlightColor", Utils::StyleHelper::notTooBrightHighlightColor()}, + {"highlightColor", ::Utils::StyleHelper::notTooBrightHighlightColor()}, {"tooltipBackend", QVariant::fromValue(m_previewTooltipBackend.get())}}); - reloadQmlSource(); } @@ -246,7 +236,7 @@ void ItemLibraryWidget::handleAddImport(int index) + importStr); } - QList<Import> imports; + Imports imports; const QString dependency = getDependencyImport(import); auto document = QmlDesignerPlugin::instance()->currentDesignDocument(); @@ -303,7 +293,7 @@ void ItemLibraryWidget::setModel(Model *model) QString ItemLibraryWidget::qmlSourcesPath() { #ifdef SHARE_QML_PATH - if (Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) + if (::Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) return QLatin1String(SHARE_QML_PATH) + "/itemLibraryQmlSources"; #endif return Core::ICore::resourcePath("qmldesigner/itemLibraryQmlSources").toString(); @@ -346,13 +336,13 @@ void ItemLibraryWidget::updateModel() updateSearch(); } -void ItemLibraryWidget::updatePossibleImports(const QList<Import> &possibleImports) +void ItemLibraryWidget::updatePossibleImports(const Imports &possibleImports) { - m_addModuleModel->update(possibleImports); + m_addModuleModel->update(difference(possibleImports, m_model->imports())); delayedUpdateModel(); } -void ItemLibraryWidget::updateUsedImports(const QList<Import> &usedImports) +void ItemLibraryWidget::updateUsedImports(const Imports &usedImports) { m_itemLibraryModel->updateUsedImports(usedImports); } @@ -368,7 +358,7 @@ void ItemLibraryWidget::handlePriorityImportsChanged() { if (!m_itemLibraryInfo.isNull()) { m_addModuleModel->setPriorityImports(m_itemLibraryInfo->priorityImports()); - m_addModuleModel->update(m_model->possibleImports()); + m_addModuleModel->update(difference(m_model->possibleImports(), m_model->imports())); } } diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h index 394b3ea6103..38cd782d097 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h @@ -60,8 +60,8 @@ public: void switchToComponentsView(); void delayedUpdateModel(); void updateModel(); - void updatePossibleImports(const QList<Import> &possibleImports); - void updateUsedImports(const QList<Import> &usedImports); + void updatePossibleImports(const Imports &possibleImports); + void updateUsedImports(const Imports &usedImports); void setModel(Model *model); void setFlowMode(bool b); diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp index 10b6660db57..4ed241abf98 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp @@ -51,6 +51,10 @@ QVariant MaterialBrowserTexturesModel::data(const QModelIndex &index, int role) if (role == RoleTexInternalId) return m_textureList.at(index.row()).internalId(); + if (role == RoleTexId) { + return m_textureList.at(index.row()).id(); + } + if (role == RoleTexToolTip) { QString source = data(index, RoleTexSource).toString(); // absolute path if (source.isEmpty()) @@ -88,6 +92,7 @@ QHash<int, QByteArray> MaterialBrowserTexturesModel::roleNames() const static const QHash<int, QByteArray> roles { {RoleTexHasDynamicProps, "hasDynamicProperties"}, {RoleTexInternalId, "textureInternalId"}, + {RoleTexId, "textureId"}, {RoleTexSource, "textureSource"}, {RoleTexToolTip, "textureToolTip"}, {RoleTexVisible, "textureVisible"} @@ -294,6 +299,21 @@ void MaterialBrowserTexturesModel::deleteTexture(int idx) } } +void MaterialBrowserTexturesModel::setTextureId(int idx, const QString &newId) +{ + if (!isValidIndex(idx)) + return; + + ModelNode node = m_textureList[idx]; + if (!node.isValid()) + return; + + if (node.id() != newId) { + node.setIdWithRefactoring(newId); + emit dataChanged(index(idx, 0), index(idx, 0), {RoleTexId}); + } +} + void MaterialBrowserTexturesModel::applyToSelectedMaterial(qint64 internalId) { int idx = m_textureIndexHash.value(internalId); diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.h index 6d66ad13ec5..9cb7c5ac18f 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.h +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.h @@ -55,6 +55,7 @@ public: Q_INVOKABLE void addNewTexture(); Q_INVOKABLE void duplicateTexture(int idx); Q_INVOKABLE void deleteTexture(int idx); + Q_INVOKABLE void setTextureId(int idx, const QString &newId); Q_INVOKABLE void applyToSelectedMaterial(qint64 internalId); Q_INVOKABLE void applyToSelectedModel(qint64 internalId); Q_INVOKABLE void openTextureEditor(); @@ -91,6 +92,7 @@ private: enum { RoleTexHasDynamicProps = Qt::UserRole + 1, RoleTexInternalId, + RoleTexId, RoleTexSource, RoleTexToolTip, RoleTexVisible diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp index 5265c70683d..5349fc26ce7 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp @@ -264,12 +264,17 @@ void MaterialBrowserView::refreshModel(bool updateImages) m_widget->materialBrowserTexturesModel()->setTextures(textures); m_widget->materialBrowserModel()->setHasMaterialLibrary(matLib.isValid()); - if (updateImages) { - for (const ModelNode &node : std::as_const(materials)) - m_previewRequests.insert(node); - if (!m_previewRequests.isEmpty()) - m_previewTimer.start(0); - } + if (updateImages) + updateMaterialsPreview(); +} + +void MaterialBrowserView::updateMaterialsPreview() +{ + const QList<ModelNode> materials = m_widget->materialBrowserModel()->materials(); + for (const ModelNode &node : materials) + m_previewRequests.insert(node); + if (!m_previewRequests.isEmpty()) + m_previewTimer.start(0); } bool MaterialBrowserView::isMaterial(const ModelNode &node) const @@ -469,8 +474,8 @@ ModelNode MaterialBrowserView::getMaterialOfModel(const ModelNode &model, int id return mat; } -void MaterialBrowserView::importsChanged([[maybe_unused]] const QList<Import> &addedImports, - [[maybe_unused]] const QList<Import> &removedImports) +void MaterialBrowserView::importsChanged([[maybe_unused]] const Imports &addedImports, + [[maybe_unused]] const Imports &removedImports) { bool hasQuick3DImport = model()->hasImport("QtQuick3D"); @@ -541,6 +546,7 @@ void MaterialBrowserView::active3DSceneChanged(qint32 sceneId) void MaterialBrowserView::currentStateChanged([[maybe_unused]] const ModelNode &node) { m_widget->materialBrowserTexturesModel()->updateAllTexturesSources(); + updateMaterialsPreview(); } void MaterialBrowserView::instancesCompleted(const QVector<ModelNode> &completedNodeList) diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h index 811d6679b63..297487ae66f 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h @@ -44,7 +44,7 @@ public: void nodeAboutToBeRemoved(const ModelNode &removedNode) override; void nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty, PropertyChangeFlags propertyChange) override; - void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override; + void importsChanged(const Imports &addedImports, const Imports &removedImports) override; void customNotification(const AbstractView *view, const QString &identifier, const QList<ModelNode> &nodeList, const QList<QVariant> &data) override; void instancesCompleted(const QVector<ModelNode> &completedNodeList) override; @@ -66,6 +66,7 @@ protected: private: void refreshModel(bool updateImages); + void updateMaterialsPreview(); bool isMaterial(const ModelNode &node) const; bool isTexture(const ModelNode &node) const; void loadPropertyGroups(); diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp index 47d47985ebd..96e01072904 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp @@ -38,6 +38,20 @@ #include <QShortcut> #include <QColorDialog> +namespace { +QSize maxSize(const std::initializer_list<QSize> &sizeList) +{ + QSize result; + for (const QSize &size : sizeList) { + if (size.width() > result.width()) + result.setWidth(size.width()); + if (size.height() > result.height()) + result.setHeight(size.height()); + } + return result; +} +} + namespace QmlDesigner { MaterialEditorView::MaterialEditorView(ExternalDependenciesInterface &externalDependencies) @@ -64,7 +78,6 @@ MaterialEditorView::MaterialEditorView(ExternalDependenciesInterface &externalDe m_typeUpdateTimer.setInterval(500); connect(&m_typeUpdateTimer, &QTimer::timeout, this, &MaterialEditorView::updatePossibleTypes); - m_stackedWidget->setMinimumWidth(250); QmlDesignerPlugin::trackWidgetFocusTime(m_stackedWidget, Constants::EVENT_MATERIALEDITOR_TIME); MaterialEditorDynamicPropertiesProxyModel::registerDeclarativeType(); @@ -594,6 +607,14 @@ void MaterialEditorView::setupQmlBackend() initPreviewData(); m_stackedWidget->setCurrentWidget(m_qmlBackEnd->widget()); + if (m_qmlBackEnd->widget()) { + m_stackedWidget->setMinimumSize(maxSize({m_qmlBackEnd->widget()->sizeHint(), + m_qmlBackEnd->widget()->initialSize(), + m_qmlBackEnd->widget()->minimumSizeHint(), + m_qmlBackEnd->widget()->minimumSize()})); + } else { + m_stackedWidget->setMinimumSize({400, 300}); + } } void MaterialEditorView::commitVariantValueToModel(const PropertyName &propertyName, const QVariant &value) @@ -974,8 +995,8 @@ void MaterialEditorView::modelNodePreviewPixmapChanged(const ModelNode &node, co m_qmlBackEnd->updateMaterialPreview(pixmap); } -void MaterialEditorView::importsChanged([[maybe_unused]] const QList<Import> &addedImports, - [[maybe_unused]] const QList<Import> &removedImports) +void MaterialEditorView::importsChanged([[maybe_unused]] const Imports &addedImports, + [[maybe_unused]] const Imports &removedImports) { m_hasQuick3DImport = model()->hasImport("QtQuick3D"); m_qmlBackEnd->contextObject()->setHasQuick3DImport(m_hasQuick3DImport); diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h index d47d030e57b..c201742bd51 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h @@ -55,7 +55,7 @@ public: void nodeTypeChanged(const ModelNode& node, const TypeName &type, int majorVersion, int minorVersion) override; void rootNodeTypeChanged(const QString &type, int majorVersion, int minorVersion) override; void modelNodePreviewPixmapChanged(const ModelNode &node, const QPixmap &pixmap) override; - void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override; + void importsChanged(const Imports &addedImports, const Imports &removedImports) override; void customNotification(const AbstractView *view, const QString &identifier, const QList<ModelNode> &nodeList, const QList<QVariant> &data) override; void nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, diff --git a/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp b/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp index 35078859de0..91c300e87a2 100644 --- a/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp +++ b/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp @@ -90,7 +90,7 @@ ChooseFromPropertyListFilter::ChooseFromPropertyListFilter(const NodeMetaInfo &i } else if (insertInfo.isQtQuick3DParticles3DParticle3D()) { if (parentInfo.isQtQuick3DParticles3DParticleEmitter3D()) propertyList.append("particle"); - } else if (insertInfo.isQuick3DParticleAbstractShape()) { + } else if (insertInfo.isQtQuick3DParticleAbstractShape()) { if (parentInfo.isQtQuick3DParticles3DParticleEmitter3D() || parentInfo.isQtQuick3DParticles3DAttractor3D()) propertyList.append("shape"); diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp index 62868290285..16dd2242863 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp @@ -9,23 +9,24 @@ #include "qmldesignerplugin.h" #include "assetslibrarywidget.h" +#include <abstractview.h> #include <bindingproperty.h> +#include <coreplugin/icore.h> +#include <designeractionmanager.h> #include <designersettings.h> +#include <import.h> +#include <invalididexception.h> +#include <materialutils.h> +#include <metainfo.h> +#include <model/modelutils.h> #include <nodeabstractproperty.h> #include <nodehints.h> #include <nodelistproperty.h> #include <nodeproperty.h> -#include <variantproperty.h> -#include <metainfo.h> -#include <materialutils.h> -#include <abstractview.h> -#include <invalididexception.h> #include <rewritingexception.h> +#include <variantproperty.h> #include <qmldesignerconstants.h> #include <qmlitemnode.h> -#include <designeractionmanager.h> -#include <import.h> -#include <coreplugin/icore.h> #include <qmlprojectmanager/qmlproject.h> @@ -210,7 +211,7 @@ QVariant NavigatorTreeModel::data(const QModelIndex &index, int role) const return modelNode.displayName(); } else if (role == Qt::DecorationRole) { if (currentQmlObjectNode.hasError()) - return Utils::Icons::WARNING.icon(); + return ::Utils::Icons::WARNING.icon(); return modelNode.typeIcon(); @@ -314,14 +315,14 @@ QList<ModelNode> NavigatorTreeModel::filteredList(const NodeListProperty &proper if (m_nameFilter.isEmpty()) { nameFilteredList = propertyNodes; } else { - nameFilteredList.append(Utils::filtered(propertyNodes, [&] (const ModelNode &arg){ + nameFilteredList.append(::Utils::filtered(propertyNodes, [&](const ModelNode &arg) { const bool value = m_nameFilteredList.contains(arg); return value; })); } if (filter) { - list.append(Utils::filtered(nameFilteredList, [] (const ModelNode &arg) { + list.append(::Utils::filtered(nameFilteredList, [](const ModelNode &arg) { const bool value = (QmlItemNode::isValidQmlItemNode(arg) || NodeHints::fromModelNode(arg).visibleInNavigator()) && arg.id() != Constants::MATERIAL_LIB_ID; return value; @@ -899,17 +900,8 @@ ModelNode NavigatorTreeModel::handleItemLibraryFontDrop(const QString &fontFamil void NavigatorTreeModel::addImport(const QString &importName) { - Import import = Import::createLibraryImport(importName); - if (!m_view->model()->hasImport(import, true, true)) { - const QList<Import> possImports = m_view->model()->possibleImports(); - for (const auto &possImport : possImports) { - if (possImport.url() == import.url()) { - import = possImport; - m_view->model()->changeImports({import}, {}); - break; - } - } - } + if (!Utils::addImportWithCheck(importName, m_view->model())) + qWarning() << __FUNCTION__ << "Adding import failed:" << importName; } bool QmlDesigner::NavigatorTreeModel::moveNodeToParent(const NodeAbstractProperty &targetProperty, @@ -1268,12 +1260,12 @@ static QList<ModelNode> collectParents(const QList<ModelNode> &modelNodes) } } - return Utils::toList(parents); + return ::Utils::toList(parents); } QList<QPersistentModelIndex> NavigatorTreeModel::nodesToPersistentIndex(const QList<ModelNode> &modelNodes) { - return Utils::transform(modelNodes, [this](const ModelNode &modelNode) { + return ::Utils::transform(modelNodes, [this](const ModelNode &modelNode) { return QPersistentModelIndex(indexForModelNode(modelNode)); }); } diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp index 38e6b9ada75..f1a00a36c7c 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp @@ -228,7 +228,7 @@ void NavigatorView::modelAboutToBeDetached(Model *model) AbstractView::modelAboutToBeDetached(model); } -void NavigatorView::importsChanged(const QList<Import> &/*addedImports*/, const QList<Import> &/*removedImports*/) +void NavigatorView::importsChanged(const Imports &/*addedImports*/, const Imports &/*removedImports*/) { treeWidget()->update(); } diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.h b/src/plugins/qmldesigner/components/navigator/navigatorview.h index f5fa81591d9..662b749ecec 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorview.h +++ b/src/plugins/qmldesigner/components/navigator/navigatorview.h @@ -50,7 +50,7 @@ public: void modelAttached(Model *model) override; void modelAboutToBeDetached(Model *model) override; - void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override; + void importsChanged(const Imports &addedImports, const Imports &removedImports) override; void nodeAboutToBeRemoved(const ModelNode &removedNode) override; void nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty, PropertyChangeFlags propertyChange) override; diff --git a/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp b/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp index 550ff8fcaf9..16328ae532f 100644 --- a/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp +++ b/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp @@ -32,8 +32,9 @@ void PreviewTooltipBackend::showTooltip() m_tooltip->setInfo(m_info); m_cache.requestImage( - m_path, - [tooltip = QPointer<PreviewImageTooltip>(m_tooltip.get()), scaleImage = m_scaleImage](const QImage &image) { + Utils::PathString{m_path}, + [tooltip = QPointer<PreviewImageTooltip>(m_tooltip.get()), + scaleImage = m_scaleImage](const QImage &image) { QMetaObject::invokeMethod(tooltip, [tooltip, image, scaleImage] { if (tooltip) { tooltip->setImage(image, scaleImage); @@ -42,7 +43,7 @@ void PreviewTooltipBackend::showTooltip() }); }, [](auto) {}, - m_extraId, + Utils::PathString{m_extraId}, m_auxiliaryData); reposition(); diff --git a/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp b/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp index 75b9f34cefd..a81560f5662 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp @@ -29,14 +29,13 @@ GradientModel::GradientModel(QObject *parent) : int GradientModel::rowCount(const QModelIndex & /*parent*/) const { - if (m_itemNode.isValid()) { - if (m_itemNode.modelNode().hasNodeProperty(gradientPropertyName().toUtf8())) { - QmlDesigner::ModelNode gradientNode = - m_itemNode.modelNode().nodeProperty(gradientPropertyName().toUtf8()).modelNode(); + if (m_itemNode.modelNode().hasNodeProperty(gradientPropertyName().toUtf8())) { + QmlDesigner::ModelNode gradientNode = m_itemNode.modelNode() + .nodeProperty(gradientPropertyName().toUtf8()) + .modelNode(); - if (gradientNode.hasNodeListProperty("stops")) - return gradientNode.nodeListProperty("stops").toModelNodeList().count(); - } + if (gradientNode.hasNodeListProperty("stops")) + return gradientNode.nodeListProperty("stops").count(); } return 0; diff --git a/src/plugins/qmldesigner/components/propertyeditor/itemfiltermodel.cpp b/src/plugins/qmldesigner/components/propertyeditor/itemfiltermodel.cpp index d52e47c3678..c5d9a23d7ea 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/itemfiltermodel.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/itemfiltermodel.cpp @@ -7,7 +7,7 @@ #include <model.h> #include <nodemetainfo.h> #include <qmlmodelnodeproxy.h> -#include "variantproperty.h" +#include <variantproperty.h> #include <QFileDialog> #include <QDirIterator> @@ -15,24 +15,14 @@ using namespace QmlDesigner; -QHash<int, QByteArray> ItemFilterModel::m_roles; - ItemFilterModel::ItemFilterModel(QObject *parent) : QAbstractListModel(parent) , m_typeFilter("QtQuick.Item") , m_selectionOnly(false) -{ - if (m_roles.empty()) { - m_roles = QAbstractListModel::roleNames(); - QMetaEnum roleEnum = QMetaEnum::fromType<Roles>(); - for (int i = 0; i < roleEnum.keyCount(); i++) - m_roles.insert(roleEnum.value(i), roleEnum.key(i)); - } -} +{} void ItemFilterModel::setModelNodeBackend(const QVariant &modelNodeBackend) { - auto modelNodeBackendObject = modelNodeBackend.value<QObject*>(); const auto backendObjectCasted = @@ -47,18 +37,28 @@ void ItemFilterModel::setModelNodeBackend(const QVariant &modelNodeBackend) void ItemFilterModel::setTypeFilter(const QString &filter) { - if (m_typeFilter != filter) { - m_typeFilter = filter; - setupModel(); - } + if (m_typeFilter == filter) + return; + + m_typeFilter = filter; + setupModel(); + emit typeFilterChanged(); } void ItemFilterModel::setSelectionOnly(bool value) { - if (m_selectionOnly != value) { - m_selectionOnly = value; - setupModel(); - } + if (m_selectionOnly == value) + return; + + m_selectionOnly = value; + setupModel(); + emit selectionOnlyChanged(); +} + +void ItemFilterModel::setSelectedItems(const QStringList &selectedItems) +{ + m_selectedItems = selectedItems; + emit selectedItemsChanged(); } QString ItemFilterModel::typeFilter() const @@ -71,14 +71,14 @@ bool ItemFilterModel::selectionOnly() const return m_selectionOnly; } -void ItemFilterModel::registerDeclarativeType() +QStringList ItemFilterModel::selectedItems() const { - qmlRegisterType<ItemFilterModel>("HelperWidgets",2,0,"ItemFilterModel"); + return m_selectedItems; } -QModelIndex ItemFilterModel::index(int row, int column, const QModelIndex &parent) const +void ItemFilterModel::registerDeclarativeType() { - return QAbstractListModel::index(row, column, parent); + qmlRegisterType<ItemFilterModel>("HelperWidgets", 2, 0, "ItemFilterModel"); } int ItemFilterModel::rowCount(const QModelIndex &) const @@ -88,41 +88,34 @@ int ItemFilterModel::rowCount(const QModelIndex &) const QVariant ItemFilterModel::data(const QModelIndex &index, int role) const { - if (!index.isValid()) + if (!index.isValid() || index.row() >= rowCount()) return {}; - ModelNode node = modelNodeForRow(index.row()); + const ModelNode node = modelNodeForRow(index.row()); - QVariant value; switch (role) { case IdRole: - value = node.id(); - break; + return node.id(); case NameRole: - value = node.variantProperty("objectName").value(); - break; + return node.variantProperty("objectName").value(); case IdAndNameRole: - value = QString("%1 [%2]").arg( - node.variantProperty("objectName").value().toString() - ,node.id()); - break; + return QString("%1 [%2]").arg(node.variantProperty("objectName").value().toString(), + node.id()); + case EnabledRole: + return !m_selectedItems.contains(node.id()); default: - value = node.id(); - break; + return {}; } - - return value; -} - -// TODO: Handle model data manipulation here. -bool ItemFilterModel::setData(const QModelIndex &index, const QVariant &value, int role) -{ - return QAbstractListModel::setData(index, value, role); } QHash<int, QByteArray> ItemFilterModel::roleNames() const { - return m_roles; + static QHash<int, QByteArray> roleNames{{IdRole, "id"}, + {NameRole, "name"}, + {IdAndNameRole, "idAndName"}, + {EnabledRole, "enabled"}}; + + return roleNames; } QVariant ItemFilterModel::modelNodeBackend() const diff --git a/src/plugins/qmldesigner/components/propertyeditor/itemfiltermodel.h b/src/plugins/qmldesigner/components/propertyeditor/itemfiltermodel.h index 6876f1edffd..cb7ae131778 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/itemfiltermodel.h +++ b/src/plugins/qmldesigner/components/propertyeditor/itemfiltermodel.h @@ -16,43 +16,43 @@ class ItemFilterModel : public QAbstractListModel { Q_OBJECT - Q_PROPERTY(QString typeFilter READ typeFilter WRITE setTypeFilter) - Q_PROPERTY(QVariant modelNodeBackendProperty READ modelNodeBackend WRITE setModelNodeBackend NOTIFY modelNodeBackendChanged) + Q_PROPERTY(QString typeFilter READ typeFilter WRITE setTypeFilter NOTIFY typeFilterChanged) + Q_PROPERTY(QVariant modelNodeBackendProperty READ modelNodeBackend WRITE setModelNodeBackend + NOTIFY modelNodeBackendChanged) Q_PROPERTY(QStringList itemModel READ itemModel NOTIFY itemModelChanged) - Q_PROPERTY(bool selectionOnly READ selectionOnly WRITE setSelectionOnly NOTIFY selectionOnlyChanged) + Q_PROPERTY( + bool selectionOnly READ selectionOnly WRITE setSelectionOnly NOTIFY selectionOnlyChanged) + Q_PROPERTY(QStringList selectedItems READ selectedItems WRITE setSelectedItems NOTIFY + selectedItemsChanged) public: - enum Roles { - IdRole = Qt::UserRole + 1, - NameRole, - IdAndNameRole - }; - Q_ENUM(Roles) + enum { IdRole = Qt::DisplayRole, NameRole = Qt::UserRole, IdAndNameRole, EnabledRole }; explicit ItemFilterModel(QObject *parent = nullptr); void setModelNodeBackend(const QVariant &modelNodeBackend); void setTypeFilter(const QString &typeFilter); void setSelectionOnly(bool value); + void setSelectedItems(const QStringList &selectedItems); QString typeFilter() const; bool selectionOnly() const; + QStringList selectedItems() const; void setupModel(); QStringList itemModel() const; static void registerDeclarativeType(); - // Make index accessible for Qml side since it's not accessible by default in QAbstractListModel - Q_INVOKABLE QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const override; - Q_INVOKABLE virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override; - Q_INVOKABLE virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - Q_INVOKABLE virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override; + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - virtual QHash<int,QByteArray> roleNames() const override; + virtual QHash<int, QByteArray> roleNames() const override; signals: + void typeFilterChanged(); void modelNodeBackendChanged(); void itemModelChanged(); void selectionOnlyChanged(); + void selectedItemsChanged(); private: QVariant modelNodeBackend() const; @@ -63,7 +63,7 @@ private: QList<qint32> m_modelInternalIds; QmlDesigner::ModelNode m_modelNode; bool m_selectionOnly; - static QHash<int, QByteArray> m_roles; + QStringList m_selectedItems; }; QML_DECLARE_TYPE(ItemFilterModel) diff --git a/src/plugins/qmldesigner/components/resources/dockwidgets.css b/src/plugins/qmldesigner/components/resources/dockwidgets.css index 3404065521e..4ff1b46f52d 100644 --- a/src/plugins/qmldesigner/components/resources/dockwidgets.css +++ b/src/plugins/qmldesigner/components/resources/dockwidgets.css @@ -113,40 +113,6 @@ QScrollArea#dockWidgetScrollArea { background: creatorTheme.DStabInactiveButtonPress; } -QScrollBar { - background: creatorTheme.DSscrollBarTrack; -} - -QScrollBar:vertical { - width: 10px; -} - -QScrollBar:horizontal { - height: 10px; -} - -QScrollBar::handle { - background: creatorTheme.DSscrollBarHandle; -} - -QScrollBar::handle:vertical { - min-height: 30px; -} - -QScrollBar::handle:horizontal { - min-width: 30px; -} - -QScrollBar::add-line, -QScrollBar::sub-line, -QScrollBar::left-arrow, -QScrollBar::right-arrow, -QScrollBar::add-page, -QScrollBar::sub-page { - height: 0px; - width: 0px; -} - /* Focus related styling */ ADS--DockWidgetTab[focused="true"] { background: creatorTheme.DStabFocusBackground; diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp index a7ada80ed73..6c6102036ac 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp +++ b/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp @@ -27,10 +27,11 @@ #include <texteditor/texteditorconstants.h> #include <qmljseditor/qmljseditordocument.h> -#include <qmljs/qmljsmodelmanagerinterface.h> -#include <qmljs/qmljsreformatter.h> #include <utils/changeset.h> #include <utils/qtcassert.h> +#include <utils/uniqueobjectptr.h> +#include <qmljs/qmljsmodelmanagerinterface.h> +#include <qmljs/qmljsreformatter.h> #include <QDebug> #include <QPair> @@ -78,15 +79,15 @@ void TextEditorView::modelAttached(Model *model) AbstractView::modelAttached(model); - auto textEditor = qobject_cast<TextEditor::BaseTextEditor *>( - QmlDesignerPlugin::instance()->currentDesignDocument()->textEditor()->duplicate()); + auto textEditor = Utils::UniqueObjectLatePtr<TextEditor::BaseTextEditor>( + QmlDesignerPlugin::instance()->currentDesignDocument()->textEditor()->duplicate()); // Set the context of the text editor, but we add another special context to override shortcuts. Core::Context context = textEditor->context(); context.prepend(TEXTEDITOR_CONTEXT_ID); m_textEditorContext->setContext(context); - m_widget->setTextEditor(textEditor); + m_widget->setTextEditor(std::move(textEditor)); } void TextEditorView::modelAboutToBeDetached(Model *model) @@ -102,7 +103,7 @@ void TextEditorView::modelAboutToBeDetached(Model *model) } } -void TextEditorView::importsChanged(const QList<Import> &/*addedImports*/, const QList<Import> &/*removedImports*/) +void TextEditorView::importsChanged(const Imports &/*addedImports*/, const Imports &/*removedImports*/) { } diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorview.h b/src/plugins/qmldesigner/components/texteditor/texteditorview.h index b1865e78f9f..e50372279d9 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorview.h +++ b/src/plugins/qmldesigner/components/texteditor/texteditorview.h @@ -34,7 +34,7 @@ public: void modelAttached(Model *model) override; void modelAboutToBeDetached(Model *model) override; - void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override; + void importsChanged(const Imports &addedImports, const Imports &removedImports) override; void nodeAboutToBeRemoved(const ModelNode &removedNode) override; void nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange) override; diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp index 1529c0a87c1..9f4bb59231e 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp +++ b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "texteditorwidget.h" +#include "utils/uniqueobjectptr.h" #include <texteditorstatusbar.h> #include <texteditorview.h> @@ -51,34 +52,30 @@ TextEditorWidget::TextEditorWidget(TextEditorView *textEditorView) QmlDesignerPlugin::trackWidgetFocusTime(this, Constants::EVENT_TEXTEDITOR_TIME); } -void TextEditorWidget::setTextEditor(TextEditor::BaseTextEditor *textEditor) +void TextEditorWidget::setTextEditor(Utils::UniqueObjectLatePtr<TextEditor::BaseTextEditor> textEditor) { - TextEditor::BaseTextEditor *oldEditor = m_textEditor.release(); - m_textEditor.reset(textEditor); + std::swap(m_textEditor, textEditor); - if (textEditor) { - m_layout->insertWidget(0, textEditor->editorWidget()); + if (m_textEditor) { + m_layout->insertWidget(0, m_textEditor->editorWidget()); - setFocusProxy(textEditor->editorWidget()); + setFocusProxy(m_textEditor->editorWidget()); - QmlDesignerPlugin::instance()->emitCurrentTextEditorChanged(textEditor); + QmlDesignerPlugin::instance()->emitCurrentTextEditorChanged(m_textEditor.get()); - connect(textEditor->editorWidget(), &QPlainTextEdit::cursorPositionChanged, this, [this] { + connect(m_textEditor->editorWidget(), &QPlainTextEdit::cursorPositionChanged, this, [this] { // Cursor position is changed by rewriter if (!m_blockCursorSelectionSynchronisation) m_updateSelectionTimer.start(); }); - textEditor->editorWidget()->installEventFilter(this); + m_textEditor->editorWidget()->installEventFilter(this); static QString styleSheet = Theme::replaceCssColors( QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/scrollbar.css"))); - textEditor->editorWidget()->verticalScrollBar()->setStyleSheet(styleSheet); - textEditor->editorWidget()->horizontalScrollBar()->setStyleSheet(styleSheet); + m_textEditor->editorWidget()->verticalScrollBar()->setStyleSheet(styleSheet); + m_textEditor->editorWidget()->horizontalScrollBar()->setStyleSheet(styleSheet); } - - if (oldEditor) - oldEditor->deleteLater(); } void TextEditorWidget::contextHelp(const Core::IContext::HelpCallback &callback) const diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.h b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.h index c5e5c4f5bc9..947826ab598 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.h +++ b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.h @@ -3,6 +3,7 @@ #pragma once #include <texteditor/texteditor.h> +#include <utils/uniqueobjectptr.h> #include <QTimer> #include <QVBoxLayout> @@ -25,7 +26,7 @@ class TextEditorWidget : public QWidget { public: TextEditorWidget(TextEditorView *textEditorView); - void setTextEditor(TextEditor::BaseTextEditor *textEditor); + void setTextEditor(Utils::UniqueObjectLatePtr<TextEditor::BaseTextEditor> textEditor); TextEditor::BaseTextEditor *textEditor() const { @@ -51,7 +52,7 @@ protected: private: void updateSelectionByCursorPosition(); - std::unique_ptr<TextEditor::BaseTextEditor> m_textEditor; + Utils::UniqueObjectLatePtr<TextEditor::BaseTextEditor> m_textEditor; QPointer<TextEditorView> m_textEditorView; QTimer m_updateSelectionTimer; TextEditorStatusBar *m_statusBar = nullptr; diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp index 38664c146e4..a4132d0eb7a 100644 --- a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp +++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp @@ -720,8 +720,8 @@ void TextureEditorView::instancePropertyChanged(const QList<QPair<ModelNode, Pro m_locked = false; } -void TextureEditorView::importsChanged([[maybe_unused]] const QList<Import> &addedImports, - [[maybe_unused]] const QList<Import> &removedImports) +void TextureEditorView::importsChanged([[maybe_unused]] const Imports &addedImports, + [[maybe_unused]] const Imports &removedImports) { m_hasQuick3DImport = model()->hasImport("QtQuick3D"); m_qmlBackEnd->contextObject()->setHasQuick3DImport(m_hasQuick3DImport); diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.h b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.h index b299a1e99ae..7baa07e9d30 100644 --- a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.h +++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.h @@ -58,7 +58,7 @@ public: void currentStateChanged(const ModelNode &node) override; void instancePropertyChanged(const QList<QPair<ModelNode, PropertyName> > &propertyList) override; - void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override; + void importsChanged(const Imports &addedImports, const Imports &removedImports) override; void customNotification(const AbstractView *view, const QString &identifier, const QList<ModelNode> &nodeList, const QList<QVariant> &data) override; diff --git a/src/plugins/qmldesigner/components/toolbar/toolbar.cpp b/src/plugins/qmldesigner/components/toolbar/toolbar.cpp index 3326c889ad5..e4f523c55cc 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbar.cpp +++ b/src/plugins/qmldesigner/components/toolbar/toolbar.cpp @@ -47,10 +47,10 @@ Utils::FilePath qmlSourcesPath() return Core::ICore::resourcePath("qmldesigner/toolbar"); } -void ToolBar::create() +Utils::UniqueObjectPtr<QToolBar> ToolBar::create() { if (!isVisible()) - return; + return nullptr; ToolBarBackend::registerDeclarativeType(); @@ -58,7 +58,7 @@ void ToolBar::create() //Core::ICore::statusBar()->hide(); - auto toolBar = new QToolBar; + auto toolBar = Utils::makeUniqueObjectPtr<QToolBar>(); toolBar->setObjectName("QDS-TOOLBAR"); toolBar->setContextMenuPolicy(Qt::PreventContextMenu); @@ -66,7 +66,7 @@ void ToolBar::create() toolBar->setFloatable(false); toolBar->setMovable(false); - auto quickWidget = new StudioQuickWidget; + auto quickWidget = std::make_unique<StudioQuickWidget>(); quickWidget->setFixedHeight(48); quickWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); @@ -78,24 +78,26 @@ void ToolBar::create() quickWidget->engine()->addImportPath(propertyEditorResourcesPath().toString() + "/imports"); Utils::FilePath qmlFilePath = qmlSourcesPath() / "Main.qml"; - QTC_ASSERT(qmlFilePath.exists(), return); + QTC_ASSERT(qmlFilePath.exists(), return nullptr); Theme::setupTheme(quickWidget->engine()); quickWidget->setSource(QUrl::fromLocalFile(qmlFilePath.toFSPathString())); - toolBar->addWidget(quickWidget); - window->addToolBar(toolBar); + toolBar->addWidget(quickWidget.release()); + window->addToolBar(toolBar.get()); + + return toolBar; } -void ToolBar::createStatusBar() +Utils::UniqueObjectPtr<QWidget> ToolBar::createStatusBar() { if (!isVisible()) - return; + return nullptr; ToolBarBackend::registerDeclarativeType(); - auto quickWidget = new StudioQuickWidget; + auto quickWidget = Utils::makeUniqueObjectPtr<StudioQuickWidget>(); quickWidget->setFixedHeight(Theme::toolbarSize()); quickWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); @@ -107,7 +109,7 @@ void ToolBar::createStatusBar() quickWidget->engine()->addImportPath(propertyEditorResourcesPath().toString() + "/imports"); Utils::FilePath qmlFilePath = qmlSourcesStatusBarPath().pathAppended("/Main.qml"); - QTC_ASSERT(qmlFilePath.exists(), return); + QTC_ASSERT(qmlFilePath.exists(), return nullptr); Theme::setupTheme(quickWidget->engine()); @@ -117,8 +119,10 @@ void ToolBar::createStatusBar() w->hide(); } - Core::ICore::statusBar()->addWidget(quickWidget); + Core::ICore::statusBar()->addPermanentWidget(quickWidget.get(), 100); Core::ICore::statusBar()->setFixedHeight(Theme::toolbarSize()); + + return quickWidget; } bool ToolBar::isVisible() diff --git a/src/plugins/qmldesigner/components/toolbar/toolbar.h b/src/plugins/qmldesigner/components/toolbar/toolbar.h index 8537757cdf4..d086fdf3ed2 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbar.h +++ b/src/plugins/qmldesigner/components/toolbar/toolbar.h @@ -3,14 +3,18 @@ #pragma once +#include <QToolBar> + +#include <utils/uniqueobjectptr.h> + namespace QmlDesigner { class ToolBar { public: - static void create(); - static void createStatusBar(); + static Utils::UniqueObjectPtr<QToolBar> create(); + static Utils::UniqueObjectPtr<QWidget> createStatusBar(); static bool isVisible(); }; diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp index ce7d783a4ed..1b9f1e3271f 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp +++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp @@ -79,10 +79,288 @@ static void openUiFile() Core::EditorManager::openEditor(mainUiFile, Utils::Id()); } +CrumbleBarModel::CrumbleBarModel(QObject *) +{ + connect(crumbleBar(), &CrumbleBar::pathChanged, this, &CrumbleBarModel::handleCrumblePathChanged); +} + +int CrumbleBarModel::rowCount(const QModelIndex &) const +{ + return crumbleBar()->path().count(); +} + +QHash<int, QByteArray> CrumbleBarModel::roleNames() const +{ + static QHash<int, QByteArray> roleNames{{Qt::UserRole + 1, "fileName"}, + {Qt::UserRole + 2, "fileAddress"}}; + + return roleNames; +} + +QVariant CrumbleBarModel::data(const QModelIndex &index, int role) const +{ + if (index.isValid() && index.row() < rowCount()) { + auto info = crumbleBar()->infos().at(index.row()); + + if (role == Qt::UserRole + 1) { + return info.displayName; + } else if (role == Qt::UserRole + 2) { + return info.fileName.displayName(); + } else { + qWarning() << Q_FUNC_INFO << "invalid role"; + } + } else { + qWarning() << Q_FUNC_INFO << "invalid index"; + } + + return QVariant(); +} + +void CrumbleBarModel::handleCrumblePathChanged() +{ + beginResetModel(); + endResetModel(); +} + +void CrumbleBarModel::onCrumblePathElementClicked(int i) +{ + if (i < rowCount()) { + auto info = crumbleBar()->infos().at(i); + crumbleBar()->onCrumblePathElementClicked(QVariant::fromValue(info)); + } +} + +WorkspaceModel::WorkspaceModel(QObject *) +{ + connect(designModeWidget(), &Internal::DesignModeWidget::initialized, this, [this]() { + const auto dockManager = designModeWidget()->dockManager(); + + connect(dockManager, &ADS::DockManager::workspaceListChanged, this, [this]() { + beginResetModel(); + endResetModel(); + }); + + beginResetModel(); + endResetModel(); + }); +} + +int WorkspaceModel::rowCount(const QModelIndex &) const +{ + if (designModeWidget() && designModeWidget()->dockManager()) + return designModeWidget()->dockManager()->workspaces().count(); + + return 0; +} + +QHash<int, QByteArray> WorkspaceModel::roleNames() const +{ + static QHash<int, QByteArray> roleNames{{DisplayNameRole, "displayName"}, + {FileNameRole, "fileName"}}; + + return roleNames; +} + +QVariant WorkspaceModel::data(const QModelIndex &index, int role) const +{ + if (index.isValid() && index.row() < rowCount()) { + auto workspace = designModeWidget()->dockManager()->workspaces().at(index.row()); + + if (role == DisplayNameRole) { + return workspace.name(); + } else if (role == FileNameRole) { + return workspace.fileName(); + } else { + qWarning() << Q_FUNC_INFO << "invalid role"; + } + } else { + qWarning() << Q_FUNC_INFO << "invalid index"; + } + + return QVariant(); +} + +ActionSubscriber::ActionSubscriber(QObject *parent) + : QObject(parent) +{ + ActionAddedInterface callback = [this](ActionInterface *interface) { + if (interface->menuId() == m_actionId.toLatin1()) { + m_interface = interface; + setupNotifier(); + } + }; + + QmlDesignerPlugin::instance()->viewManager().designerActionManager().addAddActionCallback(callback); +} + +void ActionSubscriber::trigger() +{ + if (m_interface) + m_interface->action()->trigger(); +} + +bool ActionSubscriber::available() const +{ + if (m_interface) + return m_interface->action()->isEnabled(); + return false; +} + +bool ActionSubscriber::checked() const +{ + if (m_interface) + return m_interface->action()->isChecked(); + + return false; +} + +QString ActionSubscriber::actionId() const +{ + return m_actionId; +} + +void ActionSubscriber::setActionId(const QString &id) +{ + if (id == m_actionId) + return; + + m_actionId = id; + emit actionIdChanged(); + emit tooltipChanged(); +} + +QString ActionSubscriber::tooltip() const +{ + if (m_interface) + return m_interface->action()->text(); + return {}; +} + +void ActionSubscriber::setupNotifier() +{ + if (!m_interface) + return; + + connect(m_interface->action(), &QAction::enabledChanged, this, &ActionSubscriber::availableChanged); + + emit tooltipChanged(); +} + +ToolBarBackend::ToolBarBackend(QObject *parent) + : QObject(parent) +{ + ActionAddedInterface callback = [this](ActionInterface *interface) { + if (interface->menuId() == "PreviewZoom") + m_zoomAction = interface; + }; + + QmlDesignerPlugin::instance()->viewManager().designerActionManager().addAddActionCallback(callback); + + connect(designModeWidget(), + &Internal::DesignModeWidget::navigationHistoryChanged, + this, + &ToolBarBackend::navigationHistoryChanged); + + connect(Core::DocumentModel::model(), + &QAbstractItemModel::rowsInserted, + this, + &ToolBarBackend::updateDocumentModel); + connect(Core::DocumentModel::model(), + &QAbstractItemModel::rowsRemoved, + this, + &ToolBarBackend::updateDocumentModel); + connect(Core::DocumentModel::model(), + &QAbstractItemModel::dataChanged, + this, + &ToolBarBackend::updateDocumentModel); + connect(Core::DocumentModel::model(), + &QAbstractItemModel::modelReset, + this, + &ToolBarBackend::updateDocumentModel); + + connect(Core::EditorManager::instance(), + &Core::EditorManager::currentEditorChanged, + this, + &ToolBarBackend::documentIndexChanged); + + connect(Core::EditorManager::instance(), + &Core::EditorManager::currentEditorChanged, + this, + &ToolBarBackend::currentStyleChanged); + + connect(designModeWidget(), &Internal::DesignModeWidget::initialized, this, [this]() { + const auto dockManager = designModeWidget()->dockManager(); + + connect(dockManager, &ADS::DockManager::workspaceLoaded, this, [this](const QString &) { + emit currentWorkspaceChanged(); + }); + + connect(dockManager, &ADS::DockManager::workspaceListChanged, this, [this]() { + emit currentWorkspaceChanged(); + }); + + emit currentWorkspaceChanged(); + }); + + auto editorManager = Core::EditorManager::instance(); + + connect(editorManager, &Core::EditorManager::documentClosed, this, [this]() { + if (isInDesignMode() && Core::DocumentModel::entryCount() == 0) { + QTimer::singleShot(0, + Core::ModeManager::instance(), + []() { /* The mode change has to happen from event loop. + Otherwise we and up in an invalid state */ + Core::ModeManager::activateMode(Core::Constants::MODE_WELCOME); + }); + } + }); + + connect(Core::ICore::instance(), &Core::ICore::coreAboutToOpen, this, [this] { + connect(Core::DesignMode::instance(), &Core::DesignMode::enabledStateChanged, this, [this] { + emit isDesignModeEnabledChanged(); + }); + }); + + connect(Core::ModeManager::instance(), &Core::ModeManager::currentModeChanged, this, [this]() { + emit isInDesignModeChanged(); + emit isInEditModeChanged(); + emit isDesignModeEnabledChanged(); + }); + + connect(ProjectExplorer::ProjectManager::instance(), + &ProjectExplorer::ProjectManager::startupProjectChanged, + this, + [this](ProjectExplorer::Project *project) { + disconnect(m_kitConnection); + emit isQt6Changed(); + emit projectOpenedChanged(); + if (project) { + m_kitConnection = connect(project, + &ProjectExplorer::Project::activeTargetChanged, + this, + &ToolBarBackend::currentKitChanged); + emit currentKitChanged(); + } + }); + + connect(ProjectExplorer::KitManager::instance(), + &ProjectExplorer::KitManager::kitsChanged, + this, + &ToolBarBackend::kitsChanged); +} + +void ToolBarBackend::registerDeclarativeType() +{ + qmlRegisterType<ToolBarBackend>("ToolBar", 1, 0, "ToolBarBackend"); + qmlRegisterType<ActionSubscriber>("ToolBar", 1, 0, "ActionSubscriber"); + qmlRegisterType<CrumbleBarModel>("ToolBar", 1, 0, "CrumbleBarModel"); + qmlRegisterType<WorkspaceModel>("ToolBar", 1, 0, "WorkspaceModel"); +} + void ToolBarBackend::triggerModeChange() { QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_TOOLBAR_MODE_CHANGE); - QTimer::singleShot(0, [this]() { //Do not trigger mode change directly from QML + QTimer::singleShot(0, this, [this]() { //Do not trigger mode change directly from QML bool qmlFileOpen = false; if (!projectOpened()) { @@ -109,7 +387,7 @@ void ToolBarBackend::triggerModeChange() void ToolBarBackend::triggerProjectSettings() { QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_TOOLBAR_PROJECT_SETTINGS); - QTimer::singleShot(0, []() { //Do not trigger mode change directly from QML + QTimer::singleShot(0, Core::ModeManager::instance(), []() { // Do not trigger mode change directly from QML Core::ModeManager::activateMode(ProjectExplorer::Constants::MODE_SESSION); }); } @@ -230,7 +508,8 @@ void ToolBarBackend::setCurrentKit(int index) if (!newTarget) newTarget = project->addTargetForKit(kit); - project->setActiveTarget(newTarget, ProjectExplorer::SetActive::Cascade); + project->setActiveTarget(newTarget, + ProjectExplorer::SetActive::Cascade); emit currentKitChanged(); } @@ -247,113 +526,6 @@ bool ToolBarBackend::canGoForward() const return designModeWidget()->canGoForward(); } -ToolBarBackend::ToolBarBackend(QObject *parent) - : QObject(parent) -{ - ActionAddedInterface callback = [this](ActionInterface *interface) { - if (interface->menuId() == "PreviewZoom") - m_zoomAction = interface; - }; - - QmlDesignerPlugin::instance()->viewManager().designerActionManager().addAddActionCallback(callback); - - connect(designModeWidget(), - &Internal::DesignModeWidget::navigationHistoryChanged, - this, - &ToolBarBackend::navigationHistoryChanged); - - connect(Core::DocumentModel::model(), - &QAbstractItemModel::rowsInserted, - this, - &ToolBarBackend::updateDocumentModel); - connect(Core::DocumentModel::model(), - &QAbstractItemModel::rowsRemoved, - this, - &ToolBarBackend::updateDocumentModel); - connect(Core::DocumentModel::model(), - &QAbstractItemModel::dataChanged, - this, - &ToolBarBackend::updateDocumentModel); - connect(Core::DocumentModel::model(), - &QAbstractItemModel::modelReset, - this, - &ToolBarBackend::updateDocumentModel); - - connect(Core::EditorManager::instance(), - &Core::EditorManager::currentEditorChanged, - this, - &ToolBarBackend::documentIndexChanged); - - connect(Core::EditorManager::instance(), - &Core::EditorManager::currentEditorChanged, - this, - &ToolBarBackend::currentStyleChanged); - - connect(designModeWidget(), &Internal::DesignModeWidget::initialized, this, [this]() { - const auto dockManager = designModeWidget()->dockManager(); - - connect(dockManager, - &ADS::DockManager::workspaceListChanged, - this, - &ToolBarBackend::setupWorkspaces); - connect(dockManager, &ADS::DockManager::workspaceLoaded, this, [this](const QString &) { - emit currentWorkspaceChanged(); - }); - - setupWorkspaces(); - }); - - auto editorManager = Core::EditorManager::instance(); - - connect(editorManager, &Core::EditorManager::documentClosed, this, [this]() { - if (isInDesignMode() && Core::DocumentModel::entryCount() == 0) { - QTimer::singleShot(0, []() { /* The mode change has to happen from event loop. - Otherwise we and up in an invalid state */ - Core::ModeManager::activateMode(Core::Constants::MODE_WELCOME); - }); - } - }); - - connect(Core::ICore::instance(), &Core::ICore::coreAboutToOpen, this, [this] { - connect(Core::DesignMode::instance(), &Core::DesignMode::enabledStateChanged, this, [this] { - emit isDesignModeEnabledChanged(); - }); - }); - - connect(Core::ModeManager::instance(), &Core::ModeManager::currentModeChanged, this, [this]() { - emit isInDesignModeChanged(); - emit isInEditModeChanged(); - emit isDesignModeEnabledChanged(); - }); - - connect(ProjectExplorer::ProjectManager::instance(), - &ProjectExplorer::ProjectManager::startupProjectChanged, - [this](ProjectExplorer::Project *project) { - disconnect(m_kitConnection); - emit isQt6Changed(); - emit projectOpenedChanged(); - if (project) { - m_kitConnection = connect(project, - &ProjectExplorer::Project::activeTargetChanged, - this, - &ToolBarBackend::currentKitChanged); - emit currentKitChanged(); - } - }); - - connect(ProjectExplorer::KitManager::instance(), - &ProjectExplorer::KitManager::kitsChanged, - this, - &ToolBarBackend::kitsChanged); -} - -void ToolBarBackend::registerDeclarativeType() -{ - qmlRegisterType<ToolBarBackend>("ToolBar", 1, 0, "ToolBarBackend"); - qmlRegisterType<ActionSubscriber>("ToolBar", 1, 0, "ActionSubscriber"); - qmlRegisterType<CrumbleBarModel>("ToolBar", 1, 0, "CrumbleBarModel"); -} - QStringList ToolBarBackend::documentModel() const { return m_openDocuments; @@ -382,13 +554,9 @@ int ToolBarBackend::documentIndex() const QString ToolBarBackend::currentWorkspace() const { if (designModeWidget() && designModeWidget()->dockManager()) - return designModeWidget()->dockManager()->activeWorkspace(); - return {}; -} + return designModeWidget()->dockManager()->activeWorkspace()->fileName(); -QStringList ToolBarBackend::workspaces() const -{ - return m_workspaces; + return {}; } QStringList ToolBarBackend::styles() const @@ -417,17 +585,6 @@ bool ToolBarBackend::isInEditMode() const return Core::ModeManager::currentModeId() == Core::Constants::MODE_EDIT; } -void ToolBarBackend::launchGlobalAnnotations() -{ - QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_TOOLBAR_EDIT_GLOBAL_ANNOTATION); - ModelNode node = currentDesignDocument()->rewriterView()->rootModelNode(); - - if (node.isValid()) { - designModeWidget()->globalAnnotationEditor().setModelNode(node); - designModeWidget()->globalAnnotationEditor().showWidget(); - } -} - bool ToolBarBackend::isDesignModeEnabled() const { if (Core::DesignMode::instance()) @@ -445,9 +602,7 @@ int ToolBarBackend::currentStyle() const const QString qmlFile = view->model()->fileUrl().toLocalFile(); - const int index = ChangeStyleWidgetAction::getCurrentStyle(qmlFile); - - return index; + return ChangeStyleWidgetAction::getCurrentStyle(qmlFile); } QStringList ToolBarBackend::kits() const @@ -485,129 +640,16 @@ bool ToolBarBackend::projectOpened() const return ProjectExplorer::ProjectManager::instance()->startupProject(); } -void ToolBarBackend::setupWorkspaces() -{ - m_workspaces.clear(); - m_workspaces = designModeWidget()->dockManager()->workspaces(); - Utils::sort(m_workspaces); - emit workspacesChanged(); - emit currentWorkspaceChanged(); -} - -ActionSubscriber::ActionSubscriber(QObject *parent) - : QObject(parent) -{ - ActionAddedInterface callback = [this](ActionInterface *interface) { - if (interface->menuId() == m_actionId.toLatin1()) { - m_interface = interface; - setupNotifier(); - } - }; - - QmlDesignerPlugin::instance()->viewManager().designerActionManager().addAddActionCallback(callback); -} - -void ActionSubscriber::trigger() -{ - if (m_interface) - m_interface->action()->trigger(); -} - -bool ActionSubscriber::available() const -{ - if (m_interface) - return m_interface->action()->isEnabled(); - return false; -} - -bool ActionSubscriber::checked() const -{ - if (m_interface) - return m_interface->action()->isChecked(); - - return false; -} - -QString ActionSubscriber::actionId() const -{ - return m_actionId; -} - -void ActionSubscriber::setActionId(const QString &id) -{ - if (id == m_actionId) - return; - - m_actionId = id; - emit actionIdChanged(); - emit tooltipChanged(); -} - -QString ActionSubscriber::tooltip() const -{ - if (m_interface) - return m_interface->action()->text(); - return {}; -} - -void ActionSubscriber::setupNotifier() -{ - if (!m_interface) - return; - - connect(m_interface->action(), &QAction::enabledChanged, this, &ActionSubscriber::availableChanged); - - emit tooltipChanged(); -} - -CrumbleBarModel::CrumbleBarModel(QObject *) -{ - connect(crumbleBar(), &CrumbleBar::pathChanged, this, &CrumbleBarModel::handleCrumblePathChanged); -} - -int CrumbleBarModel::rowCount(const QModelIndex &) const -{ - return crumbleBar()->path().count(); -} - -QHash<int, QByteArray> CrumbleBarModel::roleNames() const -{ - static QHash<int, QByteArray> roleNames{{Qt::UserRole + 1, "fileName"}, - {Qt::UserRole + 2, "fileAddress"}}; - - return roleNames; -} - -QVariant CrumbleBarModel::data(const QModelIndex &index, int role) const +void ToolBarBackend::launchGlobalAnnotations() { - if (index.isValid() && index.row() < rowCount()) { - auto info = crumbleBar()->infos().at(index.row()); - - if (role == Qt::UserRole + 1) { - return info.displayName; - } else if (role == Qt::UserRole + 2) { - return info.fileName.displayName(); - } else { - qWarning() << Q_FUNC_INFO << "invalid role"; - } - } else { - qWarning() << Q_FUNC_INFO << "invalid index"; - } - - return QVariant(); -} + QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_TOOLBAR_EDIT_GLOBAL_ANNOTATION); -void CrumbleBarModel::handleCrumblePathChanged() -{ - beginResetModel(); - endResetModel(); -} + QTC_ASSERT(currentDesignDocument(), return); + ModelNode node = currentDesignDocument()->rewriterView()->rootModelNode(); -void CrumbleBarModel::onCrumblePathElementClicked(int i) -{ - if (i < rowCount()) { - auto info = crumbleBar()->infos().at(i); - crumbleBar()->onCrumblePathElementClicked(QVariant::fromValue(info)); + if (node.isValid()) { + designModeWidget()->globalAnnotationEditor().setModelNode(node); + designModeWidget()->globalAnnotationEditor().showWidget(); } } diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h index 1aed5aeeaf4..0091ab8a7f1 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h +++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h @@ -18,15 +18,25 @@ public: explicit CrumbleBarModel(QObject *parent = nullptr); int rowCount(const QModelIndex &parent = QModelIndex()) const override; - QHash<int, QByteArray> roleNames() const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; void handleCrumblePathChanged(); Q_INVOKABLE void onCrumblePathElementClicked(int i); +}; -private: +class WorkspaceModel : public QAbstractListModel +{ + Q_OBJECT + enum { DisplayNameRole = Qt::DisplayRole, FileNameRole = Qt::UserRole }; + +public: + explicit WorkspaceModel(QObject *parent = nullptr); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QHash<int, QByteArray> roleNames() const override; + QVariant data(const QModelIndex &index, int role = DisplayNameRole) const override; }; class ActionSubscriber : public QObject @@ -73,7 +83,6 @@ class ToolBarBackend : public QObject Q_PROPERTY(QStringList documentModel READ documentModel NOTIFY openDocumentsChanged) Q_PROPERTY(int documentIndex READ documentIndex NOTIFY documentIndexChanged) Q_PROPERTY(QString currentWorkspace READ currentWorkspace NOTIFY currentWorkspaceChanged) - Q_PROPERTY(QStringList workspaces READ workspaces NOTIFY workspacesChanged) Q_PROPERTY(QStringList styles READ styles CONSTANT) Q_PROPERTY(bool isInDesignMode READ isInDesignMode NOTIFY isInDesignModeChanged) Q_PROPERTY(bool isInEditMode READ isInEditMode NOTIFY isInEditModeChanged) @@ -111,11 +120,11 @@ public: int documentIndex() const; QString currentWorkspace() const; - QStringList workspaces() const; QStringList styles() const; bool isInDesignMode() const; + bool isInEditMode() const; bool isDesignModeEnabled() const; int currentStyle() const; @@ -127,8 +136,6 @@ public: bool projectOpened() const; - bool isInEditMode() const; - static void launchGlobalAnnotations(); signals: @@ -136,7 +143,6 @@ signals: void openDocumentsChanged(); void documentIndexChanged(); void currentWorkspaceChanged(); - void workspacesChanged(); void isInDesignModeChanged(); void isInEditModeChanged(); void isDesignModeEnabledChanged(); @@ -152,7 +158,6 @@ private: ActionInterface *m_zoomAction; QStringList m_openDocuments; - QStringList m_workspaces; QMetaObject::Connection m_kitConnection; }; diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousexplicitimagecache.cpp b/src/plugins/qmldesigner/designercore/imagecache/asynchronousexplicitimagecache.cpp index ed168b8a09b..1c46722d4c4 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousexplicitimagecache.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousexplicitimagecache.cpp @@ -78,42 +78,30 @@ void AsynchronousExplicitImageCache::wait() m_backgroundThread.join(); } -void AsynchronousExplicitImageCache::requestImage(Utils::PathString name, +void AsynchronousExplicitImageCache::requestImage(Utils::SmallStringView name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, - Utils::SmallString extraId) + Utils::SmallStringView extraId) { - addEntry(std::move(name), - std::move(extraId), - std::move(captureCallback), - std::move(abortCallback), - RequestType::Image); + addEntry(name, extraId, std::move(captureCallback), std::move(abortCallback), RequestType::Image); m_condition.notify_all(); } -void AsynchronousExplicitImageCache::requestMidSizeImage(Utils::PathString name, +void AsynchronousExplicitImageCache::requestMidSizeImage(Utils::SmallStringView name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, - Utils::SmallString extraId) + Utils::SmallStringView extraId) { - addEntry(std::move(name), - std::move(extraId), - std::move(captureCallback), - std::move(abortCallback), - RequestType::MidSizeImage); + addEntry(name, extraId, std::move(captureCallback), std::move(abortCallback), RequestType::MidSizeImage); m_condition.notify_all(); } -void AsynchronousExplicitImageCache::requestSmallImage(Utils::PathString name, +void AsynchronousExplicitImageCache::requestSmallImage(Utils::SmallStringView name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, - Utils::SmallString extraId) + Utils::SmallStringView extraId) { - addEntry(std::move(name), - std::move(extraId), - std::move(captureCallback), - std::move(abortCallback), - RequestType::SmallImage); + addEntry(name, extraId, std::move(captureCallback), std::move(abortCallback), RequestType::SmallImage); m_condition.notify_all(); } diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp index dec46f87252..00b3ab722ef 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp @@ -118,10 +118,10 @@ void AsynchronousImageCache::wait() m_backgroundThread.join(); } -void AsynchronousImageCache::requestImage(Utils::PathString name, +void AsynchronousImageCache::requestImage(Utils::SmallStringView name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, - Utils::SmallString extraId, + Utils::SmallStringView extraId, ImageCache::AuxiliaryData auxiliaryData) { addEntry(std::move(name), @@ -133,10 +133,10 @@ void AsynchronousImageCache::requestImage(Utils::PathString name, m_condition.notify_all(); } -void AsynchronousImageCache::requestMidSizeImage(Utils::PathString name, +void AsynchronousImageCache::requestMidSizeImage(Utils::SmallStringView name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, - Utils::SmallString extraId, + Utils::SmallStringView extraId, ImageCache::AuxiliaryData auxiliaryData) { addEntry(std::move(name), @@ -148,10 +148,10 @@ void AsynchronousImageCache::requestMidSizeImage(Utils::PathString name, m_condition.notify_all(); } -void AsynchronousImageCache::requestSmallImage(Utils::PathString name, +void AsynchronousImageCache::requestSmallImage(Utils::SmallStringView name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, - Utils::SmallString extraId, + Utils::SmallStringView extraId, ImageCache::AuxiliaryData auxiliaryData) { addEntry(std::move(name), diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp index 500359182ae..5d6ced30601 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp @@ -39,16 +39,16 @@ AsynchronousImageFactory::~AsynchronousImageFactory() wait(); } -void AsynchronousImageFactory::generate(Utils::PathString name, - Utils::SmallString extraId, +void AsynchronousImageFactory::generate(Utils::SmallStringView name, + Utils::SmallStringView extraId, ImageCache::AuxiliaryData auxiliaryData) { - addEntry(std::move(name), std::move(extraId), std::move(auxiliaryData)); + addEntry(name, extraId, std::move(auxiliaryData)); m_condition.notify_all(); } -void AsynchronousImageFactory::addEntry(Utils::PathString &&name, - Utils::SmallString &&extraId, +void AsynchronousImageFactory::addEntry(Utils::SmallStringView name, + Utils::SmallStringView extraId, ImageCache::AuxiliaryData &&auxiliaryData) { std::unique_lock lock{m_mutex}; diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.h b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.h index 4aa24d0a769..415646c73cd 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.h +++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.h @@ -28,8 +28,8 @@ public: ~AsynchronousImageFactory(); - void generate(Utils::PathString name, - Utils::SmallString extraId = {}, + void generate(Utils::SmallStringView name, + Utils::SmallStringView extraId = {}, ImageCache::AuxiliaryData auxiliaryData = {}); void clean(); @@ -50,8 +50,8 @@ private: ImageCache::AuxiliaryData auxiliaryData; }; - void addEntry(Utils::PathString &&name, - Utils::SmallString &&extraId, + void addEntry(Utils::SmallStringView name, + Utils::SmallStringView extraId, ImageCache::AuxiliaryData &&auxiliaryData); bool isRunning(); void waitForEntries(); diff --git a/src/plugins/qmldesigner/designercore/imagecache/explicitimagecacheimageprovider.cpp b/src/plugins/qmldesigner/designercore/imagecache/explicitimagecacheimageprovider.cpp index 1f28d4eca69..509abb5c70f 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/explicitimagecacheimageprovider.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/explicitimagecacheimageprovider.cpp @@ -17,7 +17,7 @@ QQuickImageResponse *ExplicitImageCacheImageProvider::requestImageResponse(const auto response = std::make_unique<ImageCacheImageResponse>(m_defaultImage); m_cache.requestImage( - id, + Utils::PathString{id}, [response = QPointer<ImageCacheImageResponse>(response.get())](const QImage &image) { QMetaObject::invokeMethod( response, diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachestorage.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachestorage.h index 6b0df6f1c1e..37131012d69 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachestorage.h +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachestorage.h @@ -35,12 +35,8 @@ public: ImageEntry fetchImage(Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp) const override { try { - Sqlite::DeferredTransaction transaction{database}; - - auto optionalBlob = selectImageStatement.template optionalValue<Sqlite::ByteArrayBlob>( - name, minimumTimeStamp.value); - - transaction.commit(); + auto optionalBlob = selectImageStatement.template optionalValueWithTransaction< + Sqlite::ByteArrayBlob>(name, minimumTimeStamp.value); if (optionalBlob) return {readImage(optionalBlob->byteArray)}; @@ -55,12 +51,8 @@ public: Sqlite::TimeStamp minimumTimeStamp) const override { try { - Sqlite::DeferredTransaction transaction{database}; - - auto optionalBlob = selectMidSizeImageStatement.template optionalValue<Sqlite::ByteArrayBlob>( - name, minimumTimeStamp.value); - - transaction.commit(); + auto optionalBlob = selectMidSizeImageStatement.template optionalValueWithTransaction< + Sqlite::ByteArrayBlob>(name, minimumTimeStamp.value); if (optionalBlob) return {readImage(optionalBlob->byteArray)}; @@ -75,12 +67,8 @@ public: Sqlite::TimeStamp minimumTimeStamp) const override { try { - Sqlite::DeferredTransaction transaction{database}; - - auto optionalBlob = selectSmallImageStatement.template optionalValue<Sqlite::ByteArrayBlob>( - name, minimumTimeStamp.value); - - transaction.commit(); + auto optionalBlob = selectSmallImageStatement.template optionalValueWithTransaction< + Sqlite::ByteArrayBlob>(name, minimumTimeStamp.value); if (optionalBlob) return ImageEntry{readImage(optionalBlob->byteArray)}; @@ -95,12 +83,8 @@ public: IconEntry fetchIcon(Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp) const override { try { - Sqlite::DeferredTransaction transaction{database}; - - auto optionalBlob = selectIconStatement.template optionalValue<Sqlite::ByteArrayBlob>( - name, minimumTimeStamp.value); - - transaction.commit(); + auto optionalBlob = selectIconStatement.template optionalValueWithTransaction< + Sqlite::ByteArrayBlob>(name, minimumTimeStamp.value); if (optionalBlob) return {readIcon(optionalBlob->byteArray)}; @@ -119,19 +103,16 @@ public: const QImage &smallImage) override { try { - Sqlite::ImmediateTransaction transaction{database}; - auto imageBuffer = createBuffer(image); auto midSizeImageBuffer = createBuffer(midSizeImage); auto smallImageBuffer = createBuffer(smallImage); - upsertImageStatement.write(name, - newTimeStamp.value, - createBlobView(imageBuffer.get()), - createBlobView(midSizeImageBuffer.get()), - createBlobView(smallImageBuffer.get())); - - transaction.commit(); - + Sqlite::withImmediateTransaction(database, [&] { + upsertImageStatement.write(name, + newTimeStamp.value, + createBlobView(imageBuffer.get()), + createBlobView(midSizeImageBuffer.get()), + createBlobView(smallImageBuffer.get())); + }); } catch (const Sqlite::StatementIsBusy &) { return storeImage(name, newTimeStamp, image, midSizeImage, smallImage); } @@ -140,12 +121,10 @@ public: void storeIcon(Utils::SmallStringView name, Sqlite::TimeStamp newTimeStamp, const QIcon &icon) override { try { - Sqlite::ImmediateTransaction transaction{database}; - auto iconBuffer = createBuffer(icon); - upsertIconStatement.write(name, newTimeStamp.value, createBlobView(iconBuffer.get())); - - transaction.commit(); + Sqlite::withImmediateTransaction(database, [&] { + upsertIconStatement.write(name, newTimeStamp.value, createBlobView(iconBuffer.get())); + }); } catch (const Sqlite::StatementIsBusy &) { return storeIcon(name, newTimeStamp, icon); diff --git a/src/plugins/qmldesigner/designercore/imagecache/midsizeimagecacheprovider.cpp b/src/plugins/qmldesigner/designercore/imagecache/midsizeimagecacheprovider.cpp index fd37f02f044..9b62ffe1bfa 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/midsizeimagecacheprovider.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/midsizeimagecacheprovider.cpp @@ -15,7 +15,7 @@ QQuickImageResponse *MidSizeImageCacheProvider::requestImageResponse(const QStri auto response = std::make_unique<ImageCacheImageResponse>(m_defaultImage); m_cache.requestMidSizeImage( - id, + Utils::PathString{id}, [response = QPointer<ImageCacheImageResponse>(response.get())](const QImage &image) { QMetaObject::invokeMethod( response, diff --git a/src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.cpp b/src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.cpp index 3fae3bb1754..722498dd98a 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.cpp @@ -16,7 +16,7 @@ QQuickImageResponse *SmallImageCacheProvider::requestImageResponse(const QString auto response = std::make_unique<ImageCacheImageResponse>(m_defaultImage); m_cache.requestSmallImage( - id, + Utils::PathString{id}, [response = QPointer<ImageCacheImageResponse>(response.get())](const QImage &image) { QMetaObject::invokeMethod( response, diff --git a/src/plugins/qmldesigner/designercore/include/abstractproperty.h b/src/plugins/qmldesigner/designercore/include/abstractproperty.h index 7b5c6ccf68d..c22402202d8 100644 --- a/src/plugins/qmldesigner/designercore/include/abstractproperty.h +++ b/src/plugins/qmldesigner/designercore/include/abstractproperty.h @@ -50,6 +50,10 @@ class QMLDESIGNERCORE_EXPORT AbstractProperty public: AbstractProperty() = default; + AbstractProperty(const AbstractProperty &) = default; + AbstractProperty &operator=(const AbstractProperty &) = default; + AbstractProperty(AbstractProperty &&) = default; + AbstractProperty &operator=(AbstractProperty &&) noexcept = default; ~AbstractProperty(); AbstractProperty(const AbstractProperty &property, AbstractView *view); diff --git a/src/plugins/qmldesigner/designercore/include/abstractview.h b/src/plugins/qmldesigner/designercore/include/abstractview.h index 2d886d959be..0c3df94030c 100644 --- a/src/plugins/qmldesigner/designercore/include/abstractview.h +++ b/src/plugins/qmldesigner/designercore/include/abstractview.h @@ -195,9 +195,9 @@ public: const ModelNode &movedNode, int oldIndex); - virtual void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports); - virtual void possibleImportsChanged(const QList<Import> &possibleImports); - virtual void usedImportsChanged(const QList<Import> &usedImports); + virtual void importsChanged(const Imports &addedImports, const Imports &removedImports); + virtual void possibleImportsChanged(const Imports &possibleImports); + virtual void usedImportsChanged(const Imports &usedImports); virtual void auxiliaryDataChanged(const ModelNode &node, AuxiliaryDataKeyView type, diff --git a/src/plugins/qmldesigner/designercore/include/asynchronousexplicitimagecache.h b/src/plugins/qmldesigner/designercore/include/asynchronousexplicitimagecache.h index 7cddfd29d9f..f9a053a9413 100644 --- a/src/plugins/qmldesigner/designercore/include/asynchronousexplicitimagecache.h +++ b/src/plugins/qmldesigner/designercore/include/asynchronousexplicitimagecache.h @@ -23,18 +23,18 @@ public: AsynchronousExplicitImageCache(ImageCacheStorageInterface &storage); - void requestImage(Utils::PathString name, + void requestImage(Utils::SmallStringView name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, - Utils::SmallString extraId = {}); - void requestMidSizeImage(Utils::PathString name, + Utils::SmallStringView extraId = {}); + void requestMidSizeImage(Utils::SmallStringView name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, - Utils::SmallString extraId = {}); - void requestSmallImage(Utils::PathString name, + Utils::SmallStringView extraId = {}); + void requestSmallImage(Utils::SmallStringView name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, - Utils::SmallString extraId = {}); + Utils::SmallStringView extraId = {}); void clean(); diff --git a/src/plugins/qmldesigner/designercore/include/asynchronousimagecache.h b/src/plugins/qmldesigner/designercore/include/asynchronousimagecache.h index 04882d61eb7..3257f9d75c1 100644 --- a/src/plugins/qmldesigner/designercore/include/asynchronousimagecache.h +++ b/src/plugins/qmldesigner/designercore/include/asynchronousimagecache.h @@ -28,20 +28,20 @@ public: ImageCacheGeneratorInterface &generator, TimeStampProviderInterface &timeStampProvider); - void requestImage(Utils::PathString name, + void requestImage(Utils::SmallStringView name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, - Utils::SmallString extraId = {}, + Utils::SmallStringView extraId = {}, ImageCache::AuxiliaryData auxiliaryData = {}) override; - void requestMidSizeImage(Utils::PathString name, + void requestMidSizeImage(Utils::SmallStringView name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, - Utils::SmallString extraId = {}, + Utils::SmallStringView extraId = {}, ImageCache::AuxiliaryData auxiliaryData = {}) override; - void requestSmallImage(Utils::PathString name, + void requestSmallImage(Utils::SmallStringView name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, - Utils::SmallString extraId = {}, + Utils::SmallStringView extraId = {}, ImageCache::AuxiliaryData auxiliaryData = {}) override; void clean(); diff --git a/src/plugins/qmldesigner/designercore/include/asynchronousimagecacheinterface.h b/src/plugins/qmldesigner/designercore/include/asynchronousimagecacheinterface.h index da8bda079e2..43c8e5b6726 100644 --- a/src/plugins/qmldesigner/designercore/include/asynchronousimagecacheinterface.h +++ b/src/plugins/qmldesigner/designercore/include/asynchronousimagecacheinterface.h @@ -14,22 +14,22 @@ namespace QmlDesigner { class AsynchronousImageCacheInterface { public: - virtual void requestImage(Utils::PathString name, + virtual void requestImage(Utils::SmallStringView name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, - Utils::SmallString extraId = {}, + Utils::SmallStringView extraId = {}, ImageCache::AuxiliaryData auxiliaryData = {}) = 0; - virtual void requestMidSizeImage(Utils::PathString name, + virtual void requestMidSizeImage(Utils::SmallStringView name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, - Utils::SmallString extraId = {}, + Utils::SmallStringView extraId = {}, ImageCache::AuxiliaryData auxiliaryData = {}) = 0; - virtual void requestSmallImage(Utils::PathString name, + virtual void requestSmallImage(Utils::SmallStringView name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, - Utils::SmallString extraId = {}, + Utils::SmallStringView extraId = {}, ImageCache::AuxiliaryData auxiliaryData = {}) = 0; diff --git a/src/plugins/qmldesigner/designercore/include/auxiliarydataproperties.h b/src/plugins/qmldesigner/designercore/include/auxiliarydataproperties.h index 1a1884ce645..4c12af14fef 100644 --- a/src/plugins/qmldesigner/designercore/include/auxiliarydataproperties.h +++ b/src/plugins/qmldesigner/designercore/include/auxiliarydataproperties.h @@ -106,6 +106,8 @@ inline constexpr AuxiliaryDataKeyView globalAnnotationStatus{AuxiliaryDataType:: inline constexpr AuxiliaryDataKeyView rotBlockProperty{AuxiliaryDataType::NodeInstanceAuxiliary, "rotBlock"}; inline constexpr AuxiliaryDataKeyView languageProperty{AuxiliaryDataType::Temporary, "language"}; +inline constexpr AuxiliaryDataKeyView bakeLightsManualProperty{AuxiliaryDataType::Document, + "bakeLightsManual"}; // Most material preview aux properties are duplicated as document and instance types, as they // are both required to be persistent and used at runtime to control material preview rendering diff --git a/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h b/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h index 7b2b8a2c55f..d4a087f4c7b 100644 --- a/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h +++ b/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h @@ -6,6 +6,9 @@ #include <instances/puppetstartdata.h> #include <utils/filepath.h> +#include <QColor> +#include <QUrl> + namespace QmlDesigner { class DesignerSettings; @@ -40,6 +43,11 @@ public: virtual PuppetStartData puppetStartData(const class Model &model) const = 0; virtual bool instantQmlTextUpdate() const = 0; virtual Utils::FilePath qmlPuppetPath() const = 0; + virtual QStringList modulePaths() const = 0; + virtual QStringList projectModulePaths() const = 0; + virtual bool isQt6Project() const = 0; + virtual QString qtQuickVersion() const = 0; + virtual Utils::FilePath resourcePath(const QString &relativePath) const = 0; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/include/forwardview.h b/src/plugins/qmldesigner/designercore/include/forwardview.h index 7041e851096..386ac83141c 100644 --- a/src/plugins/qmldesigner/designercore/include/forwardview.h +++ b/src/plugins/qmldesigner/designercore/include/forwardview.h @@ -44,7 +44,7 @@ public: void fileUrlChanged(const QUrl &oldUrl, const QUrl &newUrl) override; void nodeOrderChanged(const NodeListProperty &listProperty) override; - void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override; + void importsChanged(const Imports &addedImports, const Imports &removedImports) override; void auxiliaryDataChanged(const ModelNode &node, const PropertyName &name, const QVariant &data) override; @@ -197,7 +197,7 @@ void ForwardView<ViewType>::nodeOrderChanged(const NodeListProperty &listPropert } template <class ViewType> -void ForwardView<ViewType>::importChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) +void ForwardView<ViewType>::importChanged(const Imports &addedImports, const Imports &removedImports) { for (const ViewTypePointer &view : std::as_const(m_targetViewList)) view->importChanged(addedImport, removedImport); diff --git a/src/plugins/qmldesigner/designercore/include/import.h b/src/plugins/qmldesigner/designercore/include/import.h index b221b861732..f0a5b8e7700 100644 --- a/src/plugins/qmldesigner/designercore/include/import.h +++ b/src/plugins/qmldesigner/designercore/include/import.h @@ -3,18 +3,49 @@ #pragma once +#include <utils/set_algorithm.h> + #include <QString> #include <QStringList> #include <QMetaType> #include "qmldesignercorelib_global.h" +#include <limits> + namespace QmlDesigner { +class Version +{ +public: + friend bool operator==(Version first, Version second) + { + return first.major == second.major && first.minor == second.minor; + } + + friend bool operator<(Version first, Version second) + { + return std::tie(first.major, first.minor) < std::tie(second.major, second.minor); + } + + friend bool operator>(Version first, Version second) { return second < first; } + friend bool operator<=(Version first, Version second) { return !(second < first); } + friend bool operator>=(Version first, Version second) { return !(first < second); } + + bool isEmpty() const + { + return major == std::numeric_limits<int>::max() || minor == std::numeric_limits<int>::max(); + } + +public: + int major = std::numeric_limits<int>::max(); + int minor = std::numeric_limits<int>::max(); +}; + class QMLDESIGNERCORE_EXPORT Import { public: - Import(); + Import() = default; static Import createLibraryImport(const QString &url, const QString &version = QString(), const QString &alias = QString(), const QStringList &importPaths = QStringList()); static Import createFileImport(const QString &file, const QString &version = QString(), const QString &alias = QString(), const QStringList &importPaths = QStringList()); @@ -26,23 +57,35 @@ public: bool hasVersion() const { return !m_version.isEmpty(); } bool hasAlias() const { return !m_alias.isEmpty(); } - QString url() const { return m_url; } - QString file() const { return m_file; } - QString version() const { return m_version; } - QString alias() const { return m_alias; } - QStringList importPaths() const { return m_importPathList; } + const QString &url() const { return m_url; } + const QString &file() const { return m_file; } + const QString &version() const { return m_version; } + const QString &alias() const { return m_alias; } + const QStringList &importPaths() const { return m_importPathList; } QString toString(bool skipAlias = false, bool skipVersion = false) const; QString toImportString() const; - bool operator==(const Import &other) const; bool isSameModule(const Import &other) const; int majorVersion() const; int minorVersion() const; + Version toVersion() const; static int majorFromVersion(const QString &version); static int minorFromVersion(const QString &version); + friend bool operator==(const Import &first, const Import &second) + { + return first.m_url == second.m_url && first.m_file == second.m_file + && (first.m_version == second.m_version || first.m_version.isEmpty() + || second.m_version.isEmpty()); + } + + friend bool operator<(const Import &first, const Import &second) + { + return std::tie(first.m_url, first.m_file) < std::tie(second.m_url, second.m_file); + } + private: Import(const QString &url, const QString &file, const QString &version, const QString &alias, const QStringList &importPaths); @@ -56,6 +99,23 @@ private: QMLDESIGNERCORE_EXPORT size_t qHash(const Import &import); +using Imports = QList<Import>; + +QMLDESIGNERCORE_EXPORT Imports difference(const Imports &first, const Imports &second); + +template<typename Callable> +void differenceCall(const Imports &first, const Imports &second, Callable callable) +{ + Imports difference; + difference.reserve(first.size()); + + std::set_difference(first.begin(), + first.end(), + second.begin(), + second.end(), + ::Utils::make_iterator(callable)); +} + } // namespace QmlDesigner Q_DECLARE_METATYPE(QmlDesigner::Import) diff --git a/src/plugins/qmldesigner/designercore/include/itemlibraryinfo.h b/src/plugins/qmldesigner/designercore/include/itemlibraryinfo.h index 0571e64a8ce..034af72410f 100644 --- a/src/plugins/qmldesigner/designercore/include/itemlibraryinfo.h +++ b/src/plugins/qmldesigner/designercore/include/itemlibraryinfo.h @@ -6,9 +6,10 @@ #include "qmldesignercorelib_global.h" #include "propertycontainer.h" +#include <memory> +#include <QIcon> #include <QPointer> #include <QSet> -#include <memory> namespace QmlDesigner { diff --git a/src/plugins/qmldesigner/designercore/include/metainfo.h b/src/plugins/qmldesigner/designercore/include/metainfo.h index b5ec3c0aa95..914be96db7b 100644 --- a/src/plugins/qmldesigner/designercore/include/metainfo.h +++ b/src/plugins/qmldesigner/designercore/include/metainfo.h @@ -16,6 +16,7 @@ namespace QmlDesigner { class ModelNode; class AbstractProperty; class ItemLibraryInfo; +class ExternalDependenciesInterface; namespace Internal { class MetaInfoPrivate; @@ -44,15 +45,14 @@ public: ItemLibraryInfo *itemLibraryInfo() const; public: - static MetaInfo global(); - static void clearGlobal(); - - static void setPluginPaths(const QStringList &paths); + static void initializeGlobal(const QStringList &pluginPaths, + const ExternalDependenciesInterface &externalDependencies); static void disableParseItemLibraryDescriptionsUgly(); // ugly hack around broken tests private: bool isGlobal() const; + static MetaInfo global(); private: MetaInfo(); diff --git a/src/plugins/qmldesigner/designercore/include/model.h b/src/plugins/qmldesigner/designercore/include/model.h index 9e6c7ca36ad..c2cd807a8a2 100644 --- a/src/plugins/qmldesigner/designercore/include/model.h +++ b/src/plugins/qmldesigner/designercore/include/model.h @@ -6,6 +6,7 @@ #include <qmldesignercorelib_global.h> #include <documentmessage.h> +#include <model/modelresourcemanagementinterface.h> #include <projectstorage/projectstoragefwd.h> #include <QMimeData> @@ -40,7 +41,7 @@ class RewriterView; class NodeInstanceView; class TextModifier; -using PropertyListType = QList<QPair<PropertyName, QVariant> >; +using PropertyListType = QList<QPair<PropertyName, QVariant>>; class QMLDESIGNERCORE_EXPORT Model : public QObject { @@ -57,21 +58,38 @@ class QMLDESIGNERCORE_EXPORT Model : public QObject public: enum ViewNotification { NotifyView, DoNotNotifyView }; - Model(ProjectStorage<Sqlite::Database> &projectStorage, + Model(ProjectStorageType &projectStorage, const TypeName &type, int major = 1, int minor = 1, - Model *metaInfoProxyModel = nullptr); - Model(const TypeName &typeName, int major = 1, int minor = 1, Model *metaInfoProxyModel = nullptr); + Model *metaInfoProxyModel = nullptr, + std::unique_ptr<ModelResourceManagementInterface> resourceManagement = {}); + Model(const TypeName &typeName, + int major = 1, + int minor = 1, + Model *metaInfoProxyModel = nullptr, + std::unique_ptr<ModelResourceManagementInterface> resourceManagement = {}); ~Model(); static ModelPointer create(const TypeName &typeName, int major = 1, int minor = 1, - Model *metaInfoProxyModel = nullptr) + Model *metaInfoProxyModel = nullptr, + std::unique_ptr<ModelResourceManagementInterface> resourceManagement = {}) + { + return ModelPointer( + new Model(typeName, major, minor, metaInfoProxyModel, std::move(resourceManagement))); + } + + static ModelPointer create(ProjectStorageType &projectStorage, + const TypeName &typeName, + int major = 1, + int minor = 1, + std::unique_ptr<ModelResourceManagementInterface> resourceManagement = {}) { - return ModelPointer(new Model(typeName, major, minor, metaInfoProxyModel)); + return ModelPointer( + new Model(projectStorage, typeName, major, minor, nullptr, std::move(resourceManagement))); } QUrl fileUrl() const; @@ -87,6 +105,7 @@ public: NodeMetaInfo flowViewFlowTransitionMetaInfo() const; NodeMetaInfo flowViewFlowWildcardMetaInfo() const; NodeMetaInfo fontMetaInfo() const; + NodeMetaInfo qtQuick3DBakedLightmapMetaInfo() const; NodeMetaInfo qtQuick3DDefaultMaterialMetaInfo() const; NodeMetaInfo qtQuick3DMaterialMetaInfo() const; NodeMetaInfo qtQuick3DModelMetaInfo() const; @@ -111,15 +130,17 @@ public: void attachView(AbstractView *view); void detachView(AbstractView *view, ViewNotification emitDetachNotify = NotifyView); + QList<ModelNode> allModelNodes() const; + // Editing sub-components: // Imports: - const QList<Import> &imports() const; - const QList<Import> &possibleImports() const; - const QList<Import> &usedImports() const; - void changeImports(const QList<Import> &importsToBeAdded, const QList<Import> &importsToBeRemoved); - void setPossibleImports(const QList<Import> &possibleImports); - void setUsedImports(const QList<Import> &usedImports); + const Imports &imports() const; + const Imports &possibleImports() const; + const Imports &usedImports() const; + void changeImports(const Imports &importsToBeAdded, const Imports &importsToBeRemoved); + void setPossibleImports(Imports possibleImports); + void setUsedImports(Imports usedImports); bool hasImport(const Import &import, bool ignoreAlias = true, bool allowHigherVersion = false) const; bool isImportPossible(const Import &import, bool ignoreAlias = true, bool allowHigherVersion = false) const; QString pathForImport(const Import &import); @@ -157,7 +178,7 @@ public: void startDrag(QMimeData *mimeData, const QPixmap &icon); void endDrag(); - NotNullPointer<const ProjectStorage<Sqlite::Database>> projectStorage() const; + NotNullPointer<const ProjectStorageType> projectStorage() const; private: template<const auto &moduleName, const auto &typeName> @@ -168,4 +189,4 @@ private: std::unique_ptr<Internal::ModelPrivate> d; }; -} +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/include/modelfwd.h b/src/plugins/qmldesigner/designercore/include/modelfwd.h new file mode 100644 index 00000000000..66712499958 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/include/modelfwd.h @@ -0,0 +1,42 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "../projectstorage/projectstoragefwd.h" +#include "qmldesignercorelib_exports.h" + +#include <utils/smallstringview.h> + +namespace QmlDesigner { +using PropertyName = QByteArray; +using PropertyNameList = QList<PropertyName>; +using TypeName = QByteArray; +using PropertyTypeList = QList<PropertyName>; +using IdName = QByteArray; +class Model; +class ModelNode; + +struct ModelDeleter +{ + QMLDESIGNERCORE_EXPORT void operator()(class Model *model); +}; + +using ModelPointer = std::unique_ptr<class Model, ModelDeleter>; + +constexpr bool useProjectStorage() +{ +#ifdef QDS_USE_PROJECTSTORAGE + return true; +#else + return false; +#endif +} + +#ifdef QDS_MODEL_USE_PROJECTSTORAGEINTERFACE +using ProjectStorageType = ProjectStorageInterface; +#else +using ProjectStorageType = ProjectStorage<Sqlite::Database>; +#endif + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/include/modelnode.h b/src/plugins/qmldesigner/designercore/include/modelnode.h index a48713ee864..e9891da620c 100644 --- a/src/plugins/qmldesigner/designercore/include/modelnode.h +++ b/src/plugins/qmldesigner/designercore/include/modelnode.h @@ -80,6 +80,10 @@ public: ModelNode(); ModelNode(const Internal::InternalNodePointer &internalNode, Model *model, const AbstractView *view); ModelNode(const ModelNode &modelNode, AbstractView *view); + ModelNode(const ModelNode &) = default; + ModelNode &operator=(const ModelNode &) = default; + ModelNode(ModelNode &&) = default; + ModelNode &operator=(ModelNode &&) noexcept = default; ~ModelNode(); TypeName type() const; @@ -266,6 +270,8 @@ QMLDESIGNERCORE_EXPORT bool operator !=(const ModelNode &firstNode, const ModelN QMLDESIGNERCORE_EXPORT bool operator <(const ModelNode &firstNode, const ModelNode &secondNode); QMLDESIGNERCORE_EXPORT QDebug operator<<(QDebug debug, const ModelNode &modelNode); QMLDESIGNERCORE_EXPORT QTextStream& operator<<(QTextStream &stream, const ModelNode &modelNode); + +using ModelNodes = QList<ModelNode>; } Q_DECLARE_METATYPE(QmlDesigner::ModelNode) diff --git a/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h b/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h index 632dbfce077..93b4e82a2a2 100644 --- a/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h +++ b/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h @@ -85,7 +85,7 @@ public: void fileUrlChanged(const QUrl &oldUrl, const QUrl &newUrl) override; void nodeIdChanged(const ModelNode& node, const QString& newId, const QString& oldId) override; void nodeOrderChanged(const NodeListProperty &listProperty) override; - void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override; + void importsChanged(const Imports &addedImports, const Imports &removedImports) override; void auxiliaryDataChanged(const ModelNode &node, AuxiliaryDataKeyView key, const QVariant &data) override; @@ -122,6 +122,7 @@ public: QImage statePreviewImage(const ModelNode &stateNode) const; void setTarget(ProjectExplorer::Target *newTarget); + ProjectExplorer::Target *target() const; void sendToken(const QString &token, int number, const QVector<ModelNode> &nodeVector); diff --git a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h index 69ac5f2a977..d7bc414e77e 100644 --- a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h +++ b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h @@ -6,7 +6,7 @@ #include "propertymetainfo.h" #include "qmldesignercorelib_global.h" -#include <projectstorage/projectstoragefwd.h> +#include <projectstorage/projectstorageinterface.h> #include <projectstorage/projectstoragetypes.h> #include <projectstorageids.h> @@ -32,11 +32,11 @@ class QMLDESIGNERCORE_EXPORT NodeMetaInfo public: NodeMetaInfo() = default; NodeMetaInfo(Model *model, const TypeName &typeName, int majorVersion, int minorVersion); - NodeMetaInfo(TypeId typeId, NotNullPointer<const ProjectStorage<Sqlite::Database>> projectStorage) + NodeMetaInfo(TypeId typeId, NotNullPointer<const ProjectStorageType> projectStorage) : m_typeId{typeId} , m_projectStorage{projectStorage} {} - NodeMetaInfo(NotNullPointer<const ProjectStorage<Sqlite::Database>> projectStorage) + NodeMetaInfo(NotNullPointer<const ProjectStorageType> projectStorage) : m_projectStorage{projectStorage} {} ~NodeMetaInfo(); @@ -44,7 +44,7 @@ public: bool isValid() const; explicit operator bool() const { return isValid(); } bool isFileComponent() const; - bool hasProperty(Utils::SmallStringView propertyName) const; + bool hasProperty(::Utils::SmallStringView propertyName) const; PropertyMetaInfos properties() const; PropertyMetaInfos localProperties() const; PropertyMetaInfo property(const PropertyName &propertyName) const; @@ -67,8 +67,6 @@ public: QString componentFileName() const; - bool availableInVersion(int majorVersion, int minorVersion) const; - bool isBasedOn(const NodeMetaInfo &metaInfo) const; bool isBasedOn(const NodeMetaInfo &metaInfo1, const NodeMetaInfo &metaInfo2) const; bool isBasedOn(const NodeMetaInfo &metaInfo1, @@ -126,9 +124,11 @@ public: bool isQtQuick3DEffect() const; bool isQtQuick3DInstanceList() const; bool isQtQuick3DInstanceListEntry() const; + bool isQtQuick3DLight() const; bool isQtQuick3DMaterial() const; bool isQtQuick3DModel() const; bool isQtQuick3DNode() const; + bool isQtQuick3DParticleAbstractShape() const; bool isQtQuick3DParticles3DAffector3D() const; bool isQtQuick3DParticles3DAttractor3D() const; bool isQtQuick3DParticles3DParticle3D() const; @@ -171,8 +171,6 @@ public: bool isQtQuickWindowWindow() const; bool isQtSafeRendererSafePicture() const; bool isQtSafeRendererSafeRendererPicture() const; - bool isQuick3DParticleAbstractShape() const; - bool isQuickStateOperation() const; bool isString() const; bool isSuitableForMouseAreaFill() const; bool isUrl() const; @@ -199,7 +197,7 @@ private: private: TypeId m_typeId; - NotNullPointer<const ProjectStorage<Sqlite::Database>> m_projectStorage = {}; + NotNullPointer<const ProjectStorageType> m_projectStorage = {}; mutable std::optional<Storage::Info::Type> m_typeData; QSharedPointer<class NodeMetaInfoPrivate> m_privateData; }; diff --git a/src/plugins/qmldesigner/designercore/include/plaintexteditmodifier.h b/src/plugins/qmldesigner/designercore/include/plaintexteditmodifier.h index 814137d8a77..4172e8921ee 100644 --- a/src/plugins/qmldesigner/designercore/include/plaintexteditmodifier.h +++ b/src/plugins/qmldesigner/designercore/include/plaintexteditmodifier.h @@ -91,8 +91,17 @@ public: TextEditor::TabSettings tabSettings() const override { return m_tabSettings; } -private: +protected: TextEditor::TabSettings m_tabSettings; }; +class QMLDESIGNERCORE_EXPORT IndentingTextEditModifier : public NotIndentingTextEditModifier +{ +public: + IndentingTextEditModifier(QTextDocument *document, const QTextCursor &textCursor); + + void indent(int offset, int length) override; + void indentLines(int startLine, int endLine) override; +}; + } diff --git a/src/plugins/qmldesigner/designercore/include/propertymetainfo.h b/src/plugins/qmldesigner/designercore/include/propertymetainfo.h index 6cda405f615..a3c928bc36f 100644 --- a/src/plugins/qmldesigner/designercore/include/propertymetainfo.h +++ b/src/plugins/qmldesigner/designercore/include/propertymetainfo.h @@ -25,10 +25,12 @@ public: PropertyMetaInfo() = default; PropertyMetaInfo(QSharedPointer<class NodeMetaInfoPrivate> nodeMetaInfoPrivateData, const PropertyName &propertyName); - PropertyMetaInfo(PropertyDeclarationId id, - NotNullPointer<const ProjectStorage<Sqlite::Database>> projectStorage) - : m_id{id} - , m_projectStorage{projectStorage} + PropertyMetaInfo([[maybe_unused]] PropertyDeclarationId id, + [[maybe_unused]] NotNullPointer<const ProjectStorageType> projectStorage) +#ifdef QDS_USE_PROJECTSTORAGE + : m_projectStorage{projectStorage} + , m_id{id} +#endif {} ~PropertyMetaInfo(); @@ -36,11 +38,11 @@ public: bool isValid() const { - if (useProjectStorage()) { - return bool(m_id); - } else { - return bool(m_nodeMetaInfoPrivateData); - } +#ifdef QDS_USE_PROJECTSTORAGE + return bool(m_id); +#else + return bool(m_nodeMetaInfoPrivateData); +#endif } PropertyName name() const; NodeMetaInfo propertyType() const; @@ -53,20 +55,28 @@ public: friend bool operator==(const PropertyMetaInfo &first, const PropertyMetaInfo &second) { +#ifdef QDS_USE_PROJECTSTORAGE + return first.m_id == second.m_id; +#else return first.m_nodeMetaInfoPrivateData == second.m_nodeMetaInfoPrivateData && first.name() == second.name(); +#endif } private: const Storage::Info::PropertyDeclaration &propertyData() const; TypeName propertyTypeName() const; + const NodeMetaInfoPrivate *nodeMetaInfoPrivateData() const; + const PropertyName &propertyName() const; private: + NotNullPointer<const ProjectStorageType> m_projectStorage; + mutable std::optional<Storage::Info::PropertyDeclaration> m_propertyData; + PropertyDeclarationId m_id; +#ifndef QDS_USE_PROJECTSTORAGE QSharedPointer<class NodeMetaInfoPrivate> m_nodeMetaInfoPrivateData; PropertyName m_propertyName; - PropertyDeclarationId m_id; - NotNullPointer<const ProjectStorage<Sqlite::Database>> m_projectStorage; - mutable std::optional<Storage::Info::PropertyDeclaration> m_propertyData; +#endif }; using PropertyMetaInfos = std::vector<PropertyMetaInfo>; diff --git a/src/plugins/qmldesigner/designercore/include/qmldesignercorelib_exports.h b/src/plugins/qmldesigner/designercore/include/qmldesignercorelib_exports.h new file mode 100644 index 00000000000..f5e1dbf4788 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/include/qmldesignercorelib_exports.h @@ -0,0 +1,12 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#if defined(QMLDESIGNERCORE_LIBRARY) +#define QMLDESIGNERCORE_EXPORT Q_DECL_EXPORT +#elif defined(QMLDESIGNERCORE_STATIC_LIBRARY) +#define QMLDESIGNERCORE_EXPORT +#else +#define QMLDESIGNERCORE_EXPORT Q_DECL_IMPORT +#endif diff --git a/src/plugins/qmldesigner/designercore/include/qmldesignercorelib_global.h b/src/plugins/qmldesigner/designercore/include/qmldesignercorelib_global.h index 05a1c2e20f6..7871b5df3a6 100644 --- a/src/plugins/qmldesigner/designercore/include/qmldesignercorelib_global.h +++ b/src/plugins/qmldesigner/designercore/include/qmldesignercorelib_global.h @@ -3,29 +3,16 @@ #pragma once +#include "modelfwd.h" +#include "qmldesignercorelib_exports.h" + #include <QtGlobal> #include <QList> -#include <utils/smallstringview.h> - #include <nodeinstanceglobal.h> - -// Unnecessary since core isn't a dll any more. - -#if defined(QMLDESIGNERCORE_LIBRARY) -#define QMLDESIGNERCORE_EXPORT Q_DECL_EXPORT -#elif defined(QMLDESIGNERCORE_STATIC_LIBRARY) -#define QMLDESIGNERCORE_EXPORT -#else -#define QMLDESIGNERCORE_EXPORT Q_DECL_IMPORT -#endif +#include <projectstorage/projectstoragefwd.h> namespace QmlDesigner { -using PropertyName = QByteArray; -using PropertyNameList = QList<PropertyName>; -using TypeName = QByteArray; -using PropertyTypeList = QList<PropertyName>; -using IdName = QByteArray; enum AnchorLineType { AnchorLineInvalid = 0x0, @@ -38,26 +25,12 @@ enum AnchorLineType { AnchorLineVerticalCenter = 0x20, AnchorLineBaseline = 0x40, - AnchorLineFill = AnchorLineLeft | AnchorLineRight | AnchorLineTop | AnchorLineBottom, + AnchorLineFill = AnchorLineLeft | AnchorLineRight | AnchorLineTop | AnchorLineBottom, AnchorLineCenter = AnchorLineVerticalCenter | AnchorLineHorizontalCenter, AnchorLineHorizontalMask = AnchorLineLeft | AnchorLineRight | AnchorLineHorizontalCenter, - AnchorLineVerticalMask = AnchorLineTop | AnchorLineBottom | AnchorLineVerticalCenter | AnchorLineBaseline, + AnchorLineVerticalMask = AnchorLineTop | AnchorLineBottom | AnchorLineVerticalCenter + | AnchorLineBaseline, AnchorLineAllMask = AnchorLineVerticalMask | AnchorLineHorizontalMask }; -struct ModelDeleter -{ - QMLDESIGNERCORE_EXPORT void operator()(class Model *model); -}; - -using ModelPointer = std::unique_ptr<class Model, ModelDeleter>; - -constexpr bool useProjectStorage() -{ -#ifdef QDS_USE_PROJECTSTORAGE - return true; -#else - return false; -#endif -} } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/include/qmlmodelnodefacade.h b/src/plugins/qmldesigner/designercore/include/qmlmodelnodefacade.h index 9c63fd040a2..27c4c86cc0b 100644 --- a/src/plugins/qmldesigner/designercore/include/qmlmodelnodefacade.h +++ b/src/plugins/qmldesigner/designercore/include/qmlmodelnodefacade.h @@ -14,13 +14,17 @@ class NodeInstanceView; class QMLDESIGNERCORE_EXPORT QmlModelNodeFacade { public: + QmlModelNodeFacade(const QmlModelNodeFacade &) = default; + QmlModelNodeFacade &operator=(const QmlModelNodeFacade &) = default; + QmlModelNodeFacade(QmlModelNodeFacade &&) noexcept = default; + QmlModelNodeFacade &operator=(QmlModelNodeFacade &&) noexcept = default; + virtual ~QmlModelNodeFacade() = default; operator ModelNode() const { return m_modelNode; } ModelNode modelNode() const { return m_modelNode; } bool hasModelNode() const; static bool isValidQmlModelNodeFacade(const ModelNode &modelNode); virtual bool isValid() const; explicit operator bool() const { return isValid(); } - virtual ~QmlModelNodeFacade(); QmlModelNodeFacade() = default; AbstractView *view() const; diff --git a/src/plugins/qmldesigner/designercore/include/rewriterview.h b/src/plugins/qmldesigner/designercore/include/rewriterview.h index 9387fdb345e..95e2e51d8a1 100644 --- a/src/plugins/qmldesigner/designercore/include/rewriterview.h +++ b/src/plugins/qmldesigner/designercore/include/rewriterview.h @@ -84,7 +84,7 @@ public: void rewriterBeginTransaction() override; void rewriterEndTransaction() override; - void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override; + void importsChanged(const Imports &addedImports, const Imports &removedImports) override; TextModifier *textModifier() const; void setTextModifier(TextModifier *textModifier); diff --git a/src/plugins/qmldesigner/designercore/include/subcomponentmanager.h b/src/plugins/qmldesigner/designercore/include/subcomponentmanager.h index 207f1b7104b..dbc89c1cccd 100644 --- a/src/plugins/qmldesigner/designercore/include/subcomponentmanager.h +++ b/src/plugins/qmldesigner/designercore/include/subcomponentmanager.h @@ -27,7 +27,7 @@ public: explicit SubComponentManager(Model *model, class ExternalDependenciesInterface &externalDependencies); - void update(const QUrl &fileUrl, const QList<Import> &imports); + void update(const QUrl &fileUrl, const Imports &imports); void addAndParseImport(const Import &import); QStringList qmlFiles() const; @@ -53,7 +53,7 @@ private: // functions private: // variables QFileSystemWatcher m_watcher; - QList<Import> m_imports; + Imports m_imports; // key: canonical directory path QMultiHash<QString,QString> m_dirToQualifier; QUrl m_filePath; diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index 8ebfaf52044..3befd6ec52c 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -625,7 +625,7 @@ void NodeInstanceView::nodeOrderChanged(const NodeListProperty &listProperty) m_nodeInstanceServer->reparentInstances(ReparentInstancesCommand(containerList)); } -void NodeInstanceView::importsChanged(const QList<Import> &/*addedImports*/, const QList<Import> &/*removedImports*/) +void NodeInstanceView::importsChanged(const Imports &/*addedImports*/, const Imports &/*removedImports*/) { restartProcess(); } @@ -1572,6 +1572,11 @@ void NodeInstanceView::setTarget(ProjectExplorer::Target *newTarget) } } +ProjectExplorer::Target *NodeInstanceView::target() const +{ + return m_currentTarget; +} + void NodeInstanceView::statePreviewImagesChanged(const StatePreviewImageChangedCommand &command) { if (!model()) diff --git a/src/plugins/qmldesigner/designercore/metainfo/itemlibraryinfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/itemlibraryinfo.cpp index b1473d6a4c0..cbde96e9926 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/itemlibraryinfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/itemlibraryinfo.cpp @@ -1,11 +1,12 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include "itemlibraryinfo.h" +#include "../include/itemlibraryinfo.h" #include "nodemetainfo.h" #include "qregularexpression.h" #include <invalidmetainfoexception.h> +#include <propertycontainer.h> #include <QSharedData> diff --git a/src/plugins/qmldesigner/designercore/metainfo/metainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/metainfo.cpp index 354ccd63c3f..144df144483 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/metainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/metainfo.cpp @@ -7,6 +7,7 @@ #include "metainforeader.h" #include "iwidgetplugin.h" +#include <externaldependenciesinterface.h> #include <invalidmetainfoexception.h> #include <coreplugin/messagebox.h> @@ -30,24 +31,26 @@ enum { namespace QmlDesigner { namespace Internal { - -static QString globalMetaInfoPath() +static QString globalMetaInfoPath(const ExternalDependenciesInterface &externalDependecies) { #ifdef SHARE_QML_PATH if (Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) return QLatin1String(SHARE_QML_PATH) + "/globalMetaInfo"; #endif - return Core::ICore::resourcePath("qmldesigner/globalMetaInfo").toString(); + return externalDependecies.resourcePath("qmldesigner/globalMetaInfo").toString(); } -Utils::FilePaths allGlobalMetaInfoFiles() +Utils::FilePaths allGlobalMetaInfoFiles(const ExternalDependenciesInterface &externalDependecies) { static Utils::FilePaths paths; if (!paths.isEmpty()) return paths; - QDirIterator it(globalMetaInfoPath(), { "*.metainfo" }, QDir::Files, QDirIterator::Subdirectories); + QDirIterator it(globalMetaInfoPath(externalDependecies), + {"*.metainfo"}, + QDir::Files, + QDirIterator::Subdirectories); while (it.hasNext()) paths.append(Utils::FilePath::fromString(it.next())); @@ -61,9 +64,9 @@ public: MetaInfoPrivate(MetaInfo *q); void clear(); - void initialize(); + void initialize(const ExternalDependenciesInterface &externalDependencies); - void parseItemLibraryDescriptions(); + void parseItemLibraryDescriptions(const ExternalDependenciesInterface &externalDependencies); QScopedPointer<ItemLibraryInfo> m_itemLibraryInfo; @@ -90,14 +93,14 @@ namespace { bool enableParseItemLibraryDescriptions = true; } -void MetaInfoPrivate::initialize() +void MetaInfoPrivate::initialize(const ExternalDependenciesInterface &externalDependencies) { if (enableParseItemLibraryDescriptions) - parseItemLibraryDescriptions(); + parseItemLibraryDescriptions(externalDependencies); m_isInitialized = true; } -void MetaInfoPrivate::parseItemLibraryDescriptions() +void MetaInfoPrivate::parseItemLibraryDescriptions(const ExternalDependenciesInterface &externalDependencies) { Internal::WidgetPluginManager pluginManager; for (const QString &pluginDir : std::as_const(m_q->s_pluginDirs)) @@ -107,7 +110,8 @@ void MetaInfoPrivate::parseItemLibraryDescriptions() Internal::MetaInfoReader reader(*m_q); try { reader.readMetaInfoFile(plugin->metaInfo()); - } catch (const InvalidMetaInfoException &e) { + } catch ([[maybe_unused]] const InvalidMetaInfoException &e) { +#ifndef UNIT_TESTS qWarning() << e.description(); const QString errorMessage = plugin->metaInfo() + QLatin1Char('\n') + QLatin1Char('\n') + reader.errors().join(QLatin1Char('\n')); @@ -115,15 +119,18 @@ void MetaInfoPrivate::parseItemLibraryDescriptions() QCoreApplication::translate("QmlDesigner::Internal::MetaInfoPrivate", "Invalid meta info"), errorMessage); +#endif } } - const Utils::FilePaths allMetaInfoFiles = allGlobalMetaInfoFiles(); + const Utils::FilePaths allMetaInfoFiles = allGlobalMetaInfoFiles(externalDependencies); for (const Utils::FilePath &path : allMetaInfoFiles) { Internal::MetaInfoReader reader(*m_q); try { reader.readMetaInfoFile(path.toString()); } catch (const InvalidMetaInfoException &e) { + Q_UNUSED(e); +#ifndef UNIT_TESTS qWarning() << e.description(); const QString errorMessage = path.toString() + QLatin1Char('\n') + QLatin1Char('\n') + reader.errors().join(QLatin1Char('\n')); @@ -131,6 +138,7 @@ void MetaInfoPrivate::parseItemLibraryDescriptions() QCoreApplication::translate("QmlDesigner::Internal::MetaInfoPrivate", "Invalid meta info"), errorMessage); +#endif } } } @@ -186,45 +194,26 @@ ItemLibraryInfo *MetaInfo::itemLibraryInfo() const return m_p->m_itemLibraryInfo.data(); } -/*! - Accesses the global meta information object. - You almost always want to use Model::metaInfo() instead. - - Internally, all meta information objects share this \e global object - where static QML type information is stored. - */ -MetaInfo MetaInfo::global() +void MetaInfo::initializeGlobal(const QStringList &pluginPaths, + const ExternalDependenciesInterface &externalDependencies) { QMutexLocker locker(&s_lock); if (!s_global.m_p->m_isInitialized) { + s_pluginDirs = pluginPaths, s_global.m_p = QSharedPointer<MetaInfoPrivate>(new MetaInfoPrivate(&s_global)); - s_global.m_p->initialize(); + s_global.m_p->initialize(externalDependencies); } - return s_global; -} - -/*! - Clears the global meta information object. - - This function should be called once on application shutdown to free static data structures. - */ -void MetaInfo::clearGlobal() -{ - if (s_global.m_p->m_isInitialized) - s_global.m_p->clear(); } -void MetaInfo::setPluginPaths(const QStringList &paths) +bool MetaInfo::isGlobal() const { - s_pluginDirs = paths; - global(); - clearGlobal(); + return (this->m_p == s_global.m_p); } -bool MetaInfo::isGlobal() const +MetaInfo MetaInfo::global() { - return (this->m_p == s_global.m_p); + return s_global; } bool operator==(const MetaInfo &first, const MetaInfo &second) diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index f4b505c4e70..bdb7b63392e 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -768,7 +768,7 @@ NodeMetaInfoPrivate::NodeMetaInfoPrivate(Model *model, TypeName type, int maj, i } } else { m_isFileComponent = true; - const Imports *imports = context()->imports(document()); + const auto *imports = context()->imports(document()); const ImportInfo importInfo = imports->info(lookupNameComponent().constLast(), context().data()); @@ -791,7 +791,7 @@ NodeMetaInfoPrivate::NodeMetaInfoPrivate(Model *model, TypeName type, int maj, i } else { // Special case for aliased types for the rewriter - const Imports *imports = context()->imports(document()); + const auto *imports = context()->imports(document()); const ImportInfo importInfo = imports->info(QString::fromUtf8(m_qualfiedTypeName), context().data()); if (importInfo.isValid()) { @@ -1198,7 +1198,7 @@ QString NodeMetaInfoPrivate::importDirectoryPath() const ModelManagerInterface *modelManager = ModelManagerInterface::instance(); if (isValid()) { - const Imports *imports = context()->imports(document()); + const auto *imports = context()->imports(document()); ImportInfo importInfo = imports->info(lookupNameComponent().constLast(), context().data()); if (importInfo.type() == ImportType::Directory) { @@ -1333,7 +1333,7 @@ void NodeMetaInfoPrivate::setupPrototypes() m_prototypes.append(description); } else { if (context()->lookupType(document(), {ov->className()})) { - const Imports *allImports = context()->imports(document()); + const auto *allImports = context()->imports(document()); ImportInfo importInfo = allImports->info(description.className, context().data()); if (importInfo.isValid()) { @@ -1619,16 +1619,20 @@ TypeName NodeMetaInfo::simplifiedTypeName() const int NodeMetaInfo::majorVersion() const { - if (isValid()) - return m_privateData->majorVersion(); + if constexpr (!useProjectStorage()) { + if (isValid()) + return m_privateData->majorVersion(); + } return -1; } int NodeMetaInfo::minorVersion() const { - if (isValid()) - return m_privateData->minorVersion(); + if constexpr (!useProjectStorage()) { + if (isValid()) + return m_privateData->minorVersion(); + } return -1; } @@ -1649,7 +1653,6 @@ QString NodeMetaInfo::importDirectoryPath() const return {}; } -#ifdef QDS_USE_PROJECTSTORAGE const Storage::Info::Type &NodeMetaInfo::typeData() const { if (!m_typeData) @@ -1657,20 +1660,6 @@ const Storage::Info::Type &NodeMetaInfo::typeData() const return *m_typeData; } -#endif - -bool NodeMetaInfo::availableInVersion(int majorVersion, int minorVersion) const -{ - if (!isValid()) - return false; - - if (majorVersion == -1 && minorVersion == -1) - return true; - - return (m_privateData->majorVersion() >= majorVersion) - || (majorVersion == m_privateData->majorVersion() - && m_privateData->minorVersion() >= minorVersion); -} bool NodeMetaInfo::isSubclassOf(const TypeName &type, int majorVersion, int minorVersion) const { @@ -1682,7 +1671,7 @@ bool NodeMetaInfo::isSubclassOf(const TypeName &type, int majorVersion, int mino if (typeName().isEmpty()) return false; - if (typeName() == type && availableInVersion(majorVersion, minorVersion)) + if (typeName() == type) return true; if (m_privateData->prototypeCachePositives().contains( @@ -1695,8 +1684,7 @@ bool NodeMetaInfo::isSubclassOf(const TypeName &type, int majorVersion, int mino const NodeMetaInfos superClassList = superClasses(); for (const NodeMetaInfo &superClass : superClassList) { - if (superClass.m_privateData->cleverCheckType(type) - && superClass.availableInVersion(majorVersion, minorVersion)) { + if (superClass.m_privateData->cleverCheckType(type)) { m_privateData->prototypeCachePositives().insert( stringIdentifier(type, majorVersion, minorVersion)); return true; @@ -1900,8 +1888,7 @@ bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo1, namespace { template<const char *moduleName, const char *typeName> -bool isBasedOnCommonType(NotNullPointer<const ProjectStorage<Sqlite::Database>> projectStorage, - TypeId typeId) +bool isBasedOnCommonType(NotNullPointer<const ProjectStorageType> projectStorage, TypeId typeId) { auto base = projectStorage->commonTypeId<moduleName, typeName>(); @@ -2047,16 +2034,6 @@ bool NodeMetaInfo::isQtQuickPropertyChanges() const } } -bool NodeMetaInfo::isQuickStateOperation() const -{ - if constexpr (useProjectStorage()) { - using namespace Storage::Info; - return isBasedOnCommonType<QtQuick_cppnative, QuickStateOperation>(m_projectStorage, m_typeId); - } else { - return isValid() && isSubclassOf("<cpp>.QQuickStateOperation"); - } -} - bool NodeMetaInfo::isQtSafeRendererSafeRendererPicture() const { if constexpr (useProjectStorage()) { @@ -2279,6 +2256,16 @@ bool NodeMetaInfo::isQtQuick3DInstanceListEntry() const } } +bool NodeMetaInfo::isQtQuick3DLight() const +{ + if constexpr (useProjectStorage()) { + using namespace Storage::Info; + return isBasedOnCommonType<QtQuick3D, Light>(m_projectStorage, m_typeId); + } else { + return isValid() && isSubclassOf("QtQuick3D.Light"); + } +} + bool NodeMetaInfo::isQtQuick3DInstanceList() const { if constexpr (useProjectStorage()) { @@ -2320,7 +2307,7 @@ bool NodeMetaInfo::isQtQuick3DParticles3DAttractor3D() const } } -bool NodeMetaInfo::isQuick3DParticleAbstractShape() const +bool NodeMetaInfo::isQtQuick3DParticleAbstractShape() const { if constexpr (useProjectStorage()) { using namespace Storage::Info; @@ -2405,7 +2392,8 @@ bool NodeMetaInfo::isQtQuickStateOperation() const { if constexpr (useProjectStorage()) { using namespace Storage::Info; - return isBasedOnCommonType<QtQuick, QQuickStateOperation>(m_projectStorage, m_typeId); + return isBasedOnCommonType<QtQuick_cppnative, QQuickStateOperation>(m_projectStorage, + m_typeId); } else { return isValid() && isSubclassOf("<cpp>.QQuickStateOperation"); } @@ -2834,12 +2822,13 @@ bool NodeMetaInfo::isEnumeration() const return false; } -PropertyMetaInfo::PropertyMetaInfo(QSharedPointer<NodeMetaInfoPrivate> nodeMetaInfoPrivateData, - const PropertyName &propertyName) +PropertyMetaInfo::PropertyMetaInfo( + [[maybe_unused]] QSharedPointer<NodeMetaInfoPrivate> nodeMetaInfoPrivateData, + [[maybe_unused]] const PropertyName &propertyName) +#ifndef QDS_USE_PROJECTSTORAGE : m_nodeMetaInfoPrivateData{nodeMetaInfoPrivateData} , m_propertyName{propertyName} - , m_projectStorage{nullptr} - +#endif {} PropertyMetaInfo::~PropertyMetaInfo() = default; @@ -2850,8 +2839,8 @@ NodeMetaInfo PropertyMetaInfo::propertyType() const return {propertyData().typeId, m_projectStorage}; } else { if (isValid()) - return NodeMetaInfo{m_nodeMetaInfoPrivateData->model(), - m_nodeMetaInfoPrivateData->propertyType(m_propertyName), + return NodeMetaInfo{nodeMetaInfoPrivateData()->model(), + nodeMetaInfoPrivateData()->propertyType(propertyName()), -1, -1}; } @@ -2864,7 +2853,7 @@ PropertyName PropertyMetaInfo::name() const if constexpr (useProjectStorage()) return PropertyName(Utils::SmallStringView(propertyData().name)); else - return m_propertyName; + return propertyName(); } bool PropertyMetaInfo::isWritable() const @@ -2872,7 +2861,7 @@ bool PropertyMetaInfo::isWritable() const if constexpr (useProjectStorage()) return !(propertyData().traits & Storage::PropertyDeclarationTraits::IsReadOnly); else - return isValid() && m_nodeMetaInfoPrivateData->isPropertyWritable(m_propertyName); + return isValid() && nodeMetaInfoPrivateData()->isPropertyWritable(propertyName()); } bool PropertyMetaInfo::isListProperty() const @@ -2880,7 +2869,7 @@ bool PropertyMetaInfo::isListProperty() const if constexpr (useProjectStorage()) return propertyData().traits & Storage::PropertyDeclarationTraits::IsList; else - return isValid() && m_nodeMetaInfoPrivateData->isPropertyList(m_propertyName); + return isValid() && nodeMetaInfoPrivateData()->isPropertyList(propertyName()); } bool PropertyMetaInfo::isEnumType() const @@ -2888,7 +2877,7 @@ bool PropertyMetaInfo::isEnumType() const if constexpr (useProjectStorage()) return propertyType().isEnumeration(); else - return isValid() && m_nodeMetaInfoPrivateData->isPropertyEnum(m_propertyName); + return isValid() && nodeMetaInfoPrivateData()->isPropertyEnum(propertyName()); } bool PropertyMetaInfo::isPrivate() const @@ -2896,7 +2885,7 @@ bool PropertyMetaInfo::isPrivate() const if constexpr (useProjectStorage()) return propertyData().name.startsWith("__"); else - return isValid() && m_propertyName.startsWith("__"); + return isValid() && propertyName().startsWith("__"); } bool PropertyMetaInfo::isPointer() const @@ -2904,7 +2893,7 @@ bool PropertyMetaInfo::isPointer() const if constexpr (useProjectStorage()) return propertyData().traits & Storage::PropertyDeclarationTraits::IsPointer; else - return isValid() && m_nodeMetaInfoPrivateData->isPropertyPointer(m_propertyName); + return isValid() && nodeMetaInfoPrivateData()->isPropertyPointer(propertyName()); } QVariant PropertyMetaInfo::castedValue(const QVariant &value) const @@ -2920,7 +2909,7 @@ QVariant PropertyMetaInfo::castedValue(const QVariant &value) const const TypeName &typeName = propertyTypeName(); - QVariant::Type typeId = m_nodeMetaInfoPrivateData->variantTypeId(m_propertyName); + QVariant::Type typeId = nodeMetaInfoPrivateData()->variantTypeId(propertyName()); if (variant.type() == QVariant::UserType && variant.userType() == ModelNode::variantUserType()) { @@ -3012,6 +3001,25 @@ TypeName PropertyMetaInfo::propertyTypeName() const return propertyType().typeName(); } +const NodeMetaInfoPrivate *PropertyMetaInfo::nodeMetaInfoPrivateData() const +{ +#ifndef QDS_USE_PROJECTSTORAGE + return m_nodeMetaInfoPrivateData.data(); +#else + return nullptr; +#endif +} + +const PropertyName &PropertyMetaInfo::propertyName() const +{ +#ifndef QDS_USE_PROJECTSTORAGE + return m_propertyName; +#else + static PropertyName dummy; + return dummy; +#endif +} + NodeMetaInfo NodeMetaInfo::commonBase(const NodeMetaInfo &metaInfo) const { for (const NodeMetaInfo &info : metaInfo.superClasses()) { diff --git a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp index e7feb0d4454..0afd0d735c7 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp @@ -491,7 +491,7 @@ QStringList SubComponentManager::qmlFiles() const return m_watcher.files(); } -void SubComponentManager::update(const QUrl &filePath, const QList<Import> &imports) +void SubComponentManager::update(const QUrl &filePath, const Imports &imports) { if (debug) qDebug() << Q_FUNC_INFO << filePath << imports.size(); diff --git a/src/plugins/qmldesigner/designercore/model/abstractproperty.cpp b/src/plugins/qmldesigner/designercore/model/abstractproperty.cpp index 3bccf54660c..00541e12e2f 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractproperty.cpp @@ -39,7 +39,6 @@ AbstractProperty::AbstractProperty(const Internal::InternalPropertyPointer &prop m_model(model), m_view(view) { - Q_ASSERT(!m_model || m_view); } AbstractProperty::AbstractProperty(const AbstractProperty &property, AbstractView *view) diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp index b9f0315c196..9967d49ee7b 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp @@ -64,8 +64,12 @@ RewriterTransaction AbstractView::beginRewriterTransaction(const QByteArray &ide ModelNode AbstractView::createModelNode(const TypeName &typeName) { - const NodeMetaInfo metaInfo = model()->metaInfo(typeName); - return createModelNode(typeName, metaInfo.majorVersion(), metaInfo.minorVersion()); + if constexpr (useProjectStorage()) { + return createModelNode(typeName, -1, -1); + } else { + const NodeMetaInfo metaInfo = model()->metaInfo(typeName); + return createModelNode(typeName, metaInfo.majorVersion(), metaInfo.minorVersion()); + } } ModelNode AbstractView::createModelNode(const TypeName &typeName, @@ -143,9 +147,6 @@ The default implementation is setting the reference of the model to the view. void AbstractView::modelAttached(Model *model) { setModel(model); - - if (model) - model->d->updateEnabledViews(); } /*! @@ -315,15 +316,15 @@ void AbstractView::nodeTypeChanged(const ModelNode & /*node*/, const TypeName & } -void AbstractView::importsChanged(const QList<Import> &/*addedImports*/, const QList<Import> &/*removedImports*/) +void AbstractView::importsChanged(const Imports &/*addedImports*/, const Imports &/*removedImports*/) { } -void AbstractView::possibleImportsChanged(const QList<Import> &/*possibleImports*/) +void AbstractView::possibleImportsChanged(const Imports &/*possibleImports*/) { } -void AbstractView::usedImportsChanged(const QList<Import> &/*usedImports*/) +void AbstractView::usedImportsChanged(const Imports &/*usedImports*/) { } @@ -617,9 +618,6 @@ bool AbstractView::isEnabled() const void AbstractView::setEnabled(bool b) { m_enabled = b; - - if (model()) - model()->d->updateEnabledViews(); } QList<ModelNode> AbstractView::allModelNodes() const @@ -893,32 +891,28 @@ QmlTimeline AbstractView::currentTimeline() const static int getMinorVersionFromImport(const Model *model) { - const QList<Import> imports = model->imports(); - for (const Import &import : imports) { - if (import.isLibraryImport() && import.url() == "QtQuick") { - const QString versionString = import.version(); - if (versionString.contains(".")) { - const QString minorVersionString = versionString.split(".").constLast(); - return minorVersionString.toInt(); - } - } - } + const Imports &imports = model->imports(); + + auto found = std::find_if(imports.begin(), imports.end(), [](const auto &import) { + return import.url() == "QtQuick"; + }); + + if (found != imports.end()) + return found->minorVersion(); return -1; } static int getMajorVersionFromImport(const Model *model) { - const QList<Import> imports = model->imports(); - for (const Import &import : imports) { - if (import.isLibraryImport() && import.url() == QStringLiteral("QtQuick")) { - const QString versionString = import.version(); - if (versionString.contains(QStringLiteral("."))) { - const QString majorVersionString = versionString.split(QStringLiteral(".")).constFirst(); - return majorVersionString.toInt(); - } - } - } + const Imports &imports = model->imports(); + + auto found = std::find_if(imports.begin(), imports.end(), [](const auto &import) { + return import.url() == "QtQuick"; + }); + + if (found != imports.end()) + return found->majorVersion(); return -1; } diff --git a/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp b/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp index 77dbec5e25a..45d4e4cf20e 100644 --- a/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp @@ -56,7 +56,7 @@ void BindingProperty::setExpression(const QString &expression) } if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isBindingProperty()) - privateModel()->removeProperty(internalNode()->property(name())); + privateModel()->removePropertyAndRelatedResources(internalNode()->property(name())); privateModel()->setBindingProperty(internalNode(), name(), expression); } @@ -341,7 +341,7 @@ void BindingProperty::setDynamicTypeNameAndExpression(const TypeName &typeName, } if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isBindingProperty()) - privateModel()->removeProperty(internalNode()->property(name())); + privateModel()->removePropertyAndRelatedResources(internalNode()->property(name())); privateModel()->setDynamicBindingProperty(internalNode(), name(), typeName, expression); } diff --git a/src/plugins/qmldesigner/designercore/model/import.cpp b/src/plugins/qmldesigner/designercore/model/import.cpp index 3a6914d3258..54b18810437 100644 --- a/src/plugins/qmldesigner/designercore/model/import.cpp +++ b/src/plugins/qmldesigner/designercore/model/import.cpp @@ -5,9 +5,9 @@ #include <QHash> -namespace QmlDesigner { +#include <QStringView> -Import::Import() = default; +namespace QmlDesigner { Import Import::createLibraryImport(const QString &url, const QString &version, const QString &alias, const QStringList &importPaths) { @@ -62,11 +62,6 @@ QString Import::toString(bool skipAlias, bool skipVersion) const return result; } -bool Import::operator==(const Import &other) const -{ - return url() == other.url() && file() == other.file() && (version() == other.version() || version().isEmpty() || other.version().isEmpty()); -} - bool Import::isSameModule(const Import &other) const { if (isLibraryImport()) @@ -85,21 +80,56 @@ int Import::minorVersion() const return minorFromVersion(m_version); } +Version Import::toVersion() const +{ + auto found = std::find(m_version.begin(), m_version.end(), u'.'); + if (found == m_version.end()) + return {}; + + QStringView majorVersionToken{m_version.begin(), found}; + bool canConvertMajor = false; + int majorVersion = majorVersionToken.toInt(&canConvertMajor); + + QStringView minorVersionToken{std::next(found), m_version.end()}; + bool canConvertMinor = false; + int minorVersion = minorVersionToken.toInt(&canConvertMinor); + + if (canConvertMajor && canConvertMinor) + return {majorVersion, minorVersion}; + + return {}; +} + int Import::majorFromVersion(const QString &version) { - if (version.isEmpty()) + auto found = std::find(version.begin(), version.end(), u'.'); + if (found == version.end()) return -1; - return version.split('.').first().toInt(); + + QStringView majorVersionToken{version.begin(), found}; + bool canConvert = false; + int majorVersion = majorVersionToken.toInt(&canConvert); + + if (canConvert) + return majorVersion; + + return -1; } int Import::minorFromVersion(const QString &version) { - if (version.isEmpty()) + auto found = std::find(version.begin(), version.end(), u'.'); + if (found == version.end()) return -1; - const QStringList parts = version.split('.'); - if (parts.size() < 2) - return -1; - return parts[1].toInt(); + + QStringView minorVersionToken{std::next(found), version.end()}; + bool canConvert = false; + int minorVersion = minorVersionToken.toInt(&canConvert); + + if (canConvert) + return minorVersion; + + return -1; } size_t qHash(const Import &import) @@ -107,4 +137,17 @@ size_t qHash(const Import &import) return ::qHash(import.url()) ^ ::qHash(import.file()) ^ ::qHash(import.version()) ^ ::qHash(import.alias()); } +Imports difference(const Imports &first, const Imports &second) +{ + Imports difference; + difference.reserve(first.size()); + + std::set_difference(first.begin(), + first.end(), + second.begin(), + second.end(), + std::back_inserter(difference)); + + return difference; +} } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index a234c11e1fe..844454ae55e 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -37,6 +37,7 @@ #include <QHashIterator> #include <QPointer> #include <QRegularExpression> +#include <qcompilerdetection.h> /*! \defgroup CoreModel @@ -57,13 +58,15 @@ namespace QmlDesigner { namespace Internal { ModelPrivate::ModelPrivate(Model *model, - ProjectStorage<Sqlite::Database> &projectStorage, + ProjectStorageType &projectStorage, const TypeName &typeName, int major, int minor, - Model *metaInfoProxyModel) + Model *metaInfoProxyModel, + std::unique_ptr<ModelResourceManagementInterface> resourceManagement) : projectStorage{&projectStorage} , m_model{model} + , m_resourceManagement{std::move(resourceManagement)} { m_metaInfoProxyModel = metaInfoProxyModel; @@ -74,9 +77,14 @@ ModelPrivate::ModelPrivate(Model *model, m_currentTimelineNode = m_rootInternalNode; } -ModelPrivate::ModelPrivate( - Model *model, const TypeName &typeName, int major, int minor, Model *metaInfoProxyModel) +ModelPrivate::ModelPrivate(Model *model, + const TypeName &typeName, + int major, + int minor, + Model *metaInfoProxyModel, + std::unique_ptr<ModelResourceManagementInterface> resourceManagement) : m_model(model) + , m_resourceManagement{std::move(resourceManagement)} { m_metaInfoProxyModel = metaInfoProxyModel; @@ -95,7 +103,6 @@ void ModelPrivate::detachAllViews() detachView(view.data(), true); m_viewList.clear(); - updateEnabledViews(); if (m_nodeInstanceView) { m_nodeInstanceView->modelAboutToBeDetached(m_model); @@ -108,10 +115,10 @@ void ModelPrivate::detachAllViews() } } -void ModelPrivate::changeImports(const QList<Import> &toBeAddedImportList, - const QList<Import> &toBeRemovedImportList) +void ModelPrivate::changeImports(const Imports &toBeAddedImportList, + const Imports &toBeRemovedImportList) { - QList<Import> removedImportList; + Imports removedImportList; for (const Import &import : toBeRemovedImportList) { if (m_imports.contains(import)) { removedImportList.append(import); @@ -119,7 +126,7 @@ void ModelPrivate::changeImports(const QList<Import> &toBeAddedImportList, } } - QList<Import> addedImportList; + Imports addedImportList; for (const Import &import : toBeAddedImportList) { if (!m_imports.contains(import)) { addedImportList.append(import); @@ -127,12 +134,13 @@ void ModelPrivate::changeImports(const QList<Import> &toBeAddedImportList, } } + std::sort(m_imports.begin(), m_imports.end()); + if (!removedImportList.isEmpty() || !addedImportList.isEmpty()) notifyImportsChanged(addedImportList, removedImportList); } -void ModelPrivate::notifyImportsChanged(const QList<Import> &addedImports, - const QList<Import> &removedImports) +void ModelPrivate::notifyImportsChanged(const Imports &addedImports, const Imports &removedImports) { bool resetModel = false; QString description; @@ -157,7 +165,7 @@ void ModelPrivate::notifyImportsChanged(const QList<Import> &addedImports, resetModelByRewriter(description); } -void ModelPrivate::notifyPossibleImportsChanged(const QList<Import> &possibleImports) +void ModelPrivate::notifyPossibleImportsChanged(const Imports &possibleImports) { for (const QPointer<AbstractView> &view : enabledViews()) { Q_ASSERT(view != nullptr); @@ -165,7 +173,7 @@ void ModelPrivate::notifyPossibleImportsChanged(const QList<Import> &possibleImp } } -void ModelPrivate::notifyUsedImportsChanged(const QList<Import> &usedImports) +void ModelPrivate::notifyUsedImportsChanged(const Imports &usedImports) { for (const QPointer<AbstractView> &view : enabledViews()) { Q_ASSERT(view != nullptr); @@ -210,6 +218,23 @@ void ModelPrivate::changeNodeType(const InternalNodePointer &node, const TypeNam } } +namespace { +QT_WARNING_PUSH +QT_WARNING_DISABLE_CLANG("-Wunneeded-internal-declaration") + +std::pair<Utils::SmallStringView, Utils::SmallStringView> decomposeTypePath(Utils::SmallStringView typeName) +{ + auto found = std::find(typeName.rbegin(), typeName.rend(), '.'); + + if (found == typeName.rend()) + return {}; + + return {{typeName.begin(), std::prev(found.base())}, {found.base(), typeName.end()}}; +} + +QT_WARNING_POP +} // namespace + InternalNodePointer ModelPrivate::createNode(const TypeName &typeName, int majorVersion, int minorVersion, @@ -229,6 +254,15 @@ InternalNodePointer ModelPrivate::createNode(const TypeName &typeName, internalId = m_internalIdCounter++; auto newNode = std::make_shared<InternalNode>(typeName, majorVersion, minorVersion, internalId); + + if constexpr (useProjectStorage()) { + auto [moduleName, shortTypeName] = decomposeTypePath(typeName); + ModuleId moduleId = projectStorage->moduleId(moduleName); + newNode->typeId = projectStorage->typeId(moduleId, + shortTypeName, + Storage::Version{majorVersion, minorVersion}); + } + newNode->nodeSourceType = nodeSourceType; newNode->behaviorPropertyName = behaviorPropertyName; @@ -252,7 +286,9 @@ InternalNodePointer ModelPrivate::createNode(const TypeName &typeName, notifyNodeCreated(newNode); if (!newNode->propertyNameList().isEmpty()) - notifyVariantPropertiesChanged(newNode, newNode->propertyNameList(), AbstractView::PropertiesAdded); + notifyVariantPropertiesChanged(newNode, + newNode->propertyNameList(), + AbstractView::PropertiesAdded); return newNode; } @@ -271,9 +307,27 @@ void ModelPrivate::removeNodeFromModel(const InternalNodePointer &node) m_internalIdNodeHash.remove(node->internalId); } -const QList<QPointer<AbstractView>> ModelPrivate::enabledViews() const +EnabledViewRange ModelPrivate::enabledViews() const { - return m_enabledViewList; + return EnabledViewRange{m_viewList}; +} + +void ModelPrivate::handleResourceSet(const ModelResourceSet &resourceSet) +{ + for (const ModelNode &node : resourceSet.removeModelNodes) { + if (node) + removeNode(node.m_internalNode); + } + + for (const AbstractProperty &property : resourceSet.removeProperties) { + if (property) + removeProperty(property.m_internalNode->property(property.m_propertyName)); + } + + for (const auto &[property, expression] : resourceSet.setExpressions) { + if (property) + setBindingProperty(property.m_internalNode, property.m_propertyName, expression); + } } void ModelPrivate::removeAllSubNodes(const InternalNodePointer &node) @@ -282,10 +336,16 @@ void ModelPrivate::removeAllSubNodes(const InternalNodePointer &node) removeNodeFromModel(subNode); } -void ModelPrivate::removeNode(const InternalNodePointer &node) +void ModelPrivate::removeNodeAndRelatedResources(const InternalNodePointer &node) { - Q_ASSERT(node); + if (m_resourceManagement) + handleResourceSet(m_resourceManagement->removeNode(ModelNode{node, m_model, nullptr})); + else + removeNode(node); +} +void ModelPrivate::removeNode(const InternalNodePointer &node) +{ AbstractView::PropertyChangeFlags propertyChangeFlags = AbstractView::NoAdditionalChanges; notifyNodeAboutToBeRemoved(node); @@ -726,7 +786,6 @@ void ModelPrivate::detachView(AbstractView *view, bool notifyView) if (notifyView) view->modelAboutToBeDetached(m_model); m_viewList.removeOne(view); - updateEnabledViews(); } void ModelPrivate::notifyNodeCreated(const InternalNodePointer &newInternalNodePointer) @@ -1062,6 +1121,15 @@ static QList<PropertyPair> toPropertyPairList(const QList<InternalPropertyPointe return propertyPairList; } +void ModelPrivate::removePropertyAndRelatedResources(const InternalPropertyPointer &property) +{ + if (m_resourceManagement) + handleResourceSet( + m_resourceManagement->removeProperty(AbstractProperty{property, m_model, nullptr})); + else + removeProperty(property); +} + void ModelPrivate::removeProperty(const InternalPropertyPointer &property) { notifyPropertiesAboutToBeRemoved({property}); @@ -1289,13 +1357,6 @@ InternalNodePointer ModelPrivate::currentTimelineNode() const return m_currentTimelineNode; } -void ModelPrivate::updateEnabledViews() -{ - m_enabledViewList = Utils::filtered(m_viewList, [](QPointer<AbstractView> view) { - return view->isEnabled(); - }); -} - InternalNodePointer ModelPrivate::nodeForId(const QString &id) const { return m_idNodeHash.value(id); @@ -1385,89 +1446,80 @@ void WriteLocker::lock(Model *model) } // namespace Internal -Model::Model(ProjectStorage<Sqlite::Database> &projectStorage, +Model::Model(ProjectStorageType &projectStorage, const TypeName &typeName, int major, int minor, - Model *metaInfoProxyModel) + Model *metaInfoProxyModel, + std::unique_ptr<ModelResourceManagementInterface> resourceManagement) : d(std::make_unique<Internal::ModelPrivate>( - this, projectStorage, typeName, major, minor, metaInfoProxyModel)) + this, projectStorage, typeName, major, minor, metaInfoProxyModel, std::move(resourceManagement))) {} -Model::Model(const TypeName &typeName, int major, int minor, Model *metaInfoProxyModel) - : d(std::make_unique<Internal::ModelPrivate>(this, typeName, major, minor, metaInfoProxyModel)) +Model::Model(const TypeName &typeName, + int major, + int minor, + Model *metaInfoProxyModel, + std::unique_ptr<ModelResourceManagementInterface> resourceManagement) + : d(std::make_unique<Internal::ModelPrivate>( + this, typeName, major, minor, metaInfoProxyModel, std::move(resourceManagement))) {} Model::~Model() = default; -const QList<Import> &Model::imports() const +const Imports &Model::imports() const { return d->imports(); } -const QList<Import> &Model::possibleImports() const +const Imports &Model::possibleImports() const { return d->m_possibleImportList; } -const QList<Import> &Model::usedImports() const +const Imports &Model::usedImports() const { return d->m_usedImportList; } -void Model::changeImports(const QList<Import> &importsToBeAdded, - const QList<Import> &importsToBeRemoved) +void Model::changeImports(const Imports &importsToBeAdded, const Imports &importsToBeRemoved) { d->changeImports(importsToBeAdded, importsToBeRemoved); } -void Model::setPossibleImports(const QList<Import> &possibleImports) +void Model::setPossibleImports(Imports possibleImports) { + std::sort(possibleImports.begin(), possibleImports.end()); + if (d->m_possibleImportList != possibleImports) { - d->m_possibleImportList = possibleImports; - d->notifyPossibleImportsChanged(possibleImports); + d->m_possibleImportList = std::move(possibleImports); + d->notifyPossibleImportsChanged(d->m_possibleImportList); } } -void Model::setUsedImports(const QList<Import> &usedImports) +void Model::setUsedImports(Imports usedImports) { + std::sort(usedImports.begin(), usedImports.end()); + if (d->m_usedImportList != usedImports) { - d->m_usedImportList = usedImports; - d->notifyUsedImportsChanged(usedImports); + d->m_usedImportList = std::move(usedImports); + d->notifyUsedImportsChanged(d->m_usedImportList); } } -static bool compareVersions(const QString &version1, const QString &version2, bool allowHigherVersion) +static bool compareVersions(const Import &import1, const Import &import2, bool allowHigherVersion) { + auto version1 = import1.toVersion(); + auto version2 = import2.toVersion(); + if (version2.isEmpty()) return true; if (version1 == version2) return true; if (!allowHigherVersion) return false; - QStringList version1List = version1.split(QLatin1Char('.')); - QStringList version2List = version2.split(QLatin1Char('.')); - if (version1List.count() == 2 && version2List.count() == 2) { - bool ok; - int major1 = version1List.constFirst().toInt(&ok); - if (!ok) - return false; - int major2 = version2List.constFirst().toInt(&ok); - if (!ok) - return false; - if (major1 >= major2) { - int minor1 = version1List.constLast().toInt(&ok); - if (!ok) - return false; - int minor2 = version2List.constLast().toInt(&ok); - if (!ok) - return false; - if (minor1 >= minor2) - return true; - } - } - return false; + return version1 >= version2; } bool Model::hasImport(const Import &import, bool ignoreAlias, bool allowHigherVersion) const @@ -1485,7 +1537,7 @@ bool Model::hasImport(const Import &import, bool ignoreAlias, bool allowHigherVe } if (existingImport.isLibraryImport() && import.isLibraryImport()) { if (existingImport.url() == import.url() - && compareVersions(existingImport.version(), import.version(), allowHigherVersion)) { + && compareVersions(existingImport, import, allowHigherVersion)) { return true; } } @@ -1627,7 +1679,7 @@ void Model::endDrag() d->notifyDragEnded(); } -NotNullPointer<const ProjectStorage<Sqlite::Database>> Model::projectStorage() const +NotNullPointer<const ProjectStorageType> Model::projectStorage() const { return d->projectStorage; } @@ -1659,7 +1711,7 @@ bool Model::isImportPossible(const Import &import, bool ignoreAlias, bool allowH if (possibleImport.isLibraryImport() && import.isLibraryImport()) { if (possibleImport.url() == import.url() - && compareVersions(possibleImport.version(), import.version(), allowHigherVersion)) { + && compareVersions(possibleImport, import, allowHigherVersion)) { return true; } } @@ -1694,7 +1746,7 @@ Import Model::highestPossibleImport(const QString &importPath) for (const Import &import : possibleImports()) { if (import.url() == importPath) { - if (candidate.isEmpty() || compareVersions(import.version(), candidate.version(), true)) + if (candidate.isEmpty() || compareVersions(import, candidate, true)) candidate = import; } } @@ -1804,7 +1856,7 @@ void Model::setMetaInfo(const MetaInfo &metaInfo) template<const auto &moduleName, const auto &typeName> NodeMetaInfo Model::createNodeMetaInfo() const { - auto typeId = d->projectStorage->commonTypeCache.typeId<moduleName, typeName>(); + auto typeId = d->projectStorage->commonTypeCache().typeId<moduleName, typeName>(); return {typeId, d->projectStorage}; } @@ -1939,6 +1991,16 @@ NodeMetaInfo Model::qtQuick3DTextureMetaInfo() const } } +NodeMetaInfo Model::qtQuick3DBakedLightmapMetaInfo() const +{ + if constexpr (useProjectStorage()) { + using namespace Storage::Info; + return createNodeMetaInfo<QtQuick3D, BakedLightmap>(); + } else { + return metaInfo("QtQuick3D.BakedLightmap"); + } +} + NodeMetaInfo Model::qtQuick3DMaterialMetaInfo() const { if constexpr (useProjectStorage()) { @@ -2070,8 +2132,7 @@ NodeMetaInfo Model::metaInfo(const TypeName &typeName, int majorVersion, int min ModuleId moduleId = d->projectStorage->moduleId(module); TypeId typeId = d->projectStorage->typeId(moduleId, componentName, - Storage::Synchronization::Version{majorVersion, - minorVersion}); + Storage::Version{majorVersion, minorVersion}); return NodeMetaInfo(typeId, d->projectStorage); } else { return NodeMetaInfo(metaInfoProxyModel(), typeName, majorVersion, minorVersion); @@ -2143,4 +2204,9 @@ void Model::detachView(AbstractView *view, ViewNotification emitDetachNotify) d->detachView(view, emitNotify); } +QList<ModelNode> Model::allModelNodes() const +{ + return QmlDesigner::toModelNodeList(d->allNodes(), nullptr); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/model/model_p.h b/src/plugins/qmldesigner/designercore/model/model_p.h index b08b7388518..b802d703f25 100644 --- a/src/plugins/qmldesigner/designercore/model/model_p.h +++ b/src/plugins/qmldesigner/designercore/model/model_p.h @@ -3,17 +3,20 @@ #pragma once +#include "qmldesignercorelib_global.h" + +#include "abstractview.h" +#include "metainfo.h" +#include "modelnode.h" +#include "skipiterator.h" + #include <QList> #include <QPointer> #include <QSet> #include <QUrl> #include <QVector3D> -#include "modelnode.h" -#include "abstractview.h" -#include "metainfo.h" - -#include "qmldesignercorelib_global.h" +#include <algorithm> QT_BEGIN_NAMESPACE class QPlainTextEdit; @@ -64,7 +67,28 @@ private: QPointer<ModelPrivate> m_model; }; -class ModelPrivate : public QObject { +struct Increment +{ + using iterator = QList<QPointer<AbstractView>>::const_iterator; + auto operator()(iterator current) { + return std::find_if(std::next(current), + end, + [] (iterator::reference &view) { return view && view->isEnabled(); }); + } + + iterator end; +}; + +class EnabledViewRange : public SkipRange<QList<QPointer<AbstractView>>, Increment> +{ +public: + EnabledViewRange(const container &views) + : base{views, Increment{views.end()}} + {} +}; + +class ModelPrivate : public QObject +{ Q_OBJECT friend Model; @@ -73,12 +97,18 @@ class ModelPrivate : public QObject { public: ModelPrivate(Model *model, - ProjectStorage<Sqlite::Database> &projectStorage, + ProjectStorageType &projectStorage, + const TypeName &type, + int major, + int minor, + Model *metaInfoProxyModel, + std::unique_ptr<ModelResourceManagementInterface> resourceManagement); + ModelPrivate(Model *model, const TypeName &type, int major, int minor, - Model *metaInfoProxyModel); - ModelPrivate(Model *model, const TypeName &type, int major, int minor, Model *metaInfoProxyModel); + Model *metaInfoProxyModel, + std::unique_ptr<ModelResourceManagementInterface> resourceManagement); ~ModelPrivate() override; @@ -96,6 +126,7 @@ public: bool isRootNode = false); /*factory methods for internal use in model and rewriter*/ + void removeNodeAndRelatedResources(const InternalNodePointer &node); void removeNode(const InternalNodePointer &node); void changeNodeId(const InternalNodePointer &node, const QString &id); void changeNodeType(const InternalNodePointer &node, const TypeName &typeName, int majorVersion, int minorVersion); @@ -202,17 +233,16 @@ public: void resetModelByRewriter(const QString &description); // Imports: - const QList<Import> &imports() const { return m_imports; } - void addImport(const Import &import); - void removeImport(const Import &import); - void changeImports(const QList<Import> &importsToBeAdded, const QList<Import> &importToBeRemoved); - void notifyImportsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports); - void notifyPossibleImportsChanged(const QList<Import> &possibleImports); - void notifyUsedImportsChanged(const QList<Import> &usedImportsChanged); + const Imports &imports() const { return m_imports; } + void changeImports(const Imports &importsToBeAdded, const Imports &importToBeRemoved); + void notifyImportsChanged(const Imports &addedImports, const Imports &removedImports); + void notifyPossibleImportsChanged(const Imports &possibleImports); + void notifyUsedImportsChanged(const Imports &usedImportsChanged); //node state property manipulation void addProperty(const InternalNodePointer &node, const PropertyName &name); void setPropertyValue(const InternalNodePointer &node,const PropertyName &name, const QVariant &value); + void removePropertyAndRelatedResources(const InternalPropertyPointer &property); void removeProperty(const InternalPropertyPointer &property); void setBindingProperty(const InternalNodePointer &node, const PropertyName &name, const QString &expression); @@ -251,11 +281,6 @@ public: InternalNodePointer currentStateNode() const; InternalNodePointer currentTimelineNode() const; - void updateEnabledViews(); - -public: - NotNullPointer<ProjectStorage<Sqlite::Database>> projectStorage = nullptr; - private: void removePropertyWithoutNotification(const InternalPropertyPointer &property); void removeAllSubNodes(const InternalNodePointer &node); @@ -264,16 +289,19 @@ private: QList<ModelNode> toModelNodeList(const QList<InternalNodePointer> &nodeList, AbstractView *view) const; QVector<ModelNode> toModelNodeVector(const QVector<InternalNodePointer> &nodeVector, AbstractView *view) const; QVector<InternalNodePointer> toInternalNodeVector(const QVector<ModelNode> &modelNodeVector) const; - const QList<QPointer<AbstractView>> enabledViews() const; + EnabledViewRange enabledViews() const; + void handleResourceSet(const ModelResourceSet &resourceSet); + +public: + NotNullPointer<ProjectStorageType> projectStorage = nullptr; private: Model *m_model = nullptr; MetaInfo m_metaInfo; - QList<Import> m_imports; - QList<Import> m_possibleImportList; - QList<Import> m_usedImportList; + Imports m_imports; + Imports m_possibleImportList; + Imports m_usedImportList; QList<QPointer<AbstractView>> m_viewList; - QList<QPointer<AbstractView>> m_enabledViewList; QList<InternalNodePointer> m_selectedInternalNodeList; QHash<QString,InternalNodePointer> m_idNodeHash; QHash<qint32, InternalNodePointer> m_internalIdNodeHash; @@ -281,6 +309,7 @@ private: InternalNodePointer m_currentStateNode; InternalNodePointer m_rootInternalNode; InternalNodePointer m_currentTimelineNode; + std::unique_ptr<ModelResourceManagementInterface> m_resourceManagement; QUrl m_fileUrl; QPointer<RewriterView> m_rewriterView; QPointer<NodeInstanceView> m_nodeInstanceView; diff --git a/src/plugins/qmldesigner/designercore/model/modelmerger.cpp b/src/plugins/qmldesigner/designercore/model/modelmerger.cpp index 75cdcf83582..30d100f3503 100644 --- a/src/plugins/qmldesigner/designercore/model/modelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelmerger.cpp @@ -173,7 +173,7 @@ ModelNode ModelMerger::insertModel(const ModelNode &modelNode, const MergePredic return {}; RewriterTransaction transaction(view()->beginRewriterTransaction(QByteArrayLiteral("ModelMerger::insertModel"))); - QList<Import> newImports; + Imports newImports; for (const Import &import : modelNode.model()->imports()) { if (!view()->model()->hasImport(import, true, true)) diff --git a/src/plugins/qmldesigner/designercore/model/modelnode.cpp b/src/plugins/qmldesigner/designercore/model/modelnode.cpp index aab0d13c249..7e64f4c4bf4 100644 --- a/src/plugins/qmldesigner/designercore/model/modelnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelnode.cpp @@ -61,7 +61,6 @@ ModelNode::ModelNode(const InternalNodePointer &internalNode, Model *model, cons m_model(model), m_view(const_cast<AbstractView*>(view)) { - Q_ASSERT(!m_model || m_view); } ModelNode::ModelNode(const ModelNode &modelNode, AbstractView *view) @@ -291,7 +290,7 @@ A node might become invalid if e.g. it or one of its ancestors is deleted. */ bool ModelNode::isValid() const { - return !m_model.isNull() && !m_view.isNull() && m_internalNode && m_internalNode->isValid; + return !m_model.isNull() && m_internalNode && m_internalNode->isValid; } /*! @@ -653,7 +652,7 @@ void ModelNode::removeProperty(const PropertyName &name) const return; if (m_internalNode->hasProperty(name)) - model()->d->removeProperty(m_internalNode->property(name)); + model()->d->removePropertyAndRelatedResources(m_internalNode->property(name)); } /*! \brief removes this node from the node tree @@ -693,7 +692,7 @@ void ModelNode::destroy() return; removeModelNodeFromSelection(*this); - model()->d->removeNode(m_internalNode); + model()->d->removeNodeAndRelatedResources(m_internalNode); } //\} diff --git a/src/plugins/qmldesigner/designercore/model/modelresourcemanagementinterface.h b/src/plugins/qmldesigner/designercore/model/modelresourcemanagementinterface.h new file mode 100644 index 00000000000..b2188132d6a --- /dev/null +++ b/src/plugins/qmldesigner/designercore/model/modelresourcemanagementinterface.h @@ -0,0 +1,39 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include <qmldesignercorelib_exports.h> + +#include <bindingproperty.h> + +namespace QmlDesigner { + +struct ModelResourceSet +{ + struct SetExpression + { + BindingProperty property; + QString expression; + }; + + QList<ModelNode> removeModelNodes; + QList<AbstractProperty> removeProperties; + QList<SetExpression> setExpressions; +}; + +class QMLDESIGNERCORE_EXPORT ModelResourceManagementInterface +{ +public: + ModelResourceManagementInterface() = default; + virtual ~ModelResourceManagementInterface() = default; + + ModelResourceManagementInterface(const ModelResourceManagementInterface &) = delete; + ModelResourceManagementInterface &operator=(const ModelResourceManagementInterface &) = delete; + ModelResourceManagementInterface(ModelResourceManagementInterface &&) = default; + ModelResourceManagementInterface &operator=(ModelResourceManagementInterface &&) = default; + + virtual ModelResourceSet removeNode(const ModelNode &node) const = 0; + virtual ModelResourceSet removeProperty(const AbstractProperty &property) const = 0; +}; +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/model/modelutils.cpp b/src/plugins/qmldesigner/designercore/model/modelutils.cpp new file mode 100644 index 00000000000..2e454527b88 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/model/modelutils.cpp @@ -0,0 +1,93 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "modelutils.h" + +#include <utils/expected.h> + +#include <algorithm> + +namespace QmlDesigner::Utils { + +namespace { + +enum class ImportError { EmptyImportName, HasAlreadyImport, NoModule }; + +::Utils::expected<Import, ImportError> findImport(const QString &importName, + const std::function<bool(const Import &)> &predicate, + const Imports &imports, + const Imports &modules) +{ + if (importName.isEmpty()) + return ::Utils::make_unexpected(ImportError::EmptyImportName); + + auto hasName = [&](const auto &import) { + return import.url() == importName || import.file() == importName; + }; + + bool hasImport = std::any_of(imports.begin(), imports.end(), hasName); + + if (hasImport) + return ::Utils::make_unexpected(ImportError::HasAlreadyImport); + + auto foundModule = std::find_if(modules.begin(), modules.end(), [&](const Import &import) { + return hasName(import) && predicate(import); + }); + + if (foundModule == modules.end()) + return ::Utils::make_unexpected(ImportError::NoModule); + + return *foundModule; +} + +} // namespace + +bool addImportWithCheck(const QString &importName, + const std::function<bool(const Import &)> &predicate, + Model *model) +{ + return addImportsWithCheck({importName}, predicate, model); +} + +bool addImportWithCheck(const QString &importName, Model *model) +{ + return addImportWithCheck( + importName, [](const Import &) { return true; }, model); +} + +bool addImportsWithCheck(const QStringList &importNames, Model *model) +{ + return addImportsWithCheck( + importNames, [](const Import &) { return true; }, model); +} + +bool addImportsWithCheck(const QStringList &importNames, + const std::function<bool(const Import &)> &predicate, + Model *model) +{ + const Imports &imports = model->imports(); + const Imports &modules = model->possibleImports(); + + Imports importsToAdd; + importsToAdd.reserve(importNames.size()); + + for (const QString &importName : importNames) { + auto import = findImport(importName, predicate, imports, modules); + + if (import) { + importsToAdd.push_back(*import); + } else { + if (import.error() == ImportError::NoModule) + return false; + else + continue; + } + } + + if (!importsToAdd.isEmpty()) + model->changeImports(std::move(importsToAdd), {}); + + return true; +} + +} // namespace QmlDesigner::Utils diff --git a/src/plugins/qmldesigner/designercore/model/modelutils.h b/src/plugins/qmldesigner/designercore/model/modelutils.h new file mode 100644 index 00000000000..58055f39065 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/model/modelutils.h @@ -0,0 +1,24 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "qmldesignercorelib_global.h" + +#include <import.h> +#include <model.h> + +#include <functional> + +namespace QmlDesigner::Utils { + +QMLDESIGNERCORE_EXPORT bool addImportsWithCheck(const QStringList &importNames, + const std::function<bool(const Import &)> &predicate, + Model *model); +QMLDESIGNERCORE_EXPORT bool addImportsWithCheck(const QStringList &importNames, Model *model); +QMLDESIGNERCORE_EXPORT bool addImportWithCheck(const QString &importName, + const std::function<bool(const Import &)> &predicate, + Model *model); +QMLDESIGNERCORE_EXPORT bool addImportWithCheck(const QString &importName, Model *model); + +} // namespace QmlDesigner::Utils diff --git a/src/plugins/qmldesigner/designercore/model/nodeabstractproperty.cpp b/src/plugins/qmldesigner/designercore/model/nodeabstractproperty.cpp index e2ef9d76f06..60b4e77dc6d 100644 --- a/src/plugins/qmldesigner/designercore/model/nodeabstractproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/nodeabstractproperty.cpp @@ -71,7 +71,7 @@ void NodeAbstractProperty::reparentHere(const ModelNode &modelNode, bool isNode return; if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isNodeAbstractProperty()) - privateModel()->removeProperty(internalNode()->property(name())); + privateModel()->removePropertyAndRelatedResources(internalNode()->property(name())); if (modelNode.hasParentProperty()) { Internal::InternalNodeAbstractProperty::Pointer oldParentProperty = modelNode.internalNode()->parentProperty(); diff --git a/src/plugins/qmldesigner/designercore/model/nodeproperty.cpp b/src/plugins/qmldesigner/designercore/model/nodeproperty.cpp index a9cc7a34f54..4af8428e6be 100644 --- a/src/plugins/qmldesigner/designercore/model/nodeproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/nodeproperty.cpp @@ -32,7 +32,7 @@ void NodeProperty::setModelNode(const ModelNode &modelNode) } if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isNodeProperty()) - privateModel()->removeProperty(internalNode()->property(name())); + privateModel()->removePropertyAndRelatedResources(internalNode()->property(name())); privateModel()->reparentNode(internalNode(), name(), modelNode.internalNode(), false); //### we have to add a flag that this is not a list } diff --git a/src/plugins/qmldesigner/designercore/model/plaintexteditmodifier.cpp b/src/plugins/qmldesigner/designercore/model/plaintexteditmodifier.cpp index 2318c3f2cf0..a1f88b3a687 100644 --- a/src/plugins/qmldesigner/designercore/model/plaintexteditmodifier.cpp +++ b/src/plugins/qmldesigner/designercore/model/plaintexteditmodifier.cpp @@ -3,12 +3,16 @@ #include "plaintexteditmodifier.h" -#include <utils/changeset.h> #include <qmljs/qmljsmodelmanagerinterface.h> -#include <QPlainTextEdit> +#include <qmljstools/qmljscodestylepreferences.h> +#include <qmljstools/qmljsindenter.h> +#include <qmljstools/qmljstoolssettings.h> + +#include <utils/changeset.h> #include <QDebug> +#include <QPlainTextEdit> using namespace Utils; using namespace QmlDesigner; @@ -161,3 +165,41 @@ void PlainTextEditModifier::reactivateChangeSignals() emit textChanged(); } } + +IndentingTextEditModifier::IndentingTextEditModifier(QTextDocument *document, const QTextCursor &textCursor) + : NotIndentingTextEditModifier{document, textCursor} +{ + m_tabSettings = QmlJSTools::QmlJSToolsSettings::globalCodeStyle()->tabSettings(); +} + +void IndentingTextEditModifier::indent(int offset, int length) +{ + if (length == 0 || offset < 0 || offset + length >= text().length()) + return; + + int startLine = getLineInDocument(textDocument(), offset); + int endLine = getLineInDocument(textDocument(), offset + length); + + if (startLine > -1 && endLine > -1) + indentLines(startLine, endLine); +} + +void IndentingTextEditModifier::indentLines(int startLine, int endLine) +{ + if (startLine < 0) + return; + + QTextCursor tc(textDocument()); + + tc.beginEditBlock(); + for (int i = startLine; i <= endLine; i++) { + QTextBlock start = textDocument()->findBlockByNumber(i); + + if (start.isValid()) { + QmlJSEditor::Internal::Indenter indenter(textDocument()); + indenter.indentBlock(start, QChar::Null, m_tabSettings); + } + } + tc.endEditBlock(); +} + diff --git a/src/plugins/qmldesigner/designercore/model/qmlchangeset.cpp b/src/plugins/qmldesigner/designercore/model/qmlchangeset.cpp index 5029fdc79d3..134c4bf1c20 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlchangeset.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlchangeset.cpp @@ -76,7 +76,7 @@ bool QmlModelStateOperation::isValid() const bool QmlModelStateOperation::isValidQmlModelStateOperation(const ModelNode &modelNode) { - return isValidQmlModelNodeFacade(modelNode) && modelNode.metaInfo().isQuickStateOperation(); + return isValidQmlModelNodeFacade(modelNode) && modelNode.metaInfo().isQtQuickStateOperation(); } void QmlPropertyChanges::removeProperty(const PropertyName &name) diff --git a/src/plugins/qmldesigner/designercore/model/qmlmodelnodefacade.cpp b/src/plugins/qmldesigner/designercore/model/qmlmodelnodefacade.cpp index 47699eae468..7e3bc793247 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlmodelnodefacade.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlmodelnodefacade.cpp @@ -29,8 +29,6 @@ const NodeInstanceView *QmlModelNodeFacade::nodeInstanceView() const return nodeInstanceView(m_modelNode); } -QmlModelNodeFacade::~QmlModelNodeFacade() = default; - bool QmlModelNodeFacade::hasModelNode() const { return m_modelNode.isValid(); diff --git a/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp index c3ee4b975f7..3831a90d97c 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp @@ -291,6 +291,7 @@ static QmlObjectNode createQmlObjectNodeFromSource(AbstractView *view, rewriterView->setCheckSemanticErrors(false); rewriterView->setTextModifier(&modifier); rewriterView->setAllowComponentRoot(true); + rewriterView->setPossibleImportsEnabled(false); inputModel->setRewriterView(rewriterView.data()); if (rewriterView->errors().isEmpty() && rewriterView->rootModelNode().isValid()) { diff --git a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp index d8088de64be..53a0f4de596 100644 --- a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp +++ b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp @@ -261,7 +261,7 @@ void RewriterView::nodeReparented(const ModelNode &node, const NodeAbstractPrope applyChanges(); } -void RewriterView::importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) +void RewriterView::importsChanged(const Imports &addedImports, const Imports &removedImports) { for (const Import &import : addedImports) importAdded(import); diff --git a/src/plugins/qmldesigner/designercore/model/signalhandlerproperty.cpp b/src/plugins/qmldesigner/designercore/model/signalhandlerproperty.cpp index 205105187d1..4690044fa61 100644 --- a/src/plugins/qmldesigner/designercore/model/signalhandlerproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/signalhandlerproperty.cpp @@ -41,7 +41,7 @@ void SignalHandlerProperty::setSource(const QString &source) } if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isSignalHandlerProperty()) - privateModel()->removeProperty(internalNode()->property(name())); + privateModel()->removePropertyAndRelatedResources(internalNode()->property(name())); privateModel()->setSignalHandlerProperty(internalNode(), name(), source); } @@ -118,7 +118,7 @@ void SignalDeclarationProperty::setSignature(const QString &signature) } if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isSignalDeclarationProperty()) - privateModel()->removeProperty(internalNode()->property(name())); + privateModel()->removePropertyAndRelatedResources(internalNode()->property(name())); privateModel()->setSignalDeclarationProperty(internalNode(), name(), signature); } diff --git a/src/plugins/qmldesigner/designercore/model/skipiterator.h b/src/plugins/qmldesigner/designercore/model/skipiterator.h new file mode 100644 index 00000000000..11efb7d97f0 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/model/skipiterator.h @@ -0,0 +1,77 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include <iterator> + +namespace QmlDesigner { + +template<typename BaseIterator, typename Increment> +class SkipIterator +{ +public: + using iterator_category = std::forward_iterator_tag; + using difference_type = qsizetype; + using value_type = typename BaseIterator::value_type; + using pointer = typename BaseIterator::pointer; + using reference = typename BaseIterator::reference; + + SkipIterator() = default; + SkipIterator(BaseIterator current, Increment increment) + : m_current{current} + , m_increment{std::move(increment)} + {} + + SkipIterator operator++() + { + m_current = m_increment(m_current); + return *this; + } + + SkipIterator operator++(int) + { + auto tmp = *this; + m_current = m_increment(m_current); + return tmp; + } + + reference operator*() const { return *m_current; } + pointer operator->() const { return m_current.operator->(); } + + friend bool operator==(const SkipIterator &first, const SkipIterator &second) + { + return first.m_current == second.m_current; + } + + friend bool operator!=(const SkipIterator &first, const SkipIterator &second) + { + return first.m_current != second.m_current; + } + +private: + BaseIterator m_current = {}; + Increment m_increment; +}; + +template<typename Container, typename Increment> +class SkipRange +{ +public: + using container = Container; + using base = SkipRange<Container, Increment>; + using iterator = SkipIterator<typename Container::const_iterator, Increment>; + + SkipRange(const Container &container, Increment increment) + : m_begin{container.begin(), increment} + , m_end{container.end(), increment} + {} + + iterator begin() const { return m_begin; } + iterator end() const { return m_end; } + +private: + iterator m_begin; + iterator m_end; +}; +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index 4469793db83..ac89c54acd6 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -19,6 +19,8 @@ #include "signalhandlerproperty.h" #include "variantproperty.h" #include <externaldependenciesinterface.h> +#include <import.h> +#include <projectstorage/modulescanner.h> #include <rewritingexception.h> #include <enumeration.h> @@ -44,6 +46,7 @@ #include <QSet> #include <memory> +#include <tuple> using namespace LanguageUtils; using namespace QmlJS; @@ -59,12 +62,15 @@ bool isSupportedAttachedProperties(const QString &propertyName) || propertyName.startsWith(QLatin1String("InsightCategory.")); } -QStringList supportedVersionsList() +bool isSupportedVersion(QmlDesigner::Version version) { - static const QStringList list = {"2.0", "2.1", "2.2", "2.3", "2.4", "2.5", "2.6", - "2.7", "2.8", "2.9", "2.10", "2.11", "2.12", "2.13", - "2.14", "2.15", "6.0", "6.1", "6.2", "6.3", "6.4"}; - return list; + if (version.major == 2) + return version.minor <= 15; + + if (version.major == 6) + return version.minor <= 5; + + return false; } QStringList globalQtEnums() @@ -99,13 +105,15 @@ QStringList knownEnumScopes() "Grid", "ItemLayer", "ImageLayer", - "SpriteLayer"}; + "SpriteLayer", + "Light"}; return list; } -bool supportedQtQuickVersion(const QString &version) +bool supportedQtQuickVersion(const QmlDesigner::Import &import) { - return version.isEmpty() || supportedVersionsList().contains(version); + auto version = import.toVersion(); + return version.isEmpty() || isSupportedVersion(version); } QString stripQuotes(const QString &str) @@ -117,7 +125,7 @@ QString stripQuotes(const QString &str) return str; } -inline QString deEscape(const QString &value) +QString deEscape(const QString &value) { QString result = value; @@ -282,7 +290,7 @@ bool isComponentType(const QmlDesigner::TypeName &type) { return type == "Component" || type == "Qt.Component" || type == "QtQuick.Component" || type == "QtQml.Component" || type == "<cpp>.QQmlComponent" || type == "QQmlComponent" - || type == "QML.Component"; + || type == "QML.Component" || type == "QtQml.Base.Component"; } bool isCustomParserType(const QmlDesigner::TypeName &type) @@ -302,7 +310,8 @@ bool isPropertyChangesType(const QmlDesigner::TypeName &type) bool isConnectionsType(const QmlDesigner::TypeName &type) { - return type == "Connections" || type == "QtQuick.Connections" || type == "Qt.Connections" || type == "QtQml.Connections"; + return type == "Connections" || type == "QtQuick.Connections" || type == "Qt.Connections" + || type == "QtQml.Connections" || type == "QtQml.Base.Connections"; } bool propertyIsComponentType(const QmlDesigner::NodeAbstractProperty &property, const QmlDesigner::TypeName &type, QmlDesigner::Model *model) @@ -716,7 +725,7 @@ bool TextToModelMerger::isActive() const void TextToModelMerger::setupImports(const Document::Ptr &doc, DifferenceHandler &differenceHandler) { - QList<Import> existingImports = m_rewriterView->model()->imports(); + Imports existingImports = m_rewriterView->model()->imports(); m_hasVersionlessImport = false; @@ -756,162 +765,143 @@ void TextToModelMerger::setupImports(const Document::Ptr &doc, differenceHandler.importAbsentInQMl(import); } -static bool isLatestImportVersion(const ImportKey &importKey, const QHash<QString, ImportKey> &filteredPossibleImportKeys) +namespace { + +bool skipByMetaInfo(QStringView moduleName, const QStringList &skipModuleNames) { - return !filteredPossibleImportKeys.contains(importKey.path()) - || filteredPossibleImportKeys.value(importKey.path()).majorVersion < importKey.majorVersion - || (filteredPossibleImportKeys.value(importKey.path()).majorVersion == importKey.majorVersion - && filteredPossibleImportKeys.value(importKey.path()).minorVersion < importKey.minorVersion); + return std::any_of(skipModuleNames.begin(), + skipModuleNames.end(), + [&](const QString &skipModuleName) { + return moduleName.contains(skipModuleName); + }); } -static bool filterByMetaInfo(const ImportKey &importKey, Model *model) +class StartsWith : public QStringView { - if (model) { - for (const QString &filter : model->metaInfo().itemLibraryInfo()->blacklistImports()) { - if (importKey.libraryQualifiedPath().contains(filter)) - return true; - } +public: + using QStringView::QStringView; + bool operator()(QStringView moduleName) const { return moduleName.startsWith(*this); } +}; + +class EndsWith : public QStringView +{ +public: + using QStringView::QStringView; + bool operator()(QStringView moduleName) const { return moduleName.endsWith(*this); } +}; +class StartsAndEndsWith : public std::pair<QStringView, QStringView> +{ +public: + using Base = std::pair<QStringView, QStringView>; + using Base::Base; + bool operator()(QStringView moduleName) const + { + return moduleName.startsWith(first) && moduleName.endsWith(second); } +}; - return false; -} +class Equals : public QStringView +{ +public: + using QStringView::QStringView; + bool operator()(QStringView moduleName) const { return moduleName == *this; } +}; -static bool isBlacklistImport(const ImportKey &importKey, Model *model) -{ - const QString &importPathFirst = importKey.splitPath.constFirst(); - const QString &importPathLast = importKey.splitPath.constLast(); - return importPathFirst == QStringLiteral("<cpp>") - || importPathFirst == QStringLiteral("QML") - || importPathFirst == QStringLiteral("QtQml") - || (importPathFirst == QStringLiteral("QtQuick") && importPathLast == QStringLiteral("PrivateWidgets")) - || importPathLast == QStringLiteral("Private") - || importPathLast == QStringLiteral("private") - || importKey.libraryQualifiedPath() == QStringLiteral("QtQuick.Particles") //Unsupported - || importKey.libraryQualifiedPath() == QStringLiteral("QtQuick.Dialogs") //Unsupported - || importKey.libraryQualifiedPath() == QStringLiteral("QtQuick.Controls.Styles") //Unsupported - || importKey.libraryQualifiedPath() == QStringLiteral("QtNfc") //Unsupported - || importKey.libraryQualifiedPath() == QStringLiteral("Qt.WebSockets") - || importKey.libraryQualifiedPath() == QStringLiteral("QtWebkit") - || importKey.libraryQualifiedPath() == QStringLiteral("QtLocation") - || importKey.libraryQualifiedPath() == QStringLiteral("QtWebChannel") - || importKey.libraryQualifiedPath() == QStringLiteral("QtWinExtras") - || importKey.libraryQualifiedPath() == QStringLiteral("QtPurchasing") - || importKey.libraryQualifiedPath() == QStringLiteral("QtBluetooth") - || importKey.libraryQualifiedPath() == QStringLiteral("Enginio") - - || filterByMetaInfo(importKey, model); -} - -static QHash<QString, ImportKey> filterPossibleImportKeys(const QSet<ImportKey> &possibleImportKeys, Model *model) -{ - QHash<QString, ImportKey> filteredPossibleImportKeys; - for (const ImportKey &importKey : possibleImportKeys) { - if (isLatestImportVersion(importKey, filteredPossibleImportKeys) && !isBlacklistImport(importKey, model)) - filteredPossibleImportKeys.insert(importKey.path(), importKey); - } +constexpr auto skipModules = std::make_tuple(EndsWith(u".impl"), + StartsWith(u"QML"), + StartsWith(u"QtQml"), + StartsAndEndsWith(u"QtQuick", u".PrivateWidgets"), + EndsWith(u".private"), + EndsWith(u".Private"), + Equals(u"QtQuick.Particles"), + Equals(u"QtQuick.Dialogs"), + Equals(u"QtQuick.Controls.Styles"), + Equals(u"QtNfc"), + Equals(u"Qt.WebSockets"), + Equals(u"QtWebkit"), + Equals(u"QtLocation"), + Equals(u"QtWebChannel"), + Equals(u"QtWinExtras"), + Equals(u"QtPurchasing"), + Equals(u"QtBluetooth"), + Equals(u"Enginio")); - return filteredPossibleImportKeys; +bool skipModule(QStringView moduleName) +{ + return std::apply([=](const auto &...skipModule) { return (skipModule(moduleName) || ...); }, + skipModules); } -static void removeUsedImports(QHash<QString, ImportKey> &filteredPossibleImportKeys, const QList<QmlJS::Import> &usedImports) +bool skipModule(QStringView moduleName, const QStringList &skipModuleNames) { - for (const QmlJS::Import &import : usedImports) - filteredPossibleImportKeys.remove(import.info.path()); + return skipModule(moduleName) || skipByMetaInfo(moduleName, skipModuleNames); } -static QList<QmlDesigner::Import> generatePossibleFileImports(const QString &path, - const QList<QmlJS::Import> &usedImports) +void collectPossibleFileImports(const QString &checkPath, + QSet<QString> usedImportsSet, + QList<QmlDesigner::Import> &possibleImports) { - QSet<QString> usedImportsSet; - for (const QmlJS::Import &i : usedImports) - usedImportsSet.insert(i.info.path()); - - QList<QmlDesigner::Import> possibleImports; const QStringList qmlList("*.qml"); const QStringList qmldirList("qmldir"); - - QStringList fileImportPaths; const QChar delimeter('/'); - std::function<void(const QString &)> checkDir; - checkDir = [&](const QString &checkPath) { - - if (QFileInfo(checkPath).isRoot()) - return; + if (QFileInfo(checkPath).isRoot()) + return; - const QStringList entries = QDir(checkPath).entryList(QDir::Dirs | QDir::NoDot | QDir::NoDotDot); - const QString checkPathDelim = checkPath + delimeter; - for (const QString &entry : entries) { - QDir dir(checkPathDelim + entry); - const QString dirPath = dir.path(); - if (!dir.entryInfoList(qmlList, QDir::Files).isEmpty() - && dir.entryInfoList(qmldirList, QDir::Files).isEmpty() - && !usedImportsSet.contains(dirPath)) { - const QString importName = dir.path().mid(path.size() + 1); - QmlDesigner::Import import = QmlDesigner::Import::createFileImport(importName); - possibleImports.append(import); - } - checkDir(dirPath); + const QStringList entries = QDir(checkPath).entryList(QDir::Dirs | QDir::NoDot | QDir::NoDotDot); + const QString checkPathDelim = checkPath + delimeter; + for (const QString &entry : entries) { + QDir dir(checkPathDelim + entry); + const QString dirPath = dir.path(); + if (!dir.entryInfoList(qmlList, QDir::Files).isEmpty() + && dir.entryInfoList(qmldirList, QDir::Files).isEmpty() + && !usedImportsSet.contains(dirPath)) { + const QString importName = dir.path().mid(checkPath.size() + 1); + QmlDesigner::Import import = QmlDesigner::Import::createFileImport(importName); + possibleImports.append(import); } - }; - checkDir(path); - - return possibleImports; + collectPossibleFileImports(dirPath, usedImportsSet, possibleImports); + } } -static QList<QmlDesigner::Import> generatePossibleLibraryImports(const QHash<QString, ImportKey> &filteredPossibleImportKeys) +QList<QmlDesigner::Import> generatePossibleFileImports(const QString &path, + const QList<QmlJS::Import> &usedImports) { - QList<QmlDesigner::Import> possibleImports; - QSet<QString> controlsImplVersions; - bool hasVersionedControls = false; - bool hasVersionlessControls = false; - const QString controlsName = "QtQuick.Controls"; - const QString controlsImplName = "QtQuick.Controls.impl"; - - for (const ImportKey &importKey : filteredPossibleImportKeys) { - QString libraryName = importKey.splitPath.join(QLatin1Char('.')); - int majorVersion = importKey.majorVersion; - if (majorVersion >= 0) { - int minorVersion = (importKey.minorVersion == LanguageUtils::ComponentVersion::NoVersion) ? 0 : importKey.minorVersion; - - if (libraryName.contains("QtQuick.Studio")) { - majorVersion = 1; - minorVersion = 0; - } + QSet<QString> usedImportsSet; + for (const QmlJS::Import &i : usedImports) + usedImportsSet.insert(i.info.path()); - QString version = QStringLiteral("%1.%2").arg(majorVersion).arg(minorVersion); - if (!libraryName.endsWith(".impl")) - possibleImports.append(QmlDesigner::Import::createLibraryImport(libraryName, version)); - - // In Qt6, QtQuick.Controls itself doesn't have any version as it has no types, - // so it never gets added normally to possible imports. - // We work around this by injecting corresponding QtQuick.Controls version for each - // found impl version, if no valid QtQuick.Controls versions are found. - if (!hasVersionedControls) { - if (libraryName == controlsImplName) - controlsImplVersions.insert(version); - else if (libraryName == controlsName) - hasVersionedControls = true; - } - } else if (!hasVersionlessControls && libraryName == controlsName) { - // If QtQuick.Controls module is not included even in non-versioned, it means - // QtQuick.Controls is either in use or not available at all, - // so we shouldn't inject it. - hasVersionlessControls = true; - } - } + QList<QmlDesigner::Import> possibleImports; - if (hasVersionlessControls && !hasVersionedControls && !controlsImplVersions.isEmpty()) { - for (const auto &version : std::as_const(controlsImplVersions)) - possibleImports.append(QmlDesigner::Import::createLibraryImport(controlsName, version)); - } + QStringList fileImportPaths; + collectPossibleFileImports(path, usedImportsSet, possibleImports); return possibleImports; } -void TextToModelMerger::setupPossibleImports(const QmlJS::Snapshot &snapshot, const QmlJS::ViewerContext &viewContext) +QmlDesigner::Imports createQt5Modules() +{ + return {QmlDesigner::Import::createLibraryImport("QtQuick", "5.15"), + QmlDesigner::Import::createLibraryImport("QtQuick3D", "5.15"), + QmlDesigner::Import::createLibraryImport("QtQuick.Controls", "5.15"), + QmlDesigner::Import::createLibraryImport("QtQuick.Window", "5.15"), + QmlDesigner::Import::createLibraryImport("QtQuick.Layouts", "5.15"), + QmlDesigner::Import::createLibraryImport("QtQuick.Timeline", "5.15"), + QmlDesigner::Import::createLibraryImport("QtCharts", "5.15"), + QmlDesigner::Import::createLibraryImport("QtDataVisulaization", "5.15"), + QmlDesigner::Import::createLibraryImport("QtQuick.Studio.Controls", "1.0"), + QmlDesigner::Import::createLibraryImport("QtQuick.Studio.Effects", "1.0"), + QmlDesigner::Import::createLibraryImport("FlowView", "1.0"), + QmlDesigner::Import::createLibraryImport("QtQuick.Studio.LogicHelper", "1.0"), + QmlDesigner::Import::createLibraryImport("QtQuick.Studio.MultiText", "1.0"), + QmlDesigner::Import::createLibraryImport("Qt.SafeRenderer", "2.0")}; +} + +} // namespace + +void TextToModelMerger::setupPossibleImports() { if (!m_rewriterView->possibleImportsEnabled()) return; @@ -919,27 +909,42 @@ void TextToModelMerger::setupPossibleImports(const QmlJS::Snapshot &snapshot, co static QUrl lastProjectUrl; auto &externalDependencies = m_rewriterView->externalDependencies(); auto projectUrl = externalDependencies.projectUrl(); - - if (m_possibleImportKeys.isEmpty() || projectUrl != lastProjectUrl) - m_possibleImportKeys = snapshot.importDependencies()->libraryImports(viewContext); + auto allUsedImports = m_scopeChain->context()->imports(m_document.data())->all(); + + if (m_possibleModules.isEmpty() || projectUrl != lastProjectUrl) { + + auto &externalDependencies = m_rewriterView->externalDependencies(); + if (externalDependencies.isQt6Project()) { + const auto skipModuleNames = m_rewriterView->model() + ->metaInfo() + .itemLibraryInfo() + ->blacklistImports(); + ModuleScanner moduleScanner{[&](QStringView moduleName) { + return skipModule(moduleName, skipModuleNames); + }, + VersionScanning::No, + m_rewriterView->externalDependencies()}; + moduleScanner.scan(m_rewriterView->externalDependencies().modulePaths()); + m_possibleModules = moduleScanner.modules(); + } else { + ModuleScanner moduleScanner{[&](QStringView) { return false; }, + VersionScanning::Yes, + m_rewriterView->externalDependencies()}; + m_possibleModules = createQt5Modules(); + moduleScanner.scan(externalDependencies.projectModulePaths()); + m_possibleModules.append(moduleScanner.modules()); + } + } lastProjectUrl = projectUrl; - QHash<QString, ImportKey> filteredPossibleImportKeys = filterPossibleImportKeys( - m_possibleImportKeys, m_rewriterView->model()); - - const QmlJS::Imports *imports = m_scopeChain->context()->imports(m_document.data()); - if (imports) - removeUsedImports(filteredPossibleImportKeys, imports->all()); - - QList<QmlDesigner::Import> possibleImports = generatePossibleLibraryImports(filteredPossibleImportKeys); + auto modules = m_possibleModules; if (document()->fileName() != "<internal>") - possibleImports.append( - generatePossibleFileImports(document()->path().toString(), imports->all())); + modules.append(generatePossibleFileImports(document()->path().toString(), allUsedImports)); if (m_rewriterView->isAttached()) - m_rewriterView->model()->setPossibleImports(possibleImports); + m_rewriterView->model()->setPossibleImports(modules); } void TextToModelMerger::setupUsedImports() @@ -951,7 +956,7 @@ void TextToModelMerger::setupUsedImports() const QList<QmlJS::Import> allImports = imports->all(); QSet<QString> usedImportsSet; - QList<Import> usedImports; + Imports usedImports; // populate usedImportsSet from current model nodes const QList<ModelNode> allModelNodes = m_rewriterView->allModelNodes(); @@ -964,9 +969,6 @@ void TextToModelMerger::setupUsedImports() for (const QmlJS::Import &import : allImports) { QString version = import.info.version().toString(); - if (!import.info.version().isValid()) - version = getHighestPossibleImport(import.info.name()); - if (!import.info.name().isEmpty() && usedImportsSet.contains(import.info.name())) { if (import.info.type() == ImportType::Library) usedImports.append( @@ -1077,7 +1079,7 @@ bool TextToModelMerger::load(const QString &data, DifferenceHandler &differenceH collectLinkErrors(&errors, ctxt); } - setupPossibleImports(snapshot, m_vContext); + setupPossibleImports(); qCInfo(rewriterBenchmark) << "possible imports:" << time.elapsed(); @@ -1115,12 +1117,6 @@ bool TextToModelMerger::load(const QString &data, DifferenceHandler &differenceH setActive(false); - // Clear possible imports cache if code model hasn't settled yet - const int importKeysSize = m_possibleImportKeys.size(); - if (m_previousPossibleImportsSize != importKeysSize) - m_possibleImportKeys.clear(); - m_previousPossibleImportsSize = importKeysSize; - return true; } catch (Exception &e) { DocumentMessage error(&e); @@ -2174,13 +2170,12 @@ void TextToModelMerger::collectImportErrors(QList<DocumentMessage> *errors) bool hasQtQuick = false; for (const QmlDesigner::Import &import : m_rewriterView->model()->imports()) { if (import.isLibraryImport() && import.url() == QStringLiteral("QtQuick")) { - - if (supportedQtQuickVersion(import.version())) { + if (supportedQtQuickVersion(import)) { hasQtQuick = true; auto &externalDependencies = m_rewriterView->externalDependencies(); if (externalDependencies.hasStartupTarget()) { - const bool qt6import = import.version().startsWith("6"); + const bool qt6import = !import.hasVersion() || import.majorVersion() == 6; if (!externalDependencies.isQt6Import() && (m_hasVersionlessImport || qt6import)) { const QmlJS::DiagnosticMessage diagnosticMessage( @@ -2387,8 +2382,8 @@ QList<QmlTypeData> TextToModelMerger::getQMLSingletons() const void TextToModelMerger::clearPossibleImportKeys() { - m_possibleImportKeys.clear(); - m_previousPossibleImportsSize = -1; + m_possibleModules.clear(); + m_previousPossibleModulesSize = -1; } QString TextToModelMerger::textAt(const Document::Ptr &doc, @@ -2403,19 +2398,3 @@ QString TextToModelMerger::textAt(const Document::Ptr &doc, { return doc->source().mid(from.offset, to.end() - from.begin()); } - -QString TextToModelMerger::getHighestPossibleImport(const QString &importName) const -{ - QString version = "2.15"; - int maj = -1; - const auto imports = m_possibleImportKeys.values(); - for (const ImportKey &import : imports) { - if (importName == import.libraryQualifiedPath()) { - if (import.majorVersion > maj) { - version = QString("%1.%2").arg(import.majorVersion).arg(import.minorVersion); - maj = import.majorVersion; - } - } - } - return version; -} diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h index 5ed2e24c7a4..50b3a423678 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h @@ -37,7 +37,7 @@ public: bool isActive() const; void setupImports(const QmlJS::Document::Ptr &doc, DifferenceHandler &differenceHandler); - void setupPossibleImports(const QmlJS::Snapshot &snapshot, const QmlJS::ViewerContext &viewContext); + void setupPossibleImports(); void setupUsedImports(); bool load(const QString &data, DifferenceHandler &differenceHandler); @@ -137,8 +137,6 @@ private: const QmlJS::SourceLocation &from, const QmlJS::SourceLocation &to); - QString getHighestPossibleImport(const QString &importName) const; - private: RewriterView *m_rewriterView; bool m_isActive; @@ -150,8 +148,8 @@ private: QSet<ModelNode> m_clearImplicitComponentList; QmlJS::ViewerContext m_vContext; QSet<QPair<QString, QString> > m_qrcMapping; - QSet<QmlJS::ImportKey> m_possibleImportKeys; - int m_previousPossibleImportsSize = -1; + Imports m_possibleModules; + int m_previousPossibleModulesSize = -1; bool m_hasVersionlessImport = false; }; diff --git a/src/plugins/qmldesigner/designercore/model/variantproperty.cpp b/src/plugins/qmldesigner/designercore/model/variantproperty.cpp index 48d0fa86300..c879ec7a347 100644 --- a/src/plugins/qmldesigner/designercore/model/variantproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/variantproperty.cpp @@ -47,7 +47,7 @@ void VariantProperty::setValue(const QVariant &value) } if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isVariantProperty()) - privateModel()->removeProperty(internalNode()->property(name())); + privateModel()->removePropertyAndRelatedResources(internalNode()->property(name())); privateModel()->setVariantProperty(internalNode(), name(), value); } @@ -96,7 +96,7 @@ void VariantProperty::setDynamicTypeNameAndValue(const TypeName &type, const QVa } if (internalNode()->hasProperty(name()) && !internalNode()->property(name())->isVariantProperty()) - privateModel()->removeProperty(internalNode()->property(name())); + privateModel()->removePropertyAndRelatedResources(internalNode()->property(name())); privateModel()->setDynamicVariantProperty(internalNode(), name(), type, value); } diff --git a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h index 3cd7f3a0167..0db44e3df76 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h @@ -49,6 +49,7 @@ inline constexpr char GroupItem[] = "GroupItem"; inline constexpr char Image[] = "Image"; inline constexpr char InstanceListEntry[] = "InstanceListEntry"; inline constexpr char InstanceList[] = "InstanceList"; +inline constexpr char Light[] = "Light"; inline constexpr char IntType[] = "int"; inline constexpr char Item[] = "Item"; inline constexpr char KeyframeGroup[] = "KeyframeGroup"; @@ -83,6 +84,7 @@ inline constexpr char QtQuick3D[] = "QtQuick3D"; inline constexpr char QtQuick3D_Particles3D[] = "QtQuick3D.Particles3D"; inline constexpr char QtQuick3D_Particles3D_cppnative[] = "QtQuick3D.Particles3D-cppnative"; inline constexpr char QtQuick[] = "QtQuick"; +inline constexpr char QtQuick_cppnative[] = "QtQuick-cppnative"; inline constexpr char QtQuick_Controls[] = "QtQuick.Controls"; inline constexpr char QtQuick_Dialogs[] = "QtQuick.Dialogs"; inline constexpr char QtQuick_Extras[] = "QtQuick.Extras"; @@ -90,9 +92,7 @@ inline constexpr char QtQuick_Layouts[] = "QtQuick.Layouts"; inline constexpr char QtQuick_Studio_Components[] = "QtQuick.Studio.Components"; inline constexpr char QtQuick_Timeline[] = "QtQuick.Timeline"; inline constexpr char QtQuick_Window[] = "QtQuick.Window"; -inline constexpr char QtQuick_cppnative[] = "QtQuick-cppnative"; inline constexpr char Qt_SafeRenderer[] = "Qt.SafeRenderer"; -inline constexpr char QuickStateOperation[] = "QuickStateOperation"; inline constexpr char Rectangle[] = "Rectangle"; inline constexpr char Repeater[] = "Repeater"; inline constexpr char SafePicture[] = "SafePicture"; @@ -158,7 +158,6 @@ class CommonTypeCache CacheType<QML, url>, CacheType<QML, var>, CacheType<QML_cppnative, FloatType>, - CacheType<QML_cppnative, QQuickStateOperation>, CacheType<QtMultimedia, SoundEffect>, CacheType<QtQuick, BorderImage>, CacheType<QtQuick, Connections>, @@ -194,12 +193,14 @@ class CommonTypeCache CacheType<QtQuick3D, Effect>, CacheType<QtQuick3D, InstanceList>, CacheType<QtQuick3D, InstanceListEntry>, + CacheType<QtQuick3D, Light>, CacheType<QtQuick3D, Material>, CacheType<QtQuick3D, Model>, CacheType<QtQuick3D, Node>, CacheType<QtQuick3D, Pass>, CacheType<QtQuick3D, PrincipledMaterial>, CacheType<QtQuick3D, SceneEnvironment>, + CacheType<QtQuick3D, SpecularGlossyMaterial>, CacheType<QtQuick3D, Shader>, CacheType<QtQuick3D, Texture>, CacheType<QtQuick3D, TextureInput>, @@ -226,7 +227,7 @@ class CommonTypeCache CacheType<QtQuick_Timeline, KeyframeGroup>, CacheType<QtQuick_Timeline, Timeline>, CacheType<QtQuick_Timeline, TimelineAnimation>, - CacheType<QtQuick_cppnative, QuickStateOperation>, + CacheType<QtQuick_cppnative, QQuickStateOperation>, CacheType<Qt_SafeRenderer, SafePicture>, CacheType<Qt_SafeRenderer, SafeRendererPicture>, CacheType<QtQuick_Window, Window>>; @@ -242,15 +243,13 @@ public: } TypeId refreshTypedId(BaseCacheType &type, - Utils::SmallStringView moduleName, - Utils::SmallStringView typeName) const + ::Utils::SmallStringView moduleName, + ::Utils::SmallStringView typeName) const { if (!type.moduleId) type.moduleId = m_projectStorage.moduleId(moduleName); - type.typeId = m_projectStorage.typeId(type.moduleId, - typeName, - QmlDesigner::Storage::Synchronization::Version{}); + type.typeId = m_projectStorage.typeId(type.moduleId, typeName, QmlDesigner::Storage::Version{}); return type.typeId; } diff --git a/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp new file mode 100644 index 00000000000..c458d8376b9 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.cpp @@ -0,0 +1,110 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "modulescanner.h" + +#include <private/qqmldirparser_p.h> + +#include <QDirIterator> +#include <QFile> +#include <QHash> + +namespace QmlDesigner { + +namespace { + +std::optional<QString> contentAsQString(const QString &filePath) +{ + QFile file{filePath}; + if (file.open(QIODevice::ReadOnly)) + return {QString::fromUtf8(file.readAll())}; + + return {}; +} + +QString createVersion(const QMultiHash<QString, QQmlDirParser::Component> &components) +{ + auto found = std::max_element(components.begin(), components.end(), [](auto &&first, auto &&second) { + return first.version < second.version; + }); + + if (found != components.end() && found->version.isValid()) + return QString{"%1.%2"}.arg(found->version.majorVersion()).arg(found->version.minorVersion()); + + return {}; +} + +constexpr auto coreModules = std::make_tuple(QStringView{u"QtQuick"}, + QStringView{u"QtQuick.Controls"}, + QStringView{u"QtQuick3D"}, + QStringView{u"QtQuick3D.Helpers"}, + QStringView{u"QtQuick3D.Particles3D"}); + +bool isCoreVersion(QStringView moduleName) +{ + return std::apply([=](auto... coreModuleName) { return ((moduleName == coreModuleName) || ...); }, + coreModules); +} + +QString createCoreVersion(QStringView moduleName, ExternalDependenciesInterface &externalDependencies) +{ + if (isCoreVersion(moduleName)) + return externalDependencies.qtQuickVersion(); + + return {}; +} + +} // namespace + +void ModuleScanner::scan(const QStringList &modulePaths) +{ + for (const QString &modulePath : modulePaths) + scan(modulePath.toStdString()); +} + +void ModuleScanner::scan([[maybe_unused]] std::string_view modulePath) +{ + if (modulePath.empty()) + return; + + QDirIterator dirIterator{QString::fromUtf8(modulePath), + QDir::Dirs | QDir::NoDotAndDotDot, + QDirIterator::Subdirectories}; + + while (dirIterator.hasNext()) { + auto directoryPath = dirIterator.next(); + QString qmldirPath = directoryPath + "/qmldir"; + if (QFileInfo::exists(qmldirPath)) { + QQmlDirParser parser; + + auto content = contentAsQString(qmldirPath); + if (!content) + continue; + + bool hasError = parser.parse(*content); + if (hasError) + continue; + + auto moduleName = parser.typeNamespace(); + + if (moduleName.isEmpty() || m_skip(moduleName)) + continue; + + QString version = m_versionScanning == VersionScanning::Yes + ? createVersion(parser.components()) + : QString{}; + + QString coreModuleVersion = createCoreVersion(moduleName, m_externalDependencies); + + if (!coreModuleVersion.isEmpty()) + version = coreModuleVersion; + + m_modules.push_back(Import::createLibraryImport(moduleName, version)); + } + } + + std::sort(m_modules.begin(), m_modules.end()); + m_modules.erase(std::unique(m_modules.begin(), m_modules.end()), m_modules.end()); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.h b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.h new file mode 100644 index 00000000000..2083d1d48c0 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/projectstorage/modulescanner.h @@ -0,0 +1,46 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include <qmldesignercorelib_global.h> + +#include <externaldependenciesinterface.h> +#include <import.h> + +#include <optional> + +namespace QmlDesigner { + +enum class VersionScanning { No, Yes }; + +class QMLDESIGNERCORE_EXPORT ModuleScanner +{ +public: + using SkipFunction = std::function<bool(QStringView)>; + + ModuleScanner([[maybe_unused]] SkipFunction skip, + [[maybe_unused]] VersionScanning versionScanning, + [[maybe_unused]] ExternalDependenciesInterface &externalDependencies) + : m_skip{std::move(skip)} + , m_versionScanning{versionScanning} + , m_externalDependencies{externalDependencies} + { + m_modules.reserve(128); + } + + void scan(const QStringList &modulePaths); + + const Imports &modules() const { return m_modules; } + +private: + void scan(std::string_view modulePaths); + +private: + Imports m_modules; + SkipFunction m_skip; + VersionScanning m_versionScanning; + ExternalDependenciesInterface &m_externalDependencies; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index ad3e8931af8..974ebdf6aab 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -45,63 +45,61 @@ public: void synchronize(Storage::Synchronization::SynchronizationPackage package) override { - Sqlite::ImmediateTransaction transaction{database}; - - AliasPropertyDeclarations insertedAliasPropertyDeclarations; - AliasPropertyDeclarations updatedAliasPropertyDeclarations; - - AliasPropertyDeclarations relinkableAliasPropertyDeclarations; - PropertyDeclarations relinkablePropertyDeclarations; - Prototypes relinkablePrototypes; - Prototypes relinkableExtensions; - TypeIds deletedTypeIds; - - 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); - - 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(); - - transaction.commit(); + Sqlite::withImmediateTransaction(database, [&] { + AliasPropertyDeclarations insertedAliasPropertyDeclarations; + AliasPropertyDeclarations updatedAliasPropertyDeclarations; + + AliasPropertyDeclarations relinkableAliasPropertyDeclarations; + PropertyDeclarations relinkablePropertyDeclarations; + Prototypes relinkablePrototypes; + Prototypes relinkableExtensions; + TypeIds deletedTypeIds; + + 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); + + 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(); + }); } ModuleId moduleId(Utils::SmallStringView moduleName) const override @@ -119,7 +117,7 @@ public: TypeId typeId(ModuleId moduleId, Utils::SmallStringView exportedTypeName, - Storage::Synchronization::Version version) const + Storage::Version version) const override { if (version.minor) return selectTypeIdByModuleIdAndExportedNameAndVersionStatement @@ -136,19 +134,20 @@ public: .template valueWithTransaction<TypeId>(moduleId, exportedTypeName); } - PropertyDeclarationIds propertyDeclarationIds(TypeId typeId) const + PropertyDeclarationIds propertyDeclarationIds(TypeId typeId) const override { return selectPropertyDeclarationIdsForTypeStatement .template valuesWithTransaction<PropertyDeclarationId>(32, typeId); } - PropertyDeclarationIds localPropertyDeclarationIds(TypeId typeId) const + PropertyDeclarationIds localPropertyDeclarationIds(TypeId typeId) const override { return selectLocalPropertyDeclarationIdsForTypeStatement .template valuesWithTransaction<PropertyDeclarationId>(16, typeId); } - PropertyDeclarationId propertyDeclarationId(TypeId typeId, Utils::SmallStringView propertyName) const + PropertyDeclarationId propertyDeclarationId(TypeId typeId, + Utils::SmallStringView propertyName) const override { return selectPropertyDeclarationIdForTypeAndPropertyNameStatement .template valueWithTransaction<PropertyDeclarationId>(typeId, propertyName); @@ -162,69 +161,74 @@ public: } std::optional<Storage::Info::PropertyDeclaration> propertyDeclaration( - PropertyDeclarationId propertyDeclarationId) const + PropertyDeclarationId propertyDeclarationId) const override { return selectPropertyDeclarationForPropertyDeclarationIdStatement .template optionalValueWithTransaction<Storage::Info::PropertyDeclaration>( propertyDeclarationId); } - std::optional<Storage::Info::Type> type(TypeId typeId) const + std::optional<Storage::Info::Type> type(TypeId typeId) const override { return selectInfoTypeByTypeIdStatement.template optionalValueWithTransaction<Storage::Info::Type>( typeId); } - std::vector<Utils::SmallString> signalDeclarationNames(TypeId typeId) const + std::vector<Utils::SmallString> signalDeclarationNames(TypeId typeId) const override { return selectSignalDeclarationNamesForTypeStatement .template valuesWithTransaction<Utils::SmallString>(32, typeId); } - std::vector<Utils::SmallString> functionDeclarationNames(TypeId typeId) const + std::vector<Utils::SmallString> functionDeclarationNames(TypeId typeId) const override { return selectFuncionDeclarationNamesForTypeStatement .template valuesWithTransaction<Utils::SmallString>(32, typeId); } - std::optional<Utils::SmallString> propertyName(PropertyDeclarationId propertyDeclarationId) const + std::optional<Utils::SmallString> propertyName(PropertyDeclarationId propertyDeclarationId) const override { return selectPropertyNameStatement.template optionalValueWithTransaction<Utils::SmallString>( propertyDeclarationId); } + const Storage::Info::CommonTypeCache<ProjectStorageInterface> &commonTypeCache() const override + { + return commonTypeCache_; + } + template<const char *moduleName, const char *typeName> TypeId commonTypeId() const { - return commonTypeCache.template typeId<moduleName, typeName>(); + return commonTypeCache_.template typeId<moduleName, typeName>(); } template<typename BuiltinType> TypeId builtinTypeId() const { - return commonTypeCache.template builtinTypeId<BuiltinType>(); + return commonTypeCache_.template builtinTypeId<BuiltinType>(); } template<const char *builtinType> TypeId builtinTypeId() const { - return commonTypeCache.template builtinTypeId<builtinType>(); + return commonTypeCache_.template builtinTypeId<builtinType>(); } - TypeIds prototypeIds(TypeId type) const + TypeIds prototypeIds(TypeId type) const override { return selectPrototypeIdsForTypeIdInOrderStatement.template valuesWithTransaction<TypeId>(16, type); } - TypeIds prototypeAndSelfIds(TypeId type) const + TypeIds prototypeAndSelfIds(TypeId type) const override { return selectPrototypeAndSelfIdsForTypeIdInOrderStatement .template valuesWithTransaction<TypeId>(16, type); } template<typename... TypeIds> - bool isBasedOn(TypeId typeId, TypeIds... baseTypeIds) const + bool isBasedOn_(TypeId typeId, TypeIds... baseTypeIds) const { static_assert(((std::is_same_v<TypeId, TypeIds>) &&...), "Parameter must be a TypeId!"); @@ -238,6 +242,48 @@ public: return false; } + bool isBasedOn(TypeId typeId) const { return isBasedOn_(typeId); } + + bool isBasedOn(TypeId typeId, TypeId id1) const override { return isBasedOn_(typeId, id1); } + + 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, + TypeId id7) const override + { + return isBasedOn_(typeId, id1, id2, id3, id4, id5, id6, id7); + } + TypeId fetchTypeIdByExportedName(Utils::SmallStringView name) const { return selectTypeIdByExportedNameStatement.template valueWithTransaction<TypeId>(name); @@ -257,38 +303,35 @@ public: Storage::Synchronization::Type fetchTypeByTypeId(TypeId typeId) { - Sqlite::DeferredTransaction transaction{database}; - - auto type = selectTypeByTypeIdStatement.template value<Storage::Synchronization::Type>(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); - - transaction.commit(); + 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; + return type; + }); } Storage::Synchronization::Types fetchTypes() { - Sqlite::DeferredTransaction transaction{database}; + return Sqlite::withDeferredTransaction(database, [&] { + auto types = selectTypesStatement.template values<Storage::Synchronization::Type>(64); - 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); - } - - transaction.commit(); + 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; + return types; + }); } bool fetchIsProtype(TypeId type, TypeId prototype) @@ -311,13 +354,9 @@ public: SourceContextId fetchSourceContextId(Utils::SmallStringView sourceContextPath) { try { - Sqlite::DeferredTransaction transaction{database}; - - auto sourceContextId = fetchSourceContextIdUnguarded(sourceContextPath); - - transaction.commit(); - - return sourceContextId; + return Sqlite::withDeferredTransaction(database, [&] { + return fetchSourceContextIdUnguarded(sourceContextPath); + }); } catch (const Sqlite::ConstraintPreventsModification &) { return fetchSourceContextId(sourceContextPath); } @@ -325,18 +364,16 @@ public: Utils::PathString fetchSourceContextPath(SourceContextId sourceContextId) const { - Sqlite::DeferredTransaction transaction{database}; - - auto optionalSourceContextPath = selectSourceContextPathFromSourceContextsBySourceContextIdStatement - .template optionalValue<Utils::PathString>( - sourceContextId); + return Sqlite::withDeferredTransaction(database, [&] { + auto optionalSourceContextPath = selectSourceContextPathFromSourceContextsBySourceContextIdStatement + .template optionalValue<Utils::PathString>( + sourceContextId); - if (!optionalSourceContextPath) - throw SourceContextIdDoesNotExists(); + if (!optionalSourceContextPath) + throw SourceContextIdDoesNotExists(); - transaction.commit(); - - return std::move(*optionalSourceContextPath); + return std::move(*optionalSourceContextPath); + }); } auto fetchAllSourceContexts() const @@ -347,13 +384,9 @@ public: SourceId fetchSourceId(SourceContextId sourceContextId, Utils::SmallStringView sourceName) { - Sqlite::DeferredTransaction transaction{database}; - - auto sourceId = fetchSourceIdUnguarded(sourceContextId, sourceName); - - transaction.commit(); - - return sourceId; + return Sqlite::withDeferredTransaction(database, [&] { + return fetchSourceIdUnguarded(sourceContextId, sourceName); + }); } auto fetchSourceNameAndSourceContextId(SourceId sourceId) const @@ -369,12 +402,10 @@ public: void clearSources() { - Sqlite::ImmediateTransaction transaction{database}; - - deleteAllSourceContextsStatement.execute(); - deleteAllSourcesStatement.execute(); - - transaction.commit(); + Sqlite::withImmediateTransaction(database, [&] { + deleteAllSourceContextsStatement.execute(); + deleteAllSourcesStatement.execute(); + }); } SourceContextId fetchSourceContextId(SourceId sourceId) const @@ -414,16 +445,22 @@ public: sourceId); } + std::optional<Storage::Synchronization::ProjectData> fetchProjectData(SourceId sourceId) const override + { + return selectProjectDataForSourceIdStatement + .template optionalValueWithTransaction<Storage::Synchronization::ProjectData>(sourceId); + } + Storage::Synchronization::ProjectDatas fetchProjectDatas(SourceId projectSourceId) const override { - return selectProjectDatasForModuleIdStatement + return selectProjectDatasForSourceIdStatement .template valuesWithTransaction<Storage::Synchronization::ProjectData>(64, projectSourceId); } Storage::Synchronization::ProjectDatas fetchProjectDatas(const SourceIds &projectSourceIds) const { - return selectProjectDatasForModuleIdsStatement.template valuesWithTransaction< + return selectProjectDatasForSourceIdsStatement.template valuesWithTransaction< Storage::Synchronization::ProjectData>(64, toIntegers(projectSourceIds)); } @@ -470,24 +507,15 @@ private: ModuleId fetchModuleId(Utils::SmallStringView moduleName) { - Sqlite::DeferredTransaction transaction{database}; - - ModuleId moduleId = fetchModuleIdUnguarded(moduleName); - - transaction.commit(); - - return moduleId; + return Sqlite::withDeferredTransaction(database, + [&] { return fetchModuleIdUnguarded(moduleName); }); } auto fetchModuleName(ModuleId id) { - Sqlite::DeferredTransaction transaction{database}; - - auto moduleName = fetchModuleNameUnguarded(id); - - transaction.commit(); - - return moduleName; + return Sqlite::withDeferredTransaction(database, [&] { + return fetchModuleNameUnguarded(id); + }); } auto fetchAllModules() const @@ -712,7 +740,7 @@ private: < std::tie(second.projectSourceId, second.sourceId); }); - auto range = selectProjectDatasForModuleIdsStatement + auto range = selectProjectDatasForSourceIdsStatement .template range<Storage::Synchronization::ProjectData>( toIntegers(updatedProjectSourceIds)); @@ -1527,7 +1555,7 @@ private: ModuleExportedImportId moduleExportedImportId) { Storage::Synchronization::Import additionImport{ exportedModuleId, - Storage::Synchronization::Version{majorVersion, minorVersion}, + Storage::Version{majorVersion, minorVersion}, import.sourceId}; auto exportedImportKind = importKind == Storage::Synchronization::ImportKind::Import @@ -2581,6 +2609,7 @@ private: table.addColumn("fileType", Sqlite::StrictColumnType::Integer); table.addPrimaryKeyContraint({projectSourceIdColumn, sourceIdColumn}); + table.addUniqueIndex({sourceIdColumn}); table.initialize(database); } @@ -2590,7 +2619,7 @@ public: Database &database; Initializer initializer; mutable ModuleCache moduleCache{ModuleStorageAdapter{*this}}; - Storage::Info::CommonTypeCache<ProjectStorage<Database>> commonTypeCache{*this}; + Storage::Info::CommonTypeCache<ProjectStorageInterface> commonTypeCache_{*this}; ReadWriteStatement<1, 3> upsertTypeStatement{ "INSERT INTO types(sourceId, name, traits) VALUES(?1, ?2, ?3) ON CONFLICT DO " "UPDATE SET traits=excluded.traits WHERE traits IS NOT " @@ -3082,7 +3111,7 @@ public: "DELETE FROM exportedTypeNames WHERE exportedTypeNameId=?", database}; WriteStatement<2> updateExportedTypeNameTypeIdStatement{ "UPDATE exportedTypeNames SET typeId=?2 WHERE exportedTypeNameId=?1", database}; - mutable ReadStatement<4, 1> selectProjectDatasForModuleIdsStatement{ + mutable ReadStatement<4, 1> selectProjectDatasForSourceIdsStatement{ "SELECT projectSourceId, sourceId, moduleId, fileType FROM projectDatas WHERE " "projectSourceId IN carray(?1) ORDER BY projectSourceId, sourceId", database}; @@ -3095,10 +3124,14 @@ public: WriteStatement<4> updateProjectDataStatement{ "UPDATE projectDatas SET moduleId=?3, fileType=?4 WHERE projectSourceId=?1 AND sourceId=?2", database}; - mutable ReadStatement<4, 1> selectProjectDatasForModuleIdStatement{ + mutable ReadStatement<4, 1> selectProjectDatasForSourceIdStatement{ "SELECT projectSourceId, sourceId, moduleId, fileType FROM projectDatas WHERE " "projectSourceId=?1", database}; + mutable ReadStatement<4, 1> selectProjectDataForSourceIdStatement{ + "SELECT projectSourceId, sourceId, moduleId, fileType FROM projectDatas WHERE " + "sourceId=?1 LIMIT 1", + database}; mutable ReadStatement<1, 1> selectTypeIdsForSourceIdsStatement{ "SELECT typeId FROM types WHERE sourceId IN carray(?1)", database}; mutable ReadStatement<6, 1> selectModuleExportedImportsForSourceIdStatement{ diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp index d574ef54750..07e925df689 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp @@ -95,4 +95,9 @@ const char *FileStatusHasInvalidSourceId::what() const noexcept return "The source id in file status is invalid!"; } +const char *ProjectStorageError::what() const noexcept +{ + return "Project storage error!"; +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h index d78283ca180..e5e50e76092 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h @@ -9,109 +9,115 @@ namespace QmlDesigner { -class QMLDESIGNERCORE_EXPORT NoSourcePathForInvalidSourceId : std::exception +class QMLDESIGNERCORE_EXPORT ProjectStorageError : std::exception { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT NoSourceContextPathForInvalidSourceContextId : std::exception +class QMLDESIGNERCORE_EXPORT NoSourcePathForInvalidSourceId : ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT SourceContextIdDoesNotExists : std::exception +class QMLDESIGNERCORE_EXPORT NoSourceContextPathForInvalidSourceContextId : ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT SourceIdDoesNotExists : std::exception +class QMLDESIGNERCORE_EXPORT SourceContextIdDoesNotExists : ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT TypeHasInvalidSourceId : std::exception +class QMLDESIGNERCORE_EXPORT SourceIdDoesNotExists : ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT ModuleDoesNotExists : std::exception +class QMLDESIGNERCORE_EXPORT TypeHasInvalidSourceId : ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT ModuleAlreadyExists : std::exception +class QMLDESIGNERCORE_EXPORT ModuleDoesNotExists : ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT ExportedTypeCannotBeInserted : std::exception +class QMLDESIGNERCORE_EXPORT ModuleAlreadyExists : ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT TypeNameDoesNotExists : std::exception +class QMLDESIGNERCORE_EXPORT ExportedTypeCannotBeInserted : ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT PropertyNameDoesNotExists : std::exception +class QMLDESIGNERCORE_EXPORT TypeNameDoesNotExists : ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT PrototypeChainCycle : std::exception +class QMLDESIGNERCORE_EXPORT PropertyNameDoesNotExists : ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT AliasChainCycle : std::exception +class QMLDESIGNERCORE_EXPORT PrototypeChainCycle : ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT CannotParseQmlTypesFile : std::exception +class QMLDESIGNERCORE_EXPORT AliasChainCycle : ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT CannotParseQmlDocumentFile : std::exception +class QMLDESIGNERCORE_EXPORT CannotParseQmlTypesFile : ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidProjectSourceId : std::exception +class QMLDESIGNERCORE_EXPORT CannotParseQmlDocumentFile : ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidSourceId : std::exception +class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidProjectSourceId : ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidModuleId : std::exception +class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidSourceId : ProjectStorageError { public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT FileStatusHasInvalidSourceId : std::exception +class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidModuleId : ProjectStorageError +{ +public: + const char *what() const noexcept override; +}; + +class QMLDESIGNERCORE_EXPORT FileStatusHasInvalidSourceId : ProjectStorageError { public: const char *what() const noexcept override; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragefwd.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragefwd.h index 461868ec659..db0cec068bc 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragefwd.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragefwd.h @@ -5,9 +5,11 @@ namespace Sqlite { class Database; -}; +} namespace QmlDesigner { +class ProjectStorageInterface; + template<typename Database> class ProjectStorage; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h index 71823d3ad86..b61a6ec331a 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h @@ -59,7 +59,7 @@ constexpr TypeTraits operator&(TypeTraits first, TypeTraits second) return static_cast<TypeTraits>(static_cast<int>(first) & static_cast<int>(second)); } -using TypeNameString = Utils::BasicSmallString<63>; +using TypeNameString = ::Utils::BasicSmallString<63>; } // namespace QmlDesigner::Storage @@ -69,7 +69,7 @@ class PropertyDeclaration { public: PropertyDeclaration(TypeId typeId, - Utils::SmallStringView name, + ::Utils::SmallStringView name, PropertyDeclarationTraits traits, TypeId propertyTypeId) : typeId{typeId} @@ -79,7 +79,7 @@ public: {} TypeId typeId; - Utils::SmallString name; + ::Utils::SmallString name; PropertyDeclarationTraits traits; TypeId propertyTypeId; }; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h index f3e87e2cf4e..bc74172ecd7 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h @@ -3,9 +3,13 @@ #pragma once +#include "commontypecache.h" #include "filestatus.h" +#include "projectstorageids.h" #include "projectstoragetypes.h" +#include <utils/smallstringview.h> + namespace QmlDesigner { class ProjectStorageInterface @@ -13,10 +17,56 @@ class ProjectStorageInterface public: virtual void synchronize(Storage::Synchronization::SynchronizationPackage package) = 0; - virtual ModuleId moduleId(Utils::SmallStringView name) const = 0; + virtual ModuleId moduleId(::Utils::SmallStringView name) const = 0; + virtual std::optional<Storage::Info::PropertyDeclaration> + propertyDeclaration(PropertyDeclarationId propertyDeclarationId) const = 0; + virtual TypeId typeId(ModuleId moduleId, + ::Utils::SmallStringView exportedTypeName, + Storage::Version version) const + = 0; + virtual PropertyDeclarationIds propertyDeclarationIds(TypeId typeId) const = 0; + virtual PropertyDeclarationIds localPropertyDeclarationIds(TypeId typeId) const = 0; + virtual PropertyDeclarationId propertyDeclarationId(TypeId typeId, + ::Utils::SmallStringView propertyName) const + = 0; + virtual std::optional<Storage::Info::Type> type(TypeId typeId) const = 0; + virtual std::vector<::Utils::SmallString> signalDeclarationNames(TypeId typeId) const = 0; + virtual std::vector<::Utils::SmallString> functionDeclarationNames(TypeId typeId) const = 0; + virtual std::optional<::Utils::SmallString> + propertyName(PropertyDeclarationId propertyDeclarationId) const = 0; + virtual TypeIds prototypeAndSelfIds(TypeId type) const = 0; + virtual TypeIds prototypeIds(TypeId type) const = 0; + virtual bool isBasedOn(TypeId, TypeId) const = 0; + virtual bool isBasedOn(TypeId, TypeId, TypeId) const = 0; + virtual bool isBasedOn(TypeId, TypeId, TypeId, TypeId) const = 0; + virtual bool isBasedOn(TypeId, TypeId, TypeId, TypeId, TypeId) const = 0; + virtual bool isBasedOn(TypeId, TypeId, TypeId, TypeId, TypeId, TypeId) const = 0; + virtual bool isBasedOn(TypeId, TypeId, TypeId, TypeId, TypeId, TypeId, TypeId) const = 0; + virtual bool isBasedOn(TypeId, TypeId, TypeId, TypeId, TypeId, TypeId, TypeId, TypeId) const = 0; virtual FileStatus fetchFileStatus(SourceId sourceId) const = 0; virtual Storage::Synchronization::ProjectDatas fetchProjectDatas(SourceId sourceId) const = 0; + virtual std::optional<Storage::Synchronization::ProjectData> fetchProjectData(SourceId sourceId) const = 0; + + virtual const Storage::Info::CommonTypeCache<ProjectStorageInterface> &commonTypeCache() const = 0; + + template<const char *moduleName, const char *typeName> + TypeId commonTypeId() const + { + return commonTypeCache().template typeId<moduleName, typeName>(); + } + + template<typename BuiltinType> + TypeId builtinTypeId() const + { + return commonTypeCache().template builtinTypeId<BuiltinType>(); + } + + template<const char *builtinType> + TypeId builtinTypeId() const + { + return commonTypeCache().template builtinTypeId<builtinType>(); + } protected: ~ProjectStorageInterface() = default; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatcher.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatcher.h index 0d3bc730f81..d79b87807f8 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatcher.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatcher.h @@ -71,7 +71,7 @@ public: } void updateContextIdPaths(const std::vector<IdPaths> &idPaths, - const SourceContextIds &sourceContextIds) + const SourceContextIds &sourceContextIds) override { const auto &[entires, ids] = convertIdPathsToWatcherEntriesAndIds(idPaths); diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatcherinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatcherinterface.h index 110cf07cff0..8e67b80b9a7 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatcherinterface.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatcherinterface.h @@ -19,6 +19,9 @@ public: ProjectStoragePathWatcherInterface &operator=(const ProjectStoragePathWatcherInterface &) = delete; virtual void updateIdPaths(const std::vector<IdPaths> &idPaths) = 0; + virtual void updateContextIdPaths(const std::vector<IdPaths> &idPaths, + const SourceContextIds &sourceContextIds) + = 0; virtual void removeIds(const ProjectPartIds &ids) = 0; virtual void setNotifier(ProjectStoragePathWatcherNotifierInterface *notifier) = 0; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h index 1f20eb93713..83372893785 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h @@ -13,11 +13,7 @@ #include <variant> #include <vector> -namespace QmlDesigner::Storage::Synchronization { - -enum class TypeNameKind { Exported = 1, QualifiedExported = 2 }; - -enum class FileType : char { QmlTypes, QmlDocument }; +namespace QmlDesigner::Storage { class VersionNumber { @@ -83,6 +79,12 @@ public: VersionNumber minor; }; +namespace Synchronization { + +enum class TypeNameKind { Exported = 1, QualifiedExported = 2 }; + +enum class FileType : char { QmlTypes, QmlDocument }; + enum class IsQualified : int { No, Yes }; inline int operator-(IsQualified first, IsQualified second) @@ -107,7 +109,7 @@ class Import public: explicit Import() = default; - explicit Import(ModuleId moduleId, Version version, SourceId sourceId) + explicit Import(ModuleId moduleId, Storage::Version version, SourceId sourceId) : version{version} , moduleId{moduleId} , sourceId{sourceId} @@ -132,7 +134,7 @@ public: } public: - Version version; + Storage::Version version; ModuleId moduleId; SourceId sourceId; }; @@ -162,7 +164,7 @@ public: ImportId importId; SourceId sourceId; ModuleId moduleId; - Version version; + Storage::Version version; }; enum class IsAutoVersion : char { No, Yes }; @@ -177,7 +179,7 @@ class ModuleExportedImport public: explicit ModuleExportedImport(ModuleId moduleId, ModuleId exportedModuleId, - Version version, + Storage::Version version, IsAutoVersion isAutoVersion) : version{version} , moduleId{moduleId} @@ -199,7 +201,7 @@ public: } public: - Version version; + Storage::Version version; ModuleId moduleId; ModuleId exportedModuleId; IsAutoVersion isAutoVersion = IsAutoVersion::No; @@ -234,7 +236,7 @@ public: public: ModuleExportedImportId moduleExportedImportId; - Version version; + Storage::Version version; ModuleId moduleId; ModuleId exportedModuleId; IsAutoVersion isAutoVersion = IsAutoVersion::No; @@ -244,7 +246,7 @@ class ImportedType { public: explicit ImportedType() = default; - explicit ImportedType(Utils::SmallStringView name) + explicit ImportedType(::Utils::SmallStringView name) : name{name} {} @@ -261,7 +263,7 @@ class QualifiedImportedType { public: explicit QualifiedImportedType() = default; - explicit QualifiedImportedType(Utils::SmallStringView name, Import import) + explicit QualifiedImportedType(::Utils::SmallStringView name, Import import) : name{name} , import{std::move(import)} {} @@ -282,25 +284,33 @@ class ExportedType { public: explicit ExportedType() = default; - explicit ExportedType(Utils::SmallStringView name, Version version = Version{}) + explicit ExportedType(::Utils::SmallStringView name, Storage::Version version = Storage::Version{}) : name{name} , version{version} {} - explicit ExportedType(ModuleId moduleId, Utils::SmallStringView name, Version version = Version{}) + explicit ExportedType(ModuleId moduleId, + ::Utils::SmallStringView name, + Storage::Version version = Storage::Version{}) : name{name} , version{version} , moduleId{moduleId} {} - explicit ExportedType(Utils::SmallStringView name, Version version, TypeId typeId, ModuleId moduleId) + explicit ExportedType(::Utils::SmallStringView name, + Storage::Version version, + TypeId typeId, + ModuleId moduleId) : name{name} , version{version} , typeId{typeId} , moduleId{moduleId} {} - explicit ExportedType(ModuleId moduleId, Utils::SmallStringView name, int majorVersion, int minorVersion) + explicit ExportedType(ModuleId moduleId, + ::Utils::SmallStringView name, + int majorVersion, + int minorVersion) : name{name} , version{majorVersion, minorVersion} , moduleId{moduleId} @@ -318,8 +328,8 @@ public: } public: - Utils::SmallString name; - Version version; + ::Utils::SmallString name; + Storage::Version version; TypeId typeId; ModuleId moduleId; }; @@ -330,13 +340,13 @@ class ExportedTypeView { public: explicit ExportedTypeView() = default; - explicit ExportedTypeView(ModuleId moduleId, Utils::SmallStringView name, Version version) + explicit ExportedTypeView(ModuleId moduleId, ::Utils::SmallStringView name, Storage::Version version) : name{name} , version{version} , moduleId{moduleId} {} explicit ExportedTypeView(ModuleId moduleId, - Utils::SmallStringView name, + ::Utils::SmallStringView name, int majorVersion, int minorVersion, TypeId typeId, @@ -349,8 +359,8 @@ public: {} public: - Utils::SmallStringView name; - Version version; + ::Utils::SmallStringView name; + Storage::Version version; TypeId typeId; ModuleId moduleId; ExportedTypeNameId exportedTypeNameId; @@ -362,13 +372,13 @@ class EnumeratorDeclaration { public: explicit EnumeratorDeclaration() = default; - explicit EnumeratorDeclaration(Utils::SmallStringView name, long long value, int hasValue = true) + explicit EnumeratorDeclaration(::Utils::SmallStringView name, long long value, int hasValue = true) : name{name} , value{value} , hasValue{bool(hasValue)} {} - explicit EnumeratorDeclaration(Utils::SmallStringView name) + explicit EnumeratorDeclaration(::Utils::SmallStringView name) : name{name} {} @@ -379,7 +389,7 @@ public: } public: - Utils::SmallString name; + ::Utils::SmallString name; long long value = 0; bool hasValue = false; }; @@ -390,7 +400,7 @@ class EnumerationDeclaration { public: explicit EnumerationDeclaration() = default; - explicit EnumerationDeclaration(Utils::SmallStringView name, + explicit EnumerationDeclaration(::Utils::SmallStringView name, EnumeratorDeclarations enumeratorDeclarations) : name{name} , enumeratorDeclarations{std::move(enumeratorDeclarations)} @@ -413,8 +423,8 @@ class EnumerationDeclarationView { public: explicit EnumerationDeclarationView() = default; - explicit EnumerationDeclarationView(Utils::SmallStringView name, - Utils::SmallStringView enumeratorDeclarations, + explicit EnumerationDeclarationView(::Utils::SmallStringView name, + ::Utils::SmallStringView enumeratorDeclarations, EnumerationDeclarationId id) : name{name} , enumeratorDeclarations{std::move(enumeratorDeclarations)} @@ -422,8 +432,8 @@ public: {} public: - Utils::SmallStringView name; - Utils::SmallStringView enumeratorDeclarations; + ::Utils::SmallStringView name; + ::Utils::SmallStringView enumeratorDeclarations; EnumerationDeclarationId id; }; @@ -431,8 +441,8 @@ class ParameterDeclaration { public: explicit ParameterDeclaration() = default; - explicit ParameterDeclaration(Utils::SmallStringView name, - Utils::SmallStringView typeName, + explicit ParameterDeclaration(::Utils::SmallStringView name, + ::Utils::SmallStringView typeName, PropertyDeclarationTraits traits = {}) : name{name} , typeName{typeName} @@ -446,7 +456,7 @@ public: } public: - Utils::SmallString name; + ::Utils::SmallString name; TypeNameString typeName; PropertyDeclarationTraits traits = {}; }; @@ -457,12 +467,12 @@ class SignalDeclaration { public: explicit SignalDeclaration() = default; - explicit SignalDeclaration(Utils::SmallString name, ParameterDeclarations parameters) + explicit SignalDeclaration(::Utils::SmallString name, ParameterDeclarations parameters) : name{name} , parameters{std::move(parameters)} {} - explicit SignalDeclaration(Utils::SmallString name) + explicit SignalDeclaration(::Utils::SmallString name) : name{name} {} @@ -472,7 +482,7 @@ public: } public: - Utils::SmallString name; + ::Utils::SmallString name; ParameterDeclarations parameters; }; @@ -482,8 +492,8 @@ class SignalDeclarationView { public: explicit SignalDeclarationView() = default; - explicit SignalDeclarationView(Utils::SmallStringView name, - Utils::SmallStringView signature, + explicit SignalDeclarationView(::Utils::SmallStringView name, + ::Utils::SmallStringView signature, SignalDeclarationId id) : name{name} , signature{signature} @@ -491,8 +501,8 @@ public: {} public: - Utils::SmallStringView name; - Utils::SmallStringView signature; + ::Utils::SmallStringView name; + ::Utils::SmallStringView signature; SignalDeclarationId id; }; @@ -500,16 +510,16 @@ class FunctionDeclaration { public: explicit FunctionDeclaration() = default; - explicit FunctionDeclaration(Utils::SmallStringView name, - Utils::SmallStringView returnTypeName, + explicit FunctionDeclaration(::Utils::SmallStringView name, + ::Utils::SmallStringView returnTypeName, ParameterDeclarations parameters) : name{name} , returnTypeName{returnTypeName} , parameters{std::move(parameters)} {} - explicit FunctionDeclaration(Utils::SmallStringView name, - Utils::SmallStringView returnTypeName = {}) + explicit FunctionDeclaration(::Utils::SmallStringView name, + ::Utils::SmallStringView returnTypeName = {}) : name{name} , returnTypeName{returnTypeName} {} @@ -521,7 +531,7 @@ public: } public: - Utils::SmallString name; + ::Utils::SmallString name; TypeNameString returnTypeName; ParameterDeclarations parameters; }; @@ -532,9 +542,9 @@ class FunctionDeclarationView { public: explicit FunctionDeclarationView() = default; - explicit FunctionDeclarationView(Utils::SmallStringView name, - Utils::SmallStringView returnTypeName, - Utils::SmallStringView signature, + explicit FunctionDeclarationView(::Utils::SmallStringView name, + ::Utils::SmallStringView returnTypeName, + ::Utils::SmallStringView signature, FunctionDeclarationId id) : name{name} , returnTypeName{returnTypeName} @@ -543,9 +553,9 @@ public: {} public: - Utils::SmallStringView name; - Utils::SmallStringView returnTypeName; - Utils::SmallStringView signature; + ::Utils::SmallStringView name; + ::Utils::SmallStringView returnTypeName; + ::Utils::SmallStringView signature; FunctionDeclarationId id; }; @@ -555,7 +565,7 @@ class PropertyDeclaration { public: explicit PropertyDeclaration() = default; - explicit PropertyDeclaration(Utils::SmallStringView name, + explicit PropertyDeclaration(::Utils::SmallStringView name, ImportedTypeName typeName, PropertyDeclarationTraits traits) : name{name} @@ -564,7 +574,7 @@ public: , kind{PropertyKind::Property} {} - explicit PropertyDeclaration(Utils::SmallStringView name, + explicit PropertyDeclaration(::Utils::SmallStringView name, TypeId propertyTypeId, PropertyDeclarationTraits traits) : name{name} @@ -573,11 +583,11 @@ public: , kind{PropertyKind::Property} {} - explicit PropertyDeclaration(Utils::SmallStringView name, + explicit PropertyDeclaration(::Utils::SmallStringView name, ImportedTypeName typeName, PropertyDeclarationTraits traits, - Utils::SmallStringView aliasPropertyName, - Utils::SmallStringView aliasPropertyNameTail = {}) + ::Utils::SmallStringView aliasPropertyName, + ::Utils::SmallStringView aliasPropertyNameTail = {}) : name{name} , typeName{std::move(typeName)} , aliasPropertyName{aliasPropertyName} @@ -587,11 +597,11 @@ public: , kind{PropertyKind::Property} {} - explicit PropertyDeclaration(Utils::SmallStringView name, + explicit PropertyDeclaration(::Utils::SmallStringView name, TypeId propertyTypeId, PropertyDeclarationTraits traits, - Utils::SmallStringView aliasPropertyName, - Utils::SmallStringView aliasPropertyNameTail = {}) + ::Utils::SmallStringView aliasPropertyName, + ::Utils::SmallStringView aliasPropertyNameTail = {}) : name{name} , aliasPropertyName{aliasPropertyName} , aliasPropertyNameTail{aliasPropertyNameTail} @@ -600,10 +610,10 @@ public: , kind{PropertyKind::Property} {} - explicit PropertyDeclaration(Utils::SmallStringView name, + explicit PropertyDeclaration(::Utils::SmallStringView name, ImportedTypeName aliasTypeName, - Utils::SmallStringView aliasPropertyName, - Utils::SmallStringView aliasPropertyNameTail = {}) + ::Utils::SmallStringView aliasPropertyName, + ::Utils::SmallStringView aliasPropertyNameTail = {}) : name{name} , typeName{std::move(aliasTypeName)} , aliasPropertyName{aliasPropertyName} @@ -621,10 +631,10 @@ public: } public: - Utils::SmallString name; + ::Utils::SmallString name; ImportedTypeName typeName; - Utils::SmallString aliasPropertyName; - Utils::SmallString aliasPropertyNameTail; + ::Utils::SmallString aliasPropertyName; + ::Utils::SmallString aliasPropertyNameTail; PropertyDeclarationTraits traits = {}; TypeId propertyTypeId; TypeId typeId; @@ -636,7 +646,7 @@ using PropertyDeclarations = std::vector<PropertyDeclaration>; class PropertyDeclarationView { public: - explicit PropertyDeclarationView(Utils::SmallStringView name, + explicit PropertyDeclarationView(::Utils::SmallStringView name, PropertyDeclarationTraits traits, TypeId typeId, ImportedTypeNameId typeNameId, @@ -651,7 +661,7 @@ public: {} public: - Utils::SmallStringView name; + ::Utils::SmallStringView name; PropertyDeclarationTraits traits = {}; TypeId typeId; ImportedTypeNameId typeNameId; @@ -665,7 +675,7 @@ class Type { public: explicit Type() = default; - explicit Type(Utils::SmallStringView typeName, + explicit Type(::Utils::SmallStringView typeName, ImportedTypeName prototype, ImportedTypeName extension, TypeTraits traits, @@ -676,7 +686,7 @@ public: SignalDeclarations signalDeclarations = {}, EnumerationDeclarations enumerationDeclarations = {}, ChangeLevel changeLevel = ChangeLevel::Full, - Utils::SmallStringView defaultPropertyName = {}) + ::Utils::SmallStringView defaultPropertyName = {}) : typeName{typeName} , defaultPropertyName{defaultPropertyName} , prototype{std::move(prototype)} @@ -691,7 +701,7 @@ public: , changeLevel{changeLevel} {} - explicit Type(Utils::SmallStringView typeName, + explicit Type(::Utils::SmallStringView typeName, TypeId prototypeId, TypeId extensionId, TypeTraits traits, @@ -703,7 +713,7 @@ public: , extensionId{extensionId} {} - explicit Type(Utils::SmallStringView typeName, + explicit Type(::Utils::SmallStringView typeName, ImportedTypeName prototype, TypeTraits traits, SourceId sourceId, @@ -715,9 +725,9 @@ public: , changeLevel{changeLevel} {} - explicit Type(Utils::SmallStringView typeName, - Utils::SmallStringView prototype, - Utils::SmallStringView extension, + explicit Type(::Utils::SmallStringView typeName, + ::Utils::SmallStringView prototype, + ::Utils::SmallStringView extension, TypeTraits traits, SourceId sourceId) : typeName{typeName} @@ -729,12 +739,12 @@ public: {} explicit Type(SourceId sourceId, - Utils::SmallStringView typeName, + ::Utils::SmallStringView typeName, TypeId typeId, TypeId prototypeId, TypeId extensionId, TypeTraits traits, - Utils::SmallStringView defaultPropertyName) + ::Utils::SmallStringView defaultPropertyName) : typeName{typeName} , defaultPropertyName{defaultPropertyName} , traits{traits} @@ -758,7 +768,7 @@ public: public: TypeNameString typeName; - Utils::SmallString defaultPropertyName; + ::Utils::SmallString defaultPropertyName; ImportedTypeName prototype; ImportedTypeName extension; ExportedTypes exportedTypes; @@ -855,5 +865,5 @@ public: ModuleIds updatedModuleIds; }; -} // namespace QmlDesigner::Storage::Synchronization - +} // namespace Synchronization +} // namespace QmlDesigner::Storage diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp index f98c69734a8..4713dd0136f 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp @@ -113,9 +113,9 @@ void addSourceIds(SourceIds &sourceIds, const Storage::Synchronization::ProjectD sourceIds.push_back(projectData.sourceId); } -Storage::Synchronization::Version convertVersion(LanguageUtils::ComponentVersion version) +Storage::Version convertVersion(LanguageUtils::ComponentVersion version) { - return Storage::Synchronization::Version{version.majorVersion(), version.minorVersion()}; + return Storage::Version{version.majorVersion(), version.minorVersion()}; } Storage::Synchronization::IsAutoVersion convertToIsAutoVersion(QmlDirParser::Import::Flags flags) @@ -133,7 +133,7 @@ void addDependencies(Storage::Synchronization::Imports &dependencies, for (const QmlDirParser::Import &qmldirDependency : qmldirDependencies) { ModuleId moduleId = projectStorage.moduleId(Utils::PathString{qmldirDependency.module} + "-cppnative"); - dependencies.emplace_back(moduleId, Storage::Synchronization::Version{}, sourceId); + dependencies.emplace_back(moduleId, Storage::Version{}, sourceId); } } @@ -154,46 +154,53 @@ void addModuleExportedImports(Storage::Synchronization::ModuleExportedImports &i Utils::PathString{qmldirImport.module} + "-cppnative"); imports.emplace_back(cppModuleId, exportedCppModuleId, - Storage::Synchronization::Version{}, + Storage::Version{}, Storage::Synchronization::IsAutoVersion::No); } } -} // namespace - -void ProjectStorageUpdater::update(QStringList directories, - QStringList qmlTypesPaths, +std::vector<IdPaths> createIdPaths(ProjectStorageUpdater::WatchedSourceIdsIds watchedSourceIds, ProjectPartId projectPartId) { - Storage::Synchronization::SynchronizationPackage package; - SourceIdsData sourceIdsData{static_cast<std::size_t>(directories.size())}; std::vector<IdPaths> idPaths; idPaths.reserve(4); - updateDirectories(directories, package, sourceIdsData); - updateQmlTypes(qmlTypesPaths, package, sourceIdsData); + idPaths.push_back( + {projectPartId, SourceType::Directory, std::move(watchedSourceIds.directorySourceIds)}); + idPaths.push_back({projectPartId, SourceType::QmlDir, std::move(watchedSourceIds.qmldirSourceIds)}); + idPaths.push_back({projectPartId, SourceType::Qml, std::move(watchedSourceIds.qmlSourceIds)}); + idPaths.push_back( + {projectPartId, SourceType::QmlTypes, std::move(watchedSourceIds.qmltypesSourceIds)}); + + return idPaths; +} + +} // namespace + +void ProjectStorageUpdater::update(QStringList directories, QStringList qmlTypesPaths) +{ + Storage::Synchronization::SynchronizationPackage package; + WatchedSourceIdsIds watchedSourceIds{Utils::span{directories}.size()}; + NotUpdatedSourceIds notUpdatedSourceIds{Utils::span{directories}.size()}; + + updateDirectories(directories, package, notUpdatedSourceIds, watchedSourceIds); + updateQmlTypes(qmlTypesPaths, package, notUpdatedSourceIds, watchedSourceIds); package.updatedSourceIds = filterNotUpdatedSourceIds(std::move(package.updatedSourceIds), - std::move(sourceIdsData.notUpdatedSourceIds)); + std::move(notUpdatedSourceIds.sourceIds)); package.updatedFileStatusSourceIds = filterNotUpdatedSourceIds( std::move(package.updatedFileStatusSourceIds), - std::move(sourceIdsData.notUpdatedFileStatusSourceIds)); + std::move(notUpdatedSourceIds.fileStatusSourceIds)); m_projectStorage.synchronize(std::move(package)); - idPaths.push_back( - {projectPartId, SourceType::Directory, std::move(sourceIdsData.watchedDirectorySourceIds)}); - idPaths.push_back( - {projectPartId, SourceType::QmlDir, std::move(sourceIdsData.watchedQmldirSourceIds)}); - idPaths.push_back({projectPartId, SourceType::Qml, std::move(sourceIdsData.watchedQmlSourceIds)}); - idPaths.push_back( - {projectPartId, SourceType::QmlTypes, std::move(sourceIdsData.watchedQmltypesSourceIds)}); - m_pathWatcher.updateIdPaths(idPaths); + m_pathWatcher.updateIdPaths(createIdPaths(watchedSourceIds, m_projectPartId)); } void ProjectStorageUpdater::updateQmlTypes(const QStringList &qmlTypesPaths, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdsData) + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIdsIds) { if (qmlTypesPaths.empty()) return; @@ -202,7 +209,7 @@ void ProjectStorageUpdater::updateQmlTypes(const QStringList &qmlTypesPaths, for (const QString &qmlTypesPath : qmlTypesPaths) { SourceId sourceId = m_pathCache.sourceId(SourcePath{qmlTypesPath}); - sourceIdsData.watchedQmltypesSourceIds.push_back(sourceId); + watchedSourceIdsIds.qmltypesSourceIds.push_back(sourceId); Storage::Synchronization::ProjectData projectData{sourceId, sourceId, @@ -212,7 +219,7 @@ void ProjectStorageUpdater::updateQmlTypes(const QStringList &qmlTypesPaths, FileState state = parseTypeInfo(projectData, Utils::PathString{qmlTypesPath}, package, - sourceIdsData); + notUpdatedSourceIds); if (state == FileState::Changed) package.projectDatas.push_back(std::move(projectData)); @@ -235,28 +242,30 @@ ProjectStorageUpdater::FileState combineState(FileStates... fileStates) void ProjectStorageUpdater::updateDirectories(const QStringList &directories, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdsData) + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIdsIds) { for (const QString &directory : directories) - updateDirectory({directory}, package, sourceIdsData); + updateDirectory({directory}, package, notUpdatedSourceIds, watchedSourceIdsIds); } void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPath, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdsData) + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIdsIds) { SourcePath qmldirSourcePath{directoryPath + "/qmldir"}; auto [directoryId, qmlDirSourceId] = m_pathCache.sourceContextAndSourceId(qmldirSourcePath); SourcePath directorySourcePath{directoryPath + "/."}; auto directorySourceId = m_pathCache.sourceId(directorySourcePath); - auto directoryState = fileState(directorySourceId, package, sourceIdsData); + auto directoryState = fileState(directorySourceId, package, notUpdatedSourceIds); if (directoryState != FileState::NotExists) - sourceIdsData.watchedDirectorySourceIds.push_back(directorySourceId); + watchedSourceIdsIds.directorySourceIds.push_back(directorySourceId); - auto qmldirState = fileState(qmlDirSourceId, package, sourceIdsData); + auto qmldirState = fileState(qmlDirSourceId, package, notUpdatedSourceIds); if (qmldirState != FileState::NotExists) - sourceIdsData.watchedQmldirSourceIds.push_back(qmlDirSourceId); + watchedSourceIdsIds.qmldirSourceIds.push_back(qmlDirSourceId); switch (combineState(directoryState, qmldirState)) { case FileState::Changed: { @@ -295,20 +304,25 @@ void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPa directoryPath, cppModuleId, package, - sourceIdsData); + notUpdatedSourceIds, + watchedSourceIdsIds); } parseQmlComponents( createComponents(parser.components(), moduleId, pathModuleId, m_fileSystem, directoryPath), directorySourceId, directoryId, package, - sourceIdsData, + notUpdatedSourceIds, + watchedSourceIdsIds, qmldirState); package.updatedProjectSourceIds.push_back(directorySourceId); break; } case FileState::NotChanged: { - parseProjectDatas(m_projectStorage.fetchProjectDatas(directorySourceId), package, sourceIdsData); + parseProjectDatas(m_projectStorage.fetchProjectDatas(directorySourceId), + package, + notUpdatedSourceIds, + watchedSourceIdsIds); break; } case FileState::NotExists: { @@ -327,7 +341,118 @@ void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPa } } -void ProjectStorageUpdater::pathsWithIdsChanged([[maybe_unused]] const std::vector<IdPaths> &) {} +namespace { +SourceContextIds filterUniqueSourceContextIds(const SourceIds &sourceIds, + ProjectStorageUpdater::PathCache &pathCache) +{ + auto sourceContextIds = Utils::transform(sourceIds, [&](SourceId sourceId) { + return pathCache.sourceContextId(sourceId); + }); + + std::sort(sourceContextIds.begin(), sourceContextIds.end()); + auto newEnd = std::unique(sourceContextIds.begin(), sourceContextIds.end()); + sourceContextIds.erase(newEnd, sourceContextIds.end()); + + return sourceContextIds; +} + +SourceIds filterUniqueSourceIds(SourceIds sourceIds) +{ + std::sort(sourceIds.begin(), sourceIds.end()); + auto newEnd = std::unique(sourceIds.begin(), sourceIds.end()); + sourceIds.erase(newEnd, sourceIds.end()); + + return sourceIds; +} + +template<typename Container, typename Id> +bool contains(const Container &container, Id id) +{ + return std::find(container.begin(), container.end(), id) != container.end(); +} +} // namespace + +void ProjectStorageUpdater::pathsWithIdsChanged(const std::vector<IdPaths> &changedIdPaths) +{ + m_changedIdPaths.insert(m_changedIdPaths.end(), changedIdPaths.begin(), changedIdPaths.end()); + + Storage::Synchronization::SynchronizationPackage package; + + WatchedSourceIdsIds watchedSourceIds{10}; + NotUpdatedSourceIds notUpdatedSourceIds{10}; + std::vector<IdPaths> idPaths; + idPaths.reserve(4); + + SourceIds directorySourceIds; + directorySourceIds.reserve(32); + SourceIds qmlDocumentSourceIds; + qmlDocumentSourceIds.reserve(128); + SourceIds qmltypesSourceIds; + qmltypesSourceIds.reserve(32); + + for (const auto &[projectChunkId, sourceIds] : m_changedIdPaths) { + if (projectChunkId.id != m_projectPartId) + continue; + + switch (projectChunkId.sourceType) { + case SourceType::Directory: + case SourceType::QmlDir: + directorySourceIds.insert(directorySourceIds.end(), sourceIds.begin(), sourceIds.end()); + break; + case SourceType::Qml: + case SourceType::QmlUi: + qmlDocumentSourceIds.insert(qmlDocumentSourceIds.end(), sourceIds.begin(), sourceIds.end()); + break; + case SourceType::QmlTypes: + qmltypesSourceIds.insert(qmltypesSourceIds.end(), sourceIds.begin(), sourceIds.end()); + break; + } + } + + auto directorySourceContextIds = filterUniqueSourceContextIds(directorySourceIds, m_pathCache); + + for (auto sourceContextId : directorySourceContextIds) { + Utils::PathString directory = m_pathCache.sourceContextPath(sourceContextId); + updateDirectory(directory, package, notUpdatedSourceIds, watchedSourceIds); + } + + for (SourceId sourceId : filterUniqueSourceIds(qmlDocumentSourceIds)) { + if (!contains(directorySourceContextIds, m_pathCache.sourceContextId(sourceId))) + parseQmlComponent(sourceId, package, notUpdatedSourceIds); + } + + try { + for (SourceId sourceId : filterUniqueSourceIds(std::move(qmltypesSourceIds))) { + if (!contains(directorySourceContextIds, m_pathCache.sourceContextId(sourceId))) { + auto qmltypesPath = m_pathCache.sourcePath(sourceId); + auto projectData = m_projectStorage.fetchProjectData(sourceId); + if (projectData) + parseTypeInfo(*projectData, qmltypesPath, package, notUpdatedSourceIds); + } + } + } catch (const QmlDesigner::CannotParseQmlTypesFile &) { + return; + } + + package.updatedSourceIds = filterNotUpdatedSourceIds(std::move(package.updatedSourceIds), + std::move(notUpdatedSourceIds.sourceIds)); + package.updatedFileStatusSourceIds = filterNotUpdatedSourceIds( + std::move(package.updatedFileStatusSourceIds), + std::move(notUpdatedSourceIds.fileStatusSourceIds)); + + try { + m_projectStorage.synchronize(std::move(package)); + } catch (const ProjectStorageError &) { + return; + } + + if (directorySourceContextIds.size()) { + m_pathWatcher.updateContextIdPaths(createIdPaths(watchedSourceIds, m_projectPartId), + directorySourceContextIds); + } + + m_changedIdPaths.clear(); +} void ProjectStorageUpdater::pathsChanged(const SourceIds &) {} @@ -338,14 +463,15 @@ void ProjectStorageUpdater::parseTypeInfos(const QStringList &typeInfos, Utils::SmallStringView directoryPath, ModuleId moduleId, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdData) + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIds) { for (const QString &typeInfo : typeInfos) { Utils::PathString qmltypesPath = Utils::PathString::join( {directoryPath, "/", Utils::SmallString{typeInfo}}); SourceId sourceId = m_pathCache.sourceId(SourcePathView{qmltypesPath}); - sourceIdData.watchedQmltypesSourceIds.push_back(sourceId); + watchedSourceIds.qmltypesSourceIds.push_back(sourceId); addDependencies(package.moduleDependencies, sourceId, @@ -356,27 +482,28 @@ void ProjectStorageUpdater::parseTypeInfos(const QStringList &typeInfos, auto projectData = package.projectDatas.emplace_back( directorySourceId, sourceId, moduleId, Storage::Synchronization::FileType::QmlTypes); - parseTypeInfo(projectData, qmltypesPath, package, sourceIdData); + parseTypeInfo(projectData, qmltypesPath, package, notUpdatedSourceIds); } } void ProjectStorageUpdater::parseProjectDatas(const Storage::Synchronization::ProjectDatas &projectDatas, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdData) + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIds) { for (const Storage::Synchronization::ProjectData &projectData : projectDatas) { switch (projectData.fileType) { case Storage::Synchronization::FileType::QmlTypes: { - sourceIdData.watchedQmltypesSourceIds.push_back(projectData.sourceId); + watchedSourceIds.qmltypesSourceIds.push_back(projectData.sourceId); auto qmltypesPath = m_pathCache.sourcePath(projectData.sourceId); - parseTypeInfo(projectData, qmltypesPath, package, sourceIdData); + parseTypeInfo(projectData, qmltypesPath, package, notUpdatedSourceIds); break; } case Storage::Synchronization::FileType::QmlDocument: { - sourceIdData.watchedQmlSourceIds.push_back(projectData.sourceId); + watchedSourceIds.qmlSourceIds.push_back(projectData.sourceId); - parseQmlComponent(projectData.sourceId, package, sourceIdData); + parseQmlComponent(projectData.sourceId, package, notUpdatedSourceIds); } }; } @@ -385,9 +512,9 @@ void ProjectStorageUpdater::parseProjectDatas(const Storage::Synchronization::Pr auto ProjectStorageUpdater::parseTypeInfo(const Storage::Synchronization::ProjectData &projectData, Utils::SmallStringView qmltypesPath, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdData) -> FileState + NotUpdatedSourceIds ¬UpdatedSourceIds) -> FileState { - auto state = fileState(projectData.sourceId, package, sourceIdData); + auto state = fileState(projectData.sourceId, package, notUpdatedSourceIds); switch (state) { case FileState::Changed: { package.updatedSourceIds.push_back(projectData.sourceId); @@ -397,7 +524,7 @@ auto ProjectStorageUpdater::parseTypeInfo(const Storage::Synchronization::Projec break; } case FileState::NotChanged: { - sourceIdData.notUpdatedSourceIds.push_back(projectData.sourceId); + notUpdatedSourceIds.sourceIds.push_back(projectData.sourceId); break; } case FileState::NotExists: @@ -413,7 +540,8 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil Storage::Synchronization::ExportedTypes exportedTypes, SourceId directorySourceId, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdData, + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIds, FileState qmldirState) { if (std::find(relativeFilePath.begin(), relativeFilePath.end(), '+') != relativeFilePath.end()) @@ -423,14 +551,14 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil SourceId sourceId = m_pathCache.sourceId(SourcePathView{qmlFilePath}); Storage::Synchronization::Type type; - auto state = fileState(sourceId, package, sourceIdData); + auto state = fileState(sourceId, package, notUpdatedSourceIds); - sourceIdData.watchedQmlSourceIds.push_back(sourceId); + watchedSourceIds.qmlSourceIds.push_back(sourceId); switch (state) { case FileState::NotChanged: if (qmldirState == FileState::NotExists) { - sourceIdData.notUpdatedSourceIds.emplace_back(sourceId); + notUpdatedSourceIds.sourceIds.emplace_back(sourceId); package.projectDatas.emplace_back(directorySourceId, sourceId, ModuleId{}, @@ -465,14 +593,17 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil void ProjectStorageUpdater::parseQmlComponent(SourceId sourceId, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdData) + NotUpdatedSourceIds ¬UpdatedSourceIds) { - auto state = fileState(sourceId, package, sourceIdData); - if (state != FileState::Changed) + auto state = fileState(sourceId, package, notUpdatedSourceIds); + if (state == FileState::NotChanged) return; package.updatedSourceIds.push_back(sourceId); + if (state == FileState::NotExists) + return; + SourcePath sourcePath = m_pathCache.sourcePath(sourceId); const auto content = m_fileSystem.contentAsQString(QString{sourcePath}); @@ -513,8 +644,7 @@ Storage::Synchronization::ExportedTypes createExportedTypes(ProjectStorageUpdate for (const ProjectStorageUpdater::Component &component : components) { exportedTypes.emplace_back(component.moduleId, Utils::SmallString{component.typeName}, - Storage::Synchronization::Version{component.majorVersion, - component.minorVersion}); + Storage::Version{component.majorVersion, component.minorVersion}); } return exportedTypes; @@ -526,7 +656,8 @@ void ProjectStorageUpdater::parseQmlComponents(Components components, SourceId directorySourceId, SourceContextId directoryId, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdsData, + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIdsIds, FileState qmldirState) { std::sort(components.begin(), components.end(), [](auto &&first, auto &&second) { @@ -543,7 +674,8 @@ void ProjectStorageUpdater::parseQmlComponents(Components components, createExportedTypes(componentsWithSameFileName), directorySourceId, package, - sourceIdsData, + notUpdatedSourceIds, + watchedSourceIdsIds, qmldirState); }; @@ -553,7 +685,7 @@ void ProjectStorageUpdater::parseQmlComponents(Components components, ProjectStorageUpdater::FileState ProjectStorageUpdater::fileState( SourceId sourceId, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdData) const + NotUpdatedSourceIds ¬UpdatedSourceIds) const { auto currentFileStatus = m_fileStatusCache.find(sourceId); @@ -570,7 +702,7 @@ ProjectStorageUpdater::FileState ProjectStorageUpdater::fileState( return FileState::Changed; } - sourceIdData.notUpdatedFileStatusSourceIds.push_back(sourceId); + notUpdatedSourceIds.fileStatusSourceIds.push_back(sourceId); return FileState::NotChanged; } diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h index 90ce4d543d1..22778ac932a 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h @@ -43,7 +43,8 @@ public: PathCache &pathCache, QmlDocumentParserInterface &qmlDocumentParser, QmlTypesParserInterface &qmlTypesParser, - class ProjectStoragePathWatcherInterface &pathWatcher) + class ProjectStoragePathWatcherInterface &pathWatcher, + ProjectPartId projectPartId) : m_fileSystem{fileSystem} , m_projectStorage{projectStorage} , m_fileStatusCache{fileStatusCache} @@ -51,11 +52,10 @@ public: , m_qmlDocumentParser{qmlDocumentParser} , m_qmlTypesParser{qmlTypesParser} , m_pathWatcher{pathWatcher} + , m_projectPartId{projectPartId} {} - void update(QStringList directories, - QStringList qmlTypesPaths, - ProjectPartId projectPartId = ProjectPartId{}); + void update(QStringList directories, QStringList qmlTypesPaths); void pathsWithIdsChanged(const std::vector<IdPaths> &idPaths) override; void pathsChanged(const SourceIds &filePathIds) override; @@ -96,38 +96,49 @@ public: NotExists, }; -private: - struct SourceIdsData + struct WatchedSourceIdsIds + { + WatchedSourceIdsIds(std::size_t reserve) + { + directorySourceIds.reserve(reserve); + qmldirSourceIds.reserve(reserve); + qmlSourceIds.reserve(reserve * 30); + qmltypesSourceIds.reserve(reserve * 30); + } + + SourceIds directorySourceIds; + SourceIds qmldirSourceIds; + SourceIds qmlSourceIds; + SourceIds qmltypesSourceIds; + }; + + struct NotUpdatedSourceIds { - SourceIdsData(std::size_t reserve) + NotUpdatedSourceIds(std::size_t reserve) { - notUpdatedFileStatusSourceIds.reserve(reserve * 30); - notUpdatedSourceIds.reserve(reserve * 30); - watchedDirectorySourceIds.reserve(reserve); - watchedQmldirSourceIds.reserve(reserve); - watchedQmlSourceIds.reserve(reserve * 30); - watchedQmltypesSourceIds.reserve(reserve * 30); + fileStatusSourceIds.reserve(reserve * 30); + sourceIds.reserve(reserve * 30); } - SourceIds notUpdatedFileStatusSourceIds; - SourceIds notUpdatedSourceIds; - SourceIds watchedDirectorySourceIds; - SourceIds watchedQmldirSourceIds; - SourceIds watchedQmlSourceIds; - SourceIds watchedQmltypesSourceIds; + SourceIds fileStatusSourceIds; + SourceIds sourceIds; }; +private: void updateQmlTypes(const QStringList &qmlTypesPaths, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdData); + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIdsIds); void updateDirectories(const QStringList &directories, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdData); + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIdsIds); void updateDirectory(const Utils::PathString &directory, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdData); + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIdsIds); void parseTypeInfos(const QStringList &typeInfos, const QList<QmlDirParser::Import> &qmldirDependencies, @@ -136,36 +147,41 @@ private: Utils::SmallStringView directoryPath, ModuleId moduleId, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdData); + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIdsIds); void parseProjectDatas(const Storage::Synchronization::ProjectDatas &projectDatas, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdData); + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIdsIds); FileState parseTypeInfo(const Storage::Synchronization::ProjectData &projectData, Utils::SmallStringView qmltypesPath, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdData); + NotUpdatedSourceIds ¬UpdatedSourceIds); void parseQmlComponents(Components components, SourceId directorySourceId, SourceContextId directoryId, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdData, + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIdsIds, FileState qmldirState); void parseQmlComponent(Utils::SmallStringView fileName, Utils::SmallStringView directory, Storage::Synchronization::ExportedTypes exportedTypes, SourceId directorySourceId, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdData, + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIdsIds, FileState qmldirState); void parseQmlComponent(SourceId sourceId, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdData); + NotUpdatedSourceIds ¬UpdatedSourceIds); FileState fileState(SourceId sourceId, Storage::Synchronization::SynchronizationPackage &package, - SourceIdsData &sourceIdData) const; + NotUpdatedSourceIds ¬UpdatedSourceIds) const; private: + std::vector<IdPaths> m_changedIdPaths; FileSystemInterface &m_fileSystem; ProjectStorageInterface &m_projectStorage; FileStatusCache &m_fileStatusCache; @@ -173,6 +189,7 @@ private: QmlDocumentParserInterface &m_qmlDocumentParser; QmlTypesParserInterface &m_qmlTypesParser; ProjectStoragePathWatcherInterface &m_pathWatcher; + ProjectPartId m_projectPartId; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp index 97aac6a8577..3e9ac43deda 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp @@ -10,7 +10,7 @@ #include <sqlitedatabase.h> -#ifdef QDS_HAS_QMLDOM +#ifdef QDS_BUILD_QMLPARSER #include <private/qqmldomtop_p.h> #endif @@ -19,7 +19,7 @@ namespace QmlDesigner { -#ifdef QDS_HAS_QMLDOM +#ifdef QDS_BUILD_QMLPARSER namespace QmlDom = QQmlJS::Dom; @@ -32,9 +32,9 @@ int convertVersionNumber(qint32 versionNumber) return versionNumber < 0 ? -1 : versionNumber; } -Storage::Synchronization::Version convertVersion(QmlDom::Version version) +Storage::Version convertVersion(QmlDom::Version version) { - return Storage::Synchronization::Version{convertVersionNumber(version.majorVersion), + return Storage::Version{convertVersionNumber(version.majorVersion), convertVersionNumber(version.minorVersion)}; } @@ -66,7 +66,7 @@ Storage::Synchronization::Import createImport(const QmlDom::Import &qmlImport, if (uri.kind() == QmlUriKind::RelativePath) { auto path = createNormalizedPath(directoryPath, uri.localPath()); auto moduleId = storage.moduleId(createNormalizedPath(directoryPath, uri.localPath())); - return Storage::Synchronization::Import(moduleId, Storage::Synchronization::Version{}, sourceId); + return Storage::Synchronization::Import(moduleId, Storage::Version{}, sourceId); } if (uri.kind() == QmlUriKind::ModuleUri) { @@ -109,11 +109,11 @@ void addImports(Storage::Synchronization::Imports &imports, } auto localDirectoryModuleId = storage.moduleId(directoryPath); - imports.emplace_back(localDirectoryModuleId, Storage::Synchronization::Version{}, sourceId); + imports.emplace_back(localDirectoryModuleId, Storage::Version{}, sourceId); ++importCount; auto qmlModuleId = storage.moduleId("QML"); - imports.emplace_back(qmlModuleId, Storage::Synchronization::Version{}, sourceId); + imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId); ++importCount; auto end = imports.end(); diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h index 744825aadc5..2896d54ebe7 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.h @@ -18,7 +18,7 @@ public: using ProjectStorage = QmlDesigner::ProjectStorage<Sqlite::Database>; using PathCache = QmlDesigner::SourcePathCache<ProjectStorage, NonLockingMutex>; -#ifdef QDS_HAS_QMLDOM +#ifdef QDS_BUILD_QMLPARSER QmlDocumentParser(ProjectStorage &storage, PathCache &pathCache) : m_storage{storage} , m_pathCache{pathCache} @@ -35,7 +35,7 @@ public: private: // m_pathCache and m_storage are only used when compiled for QDS -#ifdef QDS_HAS_QMLDOM +#ifdef QDS_BUILD_QMLPARSER ProjectStorage &m_storage; PathCache &m_pathCache; #endif diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp index bca7b3279d8..c858076aa19 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp @@ -4,11 +4,10 @@ #include "qmltypesparser.h" #include "projectstorage.h" -#include "sourcepathcache.h" #include <sqlitedatabase.h> -#ifdef QDS_HAS_QMLDOM +#ifdef QDS_BUILD_QMLPARSER #include <private/qqmldomtop_p.h> #include <private/qqmljstypedescriptionreader_p.h> #endif @@ -20,7 +19,7 @@ namespace QmlDesigner { -#ifdef QDS_HAS_QMLDOM +#ifdef QDS_BUILD_QMLPARSER namespace QmlDom = QQmlJS::Dom; @@ -63,7 +62,7 @@ void appendImports(Storage::Synchronization::Imports &imports, moduleName.append("-cppnative"); ModuleId cppModuleId = storage.moduleId(moduleName); - imports.emplace_back(cppModuleId, Storage::Synchronization::Version{}, sourceId); + imports.emplace_back(cppModuleId, Storage::Version{}, sourceId); } void addImports(Storage::Synchronization::Imports &imports, @@ -75,10 +74,10 @@ void addImports(Storage::Synchronization::Imports &imports, for (const QString &dependency : dependencies) appendImports(imports, dependency, sourceId, storage); - imports.emplace_back(cppModuleId, Storage::Synchronization::Version{}, sourceId); + imports.emplace_back(cppModuleId, Storage::Version{}, sourceId); if (ModuleId qmlCppModuleId = storage.moduleId("QML-cppnative"); cppModuleId != qmlCppModuleId) - imports.emplace_back(qmlCppModuleId, Storage::Synchronization::Version{}, sourceId); + imports.emplace_back(qmlCppModuleId, Storage::Version{}, sourceId); } Storage::TypeTraits createTypeTraits(QQmlJSScope::AccessSemantics accessSematics) @@ -97,9 +96,9 @@ Storage::TypeTraits createTypeTraits(QQmlJSScope::AccessSemantics accessSematics return Storage::TypeTraits::None; } -Storage::Synchronization::Version createVersion(QTypeRevision qmlVersion) +Storage::Version createVersion(QTypeRevision qmlVersion) { - return Storage::Synchronization::Version{qmlVersion.majorVersion(), qmlVersion.minorVersion()}; + return Storage::Version{qmlVersion.majorVersion(), qmlVersion.minorVersion()}; } Storage::Synchronization::ExportedTypes createExports(const QList<QQmlJSScope::Export> &qmlExports, diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h index 09513bb74e3..c1d9a3a0d45 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h @@ -22,16 +22,13 @@ class QmlTypesParser : public QmlTypesParserInterface { public: using ProjectStorage = QmlDesigner::ProjectStorage<Sqlite::Database>; - using PathCache = QmlDesigner::SourcePathCache<ProjectStorage, NonLockingMutex>; -#ifdef QDS_HAS_QMLDOM - QmlTypesParser(PathCache &pathCache, ProjectStorage &storage) - : m_pathCache{pathCache} - , m_storage{storage} +#ifdef QDS_BUILD_QMLPARSER + QmlTypesParser(ProjectStorage &storage) + : m_storage{storage} {} #else - QmlTypesParser(PathCache &, ProjectStorage &) - {} + QmlTypesParser(ProjectStorage &) {} #endif void parse(const QString &sourceContent, @@ -40,9 +37,7 @@ public: const Storage::Synchronization::ProjectData &projectData) override; private: - // m_pathCache and m_storage are only used when compiled for QDS -#ifdef QDS_HAS_QMLDOM - [[maybe_unused]] PathCache &m_pathCache; +#ifdef QDS_BUILD_QMLPARSER ProjectStorage &m_storage; #endif }; diff --git a/src/plugins/qmldesigner/designmodewidget.cpp b/src/plugins/qmldesigner/designmodewidget.cpp index 2acdff18230..1bb427bd055 100644 --- a/src/plugins/qmldesigner/designmodewidget.cpp +++ b/src/plugins/qmldesigner/designmodewidget.cpp @@ -20,6 +20,7 @@ #include <coreplugin/actionmanager/command.h> #include <coreplugin/modemanager.h> #include <qmldesigner/qmldesignerconstants.h> +#include <qmldesignerbase/qmldesignerbaseplugin.h> #include <coreplugin/outputpane.h> #include <coreplugin/modemanager.h> @@ -39,6 +40,7 @@ #include <utils/stylehelper.h> #include <QActionGroup> +#include <QApplication> #include <QBoxLayout> #include <QComboBox> #include <QDir> @@ -92,6 +94,8 @@ DesignModeWidget::DesignModeWidget() , m_crumbleBar(new CrumbleBar(this)) { setAcceptDrops(true); + if (Utils::StyleHelper::isQDSTheme()) + qApp->setStyle(QmlDesignerBasePlugin::style()); } DesignModeWidget::~DesignModeWidget() @@ -182,6 +186,7 @@ void DesignModeWidget::setup() Core::ICore::resourcePath("qmldesigner/workspacePresets/").toString()); QString sheet = QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/dockwidgets.css")); + sheet += QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/scrollbar.css")); m_dockManager->setStyleSheet(Theme::replaceCssColors(sheet)); // Setup icons @@ -216,14 +221,29 @@ void DesignModeWidget::setup() mworkspaces->setOnAllDisabledBehavior(Core::ActionContainer::Show); // Connect opening of the 'workspaces' menu with creation of the workspaces menu connect(mworkspaces->menu(), &QMenu::aboutToShow, this, &DesignModeWidget::aboutToShowWorkspaces); - // Disable workspace menu when context is different to C_DESIGN_MODE - connect(Core::ICore::instance(), &Core::ICore::contextChanged, - this, [mworkspaces](const Core::Context &context){ - if (context.contains(Core::Constants::C_DESIGN_MODE)) + + Core::ActionContainer *mpanes = Core::ActionManager::actionContainer(Core::Constants::M_VIEW_PANES); + + // Initially disable menus + mviews->menu()->setEnabled(false); + mworkspaces->menu()->setEnabled(false); + mpanes->menu()->setEnabled(false); + + // Enable/disable menus when mode is different to MODE_DESIGN + connect(Core::ModeManager::instance(), + &Core::ModeManager::currentModeChanged, + this, + [mviews, mworkspaces, mpanes](Utils::Id mode, Utils::Id) { + if (mode == Core::Constants::MODE_DESIGN) { + mviews->menu()->setEnabled(true); mworkspaces->menu()->setEnabled(true); - else + mpanes->menu()->setEnabled(true); + } else { + mviews->menu()->setEnabled(false); mworkspaces->menu()->setEnabled(false); - }); + mpanes->menu()->setEnabled(false); + } + }); // Create a DockWidget for each QWidget and add them to the DockManager const Core::Context designContext(Core::Constants::C_DESIGN_MODE); @@ -231,60 +251,48 @@ void DesignModeWidget::setup() // First get all navigation views QList<Core::INavigationWidgetFactory *> factories = Core::INavigationWidgetFactory::allNavigationFactories(); - - QList<Core::Command*> viewCommands; + QList<Core::Command *> viewCommands; + const QList<Utils::Id> navigationViewIds = {"Projects", "File System", "Open Documents"}; for (Core::INavigationWidgetFactory *factory : factories) { - Core::NavigationView navigationView; - navigationView.widget = nullptr; - QString uniqueId; - QString title; + Core::NavigationView navigationView = {nullptr, {}}; - if (factory->id() == "Projects") { - navigationView = factory->createWidget(); - hideToolButtons(navigationView.dockToolBarWidgets); - navigationView.widget->setWindowTitle(tr(factory->id().name())); - uniqueId = "Projects"; - title = "Projects"; - } - if (factory->id() == "File System") { - navigationView = factory->createWidget(); - hideToolButtons(navigationView.dockToolBarWidgets); - navigationView.widget->setWindowTitle(tr(factory->id().name())); - uniqueId = "FileSystem"; - title = "File System"; - } - if (factory->id() == "Open Documents") { - navigationView = factory->createWidget(); - hideToolButtons(navigationView.dockToolBarWidgets); - navigationView.widget->setWindowTitle(tr(factory->id().name())); - uniqueId = "OpenDocuments"; - title = "Open Documents"; - } + if (!navigationViewIds.contains(factory->id())) + continue; - if (navigationView.widget) { - // Apply stylesheet to QWidget - QByteArray sheet = Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css"); - sheet += Utils::FileReader::fetchQrc(":/qmldesigner/scrollbar.css"); - sheet += "QLabel { background-color: creatorTheme.DSsectionHeadBackground; }"; - navigationView.widget->setStyleSheet(Theme::replaceCssColors(QString::fromUtf8(sheet))); - - // Create DockWidget - ADS::DockWidget *dockWidget = new ADS::DockWidget(uniqueId); - dockWidget->setWidget(navigationView.widget); - dockWidget->setWindowTitle(title); - m_dockManager->addDockWidget(ADS::NoDockWidgetArea, dockWidget); - - // Set unique id as object name - navigationView.widget->setObjectName(uniqueId); - - // Create menu action - auto command = Core::ActionManager::registerAction(dockWidget->toggleViewAction(), - actionToggle.withSuffix(uniqueId + "Widget"), - designContext); - command->setAttribute(Core::Command::CA_Hide); - viewCommands.append(command); - } + navigationView = factory->createWidget(); + + if (!navigationView.widget) + continue; + + hideToolButtons(navigationView.dockToolBarWidgets); + navigationView.widget->setWindowTitle(tr(factory->id().name())); + + QString idString = factory->id().toSetting().toString(); + const QString title = idString; + const QString uniqueId = idString.remove(" "); // title without whitespaces + + // Apply stylesheet to QWidget + QByteArray sheet = Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css"); + sheet += Utils::FileReader::fetchQrc(":/qmldesigner/scrollbar.css"); + sheet += "QLabel { background-color: creatorTheme.DSsectionHeadBackground; }"; + navigationView.widget->setStyleSheet(Theme::replaceCssColors(QString::fromUtf8(sheet))); + + // Create DockWidget + ADS::DockWidget *dockWidget = new ADS::DockWidget(uniqueId); + dockWidget->setWidget(navigationView.widget); + dockWidget->setWindowTitle(title); + m_dockManager->addDockWidget(ADS::NoDockWidgetArea, dockWidget); + + // Set unique id as object name + navigationView.widget->setObjectName(uniqueId); + + // Create menu action + auto command = Core::ActionManager::registerAction(dockWidget->toggleViewAction(), + actionToggle.withSuffix(uniqueId + "Widget"), + designContext); + command->setAttribute(Core::Command::CA_Hide); + viewCommands.append(command); } // Afterwards get all the other widgets @@ -303,7 +311,8 @@ void DesignModeWidget::setup() // Create menu action auto command = Core::ActionManager::registerAction(dockWidget->toggleViewAction(), - actionToggle.withSuffix(widgetInfo.uniqueId + "Widget"), + actionToggle.withSuffix( + widgetInfo.uniqueId + "Widget"), designContext); command->setAttribute(Core::Command::CA_Hide); viewCommands.append(command); @@ -328,22 +337,13 @@ void DesignModeWidget::setup() command->setAttribute(Core::Command::CA_Hide); viewCommands.append(command); - connect(command->action(), &QAction::triggered, this, [command]() { - if (!command->action()->isChecked()) - return; - - auto cmd = Core::ActionManager::command("QtCreator.Pane.ApplicationOutput"); - QTC_ASSERT(cmd, return); - cmd->action()->trigger(); - }); - connect(outputPanePlaceholder, &Core::OutputPanePlaceHolder::visibilityChangeRequested, m_outputPaneDockWidget, &ADS::DockWidget::toggleView); } - std::sort(viewCommands.begin(), viewCommands.end(), [](Core::Command *first, Core::Command *second){ + std::sort(viewCommands.begin(), viewCommands.end(), [](Core::Command *first, Core::Command *second) { return first->description() < second->description(); }); @@ -356,7 +356,10 @@ void DesignModeWidget::setup() toolBar->addAction(viewManager().componentViewAction()); toolBar->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); - DesignerActionToolBar *designerToolBar = QmlDesignerPlugin::instance()->viewManager().designerActionManager().createToolBar(m_toolBar); + DesignerActionToolBar *designerToolBar = QmlDesignerPlugin::instance() + ->viewManager() + .designerActionManager() + .createToolBar(m_toolBar); designerToolBar->layout()->addWidget(toolBar); @@ -365,9 +368,14 @@ void DesignModeWidget::setup() m_toolBar->setToolbarCreationFlags(Core::EditorToolBar::FlagsStandalone); m_toolBar->setNavigationVisible(true); - connect(m_toolBar, &Core::EditorToolBar::goForwardClicked, this, &DesignModeWidget::toolBarOnGoForwardClicked); - connect(m_toolBar, &Core::EditorToolBar::goBackClicked, this, &DesignModeWidget::toolBarOnGoBackClicked); - + connect(m_toolBar, + &Core::EditorToolBar::goForwardClicked, + this, + &DesignModeWidget::toolBarOnGoForwardClicked); + connect(m_toolBar, + &Core::EditorToolBar::goBackClicked, + this, + &DesignModeWidget::toolBarOnGoBackClicked); QToolBar* toolBarWrapper = new QToolBar(); toolBarWrapper->addWidget(m_toolBar); @@ -375,32 +383,8 @@ void DesignModeWidget::setup() toolBarWrapper->setMovable(false); addToolBar(Qt::TopToolBarArea, toolBarWrapper); - addSpacerToToolBar(toolBar); - auto workspaceComboBox = new QComboBox(); - workspaceComboBox->setMinimumWidth(120); - workspaceComboBox->setToolTip(tr("Switch the active workspace.")); - auto sortedWorkspaces = m_dockManager->workspaces(); - Utils::sort(sortedWorkspaces); - workspaceComboBox->addItems(sortedWorkspaces); - workspaceComboBox->setCurrentText(m_dockManager->activeWorkspace()); - toolBar->addWidget(workspaceComboBox); - - connect(m_dockManager, &ADS::DockManager::workspaceListChanged, - workspaceComboBox, [this, workspaceComboBox]() { - workspaceComboBox->clear(); - auto sortedWorkspaces = m_dockManager->workspaces(); - Utils::sort(sortedWorkspaces); - workspaceComboBox->addItems(sortedWorkspaces); - workspaceComboBox->setCurrentText(m_dockManager->activeWorkspace()); - }); - connect(m_dockManager, &ADS::DockManager::workspaceLoaded, workspaceComboBox, &QComboBox::setCurrentText); - connect(workspaceComboBox, &QComboBox::activated, - m_dockManager, [this, workspaceComboBox]([[maybe_unused]] int index) { - m_dockManager->openWorkspace(workspaceComboBox->currentText()); - }); - const QIcon gaIcon = Utils::StyleHelper::getIconFromIconFont( fontName, Theme::getIconUnicode(Theme::Icon::annotationBubble), 36, 36, Theme::getColor(Theme::IconsBaseColor)); @@ -412,7 +396,6 @@ void DesignModeWidget::setup() m_globalAnnotationEditor.showWidget(); } }); - } if (currentDesignDocument()) @@ -444,8 +427,6 @@ void DesignModeWidget::setup() } }); - - viewManager().enableWidgets(); readSettings(); show(); @@ -453,15 +434,15 @@ void DesignModeWidget::setup() void DesignModeWidget::aboutToShowWorkspaces() { - Core::ActionContainer *aci = Core::ActionManager::actionContainer(QmlDesigner::Constants::M_VIEW_WORKSPACES); + Core::ActionContainer *aci = Core::ActionManager::actionContainer( + QmlDesigner::Constants::M_VIEW_WORKSPACES); QMenu *menu = aci->menu(); menu->clear(); auto *ag = new QActionGroup(menu); connect(ag, &QActionGroup::triggered, this, [this](QAction *action) { - QString workspace = action->data().toString(); - m_dockManager->openWorkspace(workspace); + m_dockManager->openWorkspace(action->data().toString()); }); QAction *action = menu->addAction(tr("Manage...")); @@ -469,21 +450,18 @@ void DesignModeWidget::aboutToShowWorkspaces() QAction *resetWorkspace = menu->addAction(tr("Reset Active")); connect(resetWorkspace, &QAction::triggered, this, [this]() { - if (m_dockManager->resetWorkspacePreset(m_dockManager->activeWorkspace())) + if (m_dockManager->resetWorkspacePreset(m_dockManager->activeWorkspace()->fileName())) m_dockManager->reloadActiveWorkspace(); }); menu->addSeparator(); - // Sort the list of workspaces - auto sortedWorkspaces = m_dockManager->workspaces(); - Utils::sort(sortedWorkspaces); - - for (const auto &workspace : std::as_const(sortedWorkspaces)) { - QAction *action = ag->addAction(workspace); - action->setData(workspace); + auto workspaces = m_dockManager->workspaces(); + for (const auto &workspace : std::as_const(workspaces)) { + QAction *action = ag->addAction(workspace.name()); + action->setData(workspace.fileName()); action->setCheckable(true); - if (workspace == m_dockManager->activeWorkspace()) + if (workspace == *m_dockManager->activeWorkspace()) action->setChecked(true); } menu->addActions(ag->actions()); diff --git a/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp b/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp index 7d7957753c4..079cc0a326b 100644 --- a/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp +++ b/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp @@ -51,6 +51,7 @@ QProcessEnvironment PuppetEnvironmentBuilder::processEnvironment() const addMultiLanguageDatatbase(); addImportPaths(); addCustomFileSelectors(); + addDisableDeferredProperties(); qCInfo(puppetEnvirmentBuild) << "Puppet environment:" << m_environment.toStringList(); @@ -228,6 +229,11 @@ void PuppetEnvironmentBuilder::addCustomFileSelectors() const qCInfo(puppetEnvirmentBuild) << "Puppet selectors:" << customFileSelectors; } +void PuppetEnvironmentBuilder::addDisableDeferredProperties() const +{ + m_environment.set("QML_DISABLE_INTERNAL_DEFERRED_PROPERTIES", "true"); +} + PuppetType PuppetEnvironmentBuilder::determinePuppetType() const { if (m_target && m_target->kit() && m_target->kit()->isValid()) { diff --git a/src/plugins/qmldesigner/puppetenvironmentbuilder.h b/src/plugins/qmldesigner/puppetenvironmentbuilder.h index e3ef33a7211..3c2ffba271e 100644 --- a/src/plugins/qmldesigner/puppetenvironmentbuilder.h +++ b/src/plugins/qmldesigner/puppetenvironmentbuilder.h @@ -49,6 +49,7 @@ private: void addMultiLanguageDatatbase() const; void addImportPaths() const; void addCustomFileSelectors() const; + void addDisableDeferredProperties() const; private: ProjectExplorer::Target *m_target = nullptr; diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index 18039187509..d38dad74c7e 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -30,6 +30,7 @@ const char TOGGLE_RIGHT_SIDEBAR[] = "QmlDesigner.ToggleRightSideBar"; const char TOGGLE_STATES_EDITOR[] = "QmlDesigner.ToggleStatesEditor"; const char GO_INTO_COMPONENT[] = "QmlDesigner.GoIntoComponent"; const char EXPORT_AS_IMAGE[] = "QmlDesigner.ExportAsImage"; +const char TAKE_SCREENSHOT[] = "QmlDesigner.TakeScreenshot"; const char FORMEDITOR_REFRESH[] = "QmlDesigner.FormEditor.Refresh"; const char FORMEDITOR_SNAPPING[] = "QmlDesigner.FormEditor.Snapping"; const char FORMEDITOR_NO_SNAPPING[] = "QmlDesigner.FormEditor.NoSnapping"; @@ -61,7 +62,7 @@ const char EDIT3D_PARTICLES_SEEKER[] = "QmlDesigner.Editor3D.ParticlesSeeker" const char EDIT3D_PARTICLES_RESTART[] = "QmlDesigner.Editor3D.ParticlesRestart"; const char EDIT3D_VISIBILITY_TOGGLES[] = "QmlDesigner.Editor3D.VisibilityToggles"; const char EDIT3D_BACKGROUND_COLOR_ACTIONS[] = "QmlDesigner.Editor3D.BackgroundColorActions"; - +const char EDIT3D_BAKE_LIGHTS[] = "QmlDesigner.Editor3D.BakeLights"; const char QML_DESIGNER_SUBFOLDER[] = "/designer/"; const char COMPONENT_BUNDLES_FOLDER[] = "/ComponentBundles"; diff --git a/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp b/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp index 8282ea419a4..1bd58991657 100644 --- a/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp +++ b/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp @@ -15,6 +15,7 @@ #include <qmlpuppetpaths.h> #include <qtsupport/baseqtversion.h> #include <qtsupport/qtkitinformation.h> +#include <qmlprojectmanager/buildsystem/qmlbuildsystem.h> #include <coreplugin/icore.h> @@ -186,4 +187,95 @@ Utils::FilePath ExternalDependencies::qmlPuppetPath() const return puppetPath; } +namespace { + +QString qmlPath(ProjectExplorer::Target *target) +{ + auto kit = target->kit(); + + if (!kit) + return {}; + + auto qtVersion = QtSupport::QtKitAspect::qtVersion(kit); + if (!qtVersion) + return {}; + + return qtVersion->qmlPath().toString(); +} + +std::tuple<ProjectExplorer::Project *, ProjectExplorer::Target *, QmlProjectManager::QmlBuildSystem *> +activeProjectEntries() +{ + auto project = ProjectExplorer::ProjectManager::startupProject(); + + if (!project) + return {}; + + auto target = project->activeTarget(); + + if (!target) + return {}; + + const auto qmlBuildSystem = qobject_cast<QmlProjectManager::QmlBuildSystem *>( + target->buildSystem()); + + if (qmlBuildSystem) + return std::make_tuple(project, target, qmlBuildSystem); + + return {}; +} +} // namespace + +QStringList ExternalDependencies::modulePaths() const +{ + auto [project, target, qmlBuildSystem] = activeProjectEntries(); + + if (project && target && qmlBuildSystem) { + QStringList modulePaths; + + if (auto path = qmlPath(target); !path.isEmpty()) + modulePaths.push_back(path); + + for (const QString &modulePath : qmlBuildSystem->customImportPaths()) + modulePaths.append(project->projectDirectory().pathAppended(modulePath).toString()); + + return modulePaths; + } + + return {}; +} + +QStringList ExternalDependencies::projectModulePaths() const +{ + auto [project, target, qmlBuildSystem] = activeProjectEntries(); + + if (project && target && qmlBuildSystem) { + QStringList modulePaths; + + for (const QString &modulePath : qmlBuildSystem->customImportPaths()) + modulePaths.append(project->projectDirectory().pathAppended(modulePath).toString()); + } + + return {}; +} + +bool ExternalDependencies::isQt6Project() const +{ + auto [project, target, qmlBuildSystem] = activeProjectEntries(); + + return qmlBuildSystem && qmlBuildSystem->qt6Project(); +} + +QString ExternalDependencies::qtQuickVersion() const +{ + auto [project, target, qmlBuildSystem] = activeProjectEntries(); + + return qmlBuildSystem ? qmlBuildSystem->versionQtQuick() : QString{}; +} + +Utils::FilePath ExternalDependencies::resourcePath(const QString &relativePath) const +{ + return Core::ICore::resourcePath(relativePath); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/qmldesignerexternaldependencies.h b/src/plugins/qmldesigner/qmldesignerexternaldependencies.h index 3e57847ce9d..0f6acf2c106 100644 --- a/src/plugins/qmldesigner/qmldesignerexternaldependencies.h +++ b/src/plugins/qmldesigner/qmldesignerexternaldependencies.h @@ -36,6 +36,11 @@ public: PuppetStartData puppetStartData(const class Model &model) const override; bool instantQmlTextUpdate() const override; Utils::FilePath qmlPuppetPath() const override; + QStringList modulePaths() const override; + QStringList projectModulePaths() const override; + bool isQt6Project() const override; + QString qtQuickVersion() const override; + Utils::FilePath resourcePath(const QString &relativePath) const override; private: const DesignerSettings &m_designerSettings; diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index 039528475a5..a25bbb832b8 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -31,7 +31,6 @@ #include <texttool/texttool.h> #include <timelineeditor/timelineview.h> #include <transitioneditor/transitioneditorview.h> - #include <qmljseditor/qmljseditor.h> #include <qmljseditor/qmljseditorconstants.h> #include <qmljseditor/qmljseditordocument.h> @@ -66,6 +65,7 @@ #include <utils/algorithm.h> #include <utils/hostosinfo.h> #include <utils/qtcassert.h> +#include <utils/uniqueobjectptr.h> #include <QAction> #include <QApplication> @@ -144,6 +144,8 @@ public: DesignModeWidget mainWidget; QtQuickDesignerFactory m_qtQuickDesignerFactory; bool blockEditorChange = false; + Utils::UniqueObjectPtr<QToolBar> toolBar; + Utils::UniqueObjectPtr<QWidget> statusBar; }; QmlDesignerPlugin *QmlDesignerPlugin::m_instance = nullptr; @@ -184,8 +186,9 @@ static bool isDesignerMode(Utils::Id mode) static bool documentIsAlreadyOpen(DesignDocument *designDocument, Core::IEditor *editor, Utils::Id newMode) { return designDocument - && editor == designDocument->editor() - && isDesignerMode(newMode); + && editor == designDocument->editor() + && isDesignerMode(newMode) + && designDocument->fileName() == editor->document()->filePath(); } static bool shouldAssertInException() @@ -241,7 +244,7 @@ bool QmlDesignerPlugin::initialize(const QStringList & /*arguments*/, QString *e ->addAction(cmd, Core::Constants::G_HELP_SUPPORT); connect(action, &QAction::triggered, this, [this] { - lauchFeedbackPopup(Core::Constants::IDE_DISPLAY_NAME); + lauchFeedbackPopupInternal(Core::Constants::IDE_DISPLAY_NAME); }); if (!Utils::HostOsInfo::canCreateOpenGLContext(errorMessage)) @@ -273,8 +276,8 @@ bool QmlDesignerPlugin::initialize(const QStringList & /*arguments*/, QString *e }); if (QmlProjectManager::QmlProject::isQtDesignStudio()) { - ToolBar::create(); - ToolBar::createStatusBar(); + d->toolBar = ToolBar::create(); + d->statusBar = ToolBar::createStatusBar(); } return true; @@ -289,10 +292,10 @@ bool QmlDesignerPlugin::delayedInitialize() Utils::transform(ExtensionSystem::PluginManager::pluginPaths(), [postfix](const QString &p) { return QString(p + postfix); }); - MetaInfo::setPluginPaths(pluginPaths); - d->viewManager.registerView( - std::make_unique<ConnectionView>(d->externalDependencies)); + MetaInfo::initializeGlobal(pluginPaths, d->externalDependencies); + + d->viewManager.registerView(std::make_unique<ConnectionView>(d->externalDependencies)); auto timelineView = d->viewManager.registerView( std::make_unique<TimelineView>(d->externalDependencies)); @@ -334,8 +337,6 @@ bool QmlDesignerPlugin::delayedInitialize() Core::ICore::appendAboutInformation(tr("Licensee: %1").arg(licensee())); } - MetaInfo::global(); - return true; } @@ -438,14 +439,12 @@ void QmlDesignerPlugin::integrateIntoQtCreator(QWidget *modeWidget) &Core::ModeManager::currentModeChanged, [this](Utils::Id newMode, Utils::Id oldMode) { Core::IEditor *currentEditor = Core::EditorManager::currentEditor(); - if (d && currentEditor && checkIfEditorIsQtQuick(currentEditor) + if (isDesignerMode(newMode) && checkIfEditorIsQtQuick(currentEditor) && !documentIsAlreadyOpen(currentDesignDocument(), currentEditor, newMode)) { - if (isDesignerMode(newMode)) { - showDesigner(); - } else if (currentDesignDocument() - || (!isDesignerMode(newMode) && isDesignerMode(oldMode))) { - hideDesigner(); - } + showDesigner(); + } else if (currentDesignDocument() + || (!isDesignerMode(newMode) && isDesignerMode(oldMode))) { + hideDesigner(); } }); } @@ -731,6 +730,18 @@ void QmlDesignerPlugin::trackWidgetFocusTime(QWidget *widget, const QString &ide void QmlDesignerPlugin::lauchFeedbackPopup(const QString &identifier) { + if (Core::ModeManager::currentModeId() == Core::Constants::MODE_DESIGN) + lauchFeedbackPopupInternal(identifier); +} + +void QmlDesignerPlugin::handleFeedback(const QString &feedback, int rating) +{ + const QString identifier = sender()->property("identifier").toString(); + emit usageStatisticsInsertFeedback(identifier, feedback, rating); +} + +void QmlDesignerPlugin::lauchFeedbackPopupInternal(const QString &identifier) +{ m_feedbackWidget = new QQuickWidget(Core::ICore::dialogParent()); m_feedbackWidget->setObjectName(Constants::OBJECT_NAME_TOP_FEEDBACK); @@ -767,12 +778,6 @@ void QmlDesignerPlugin::lauchFeedbackPopup(const QString &identifier) m_feedbackWidget->show(); } -void QmlDesignerPlugin::handleFeedback(const QString &feedback, int rating) -{ - const QString identifier = sender()->property("identifier").toString(); - emit usageStatisticsInsertFeedback(identifier, feedback, rating); -} - void QmlDesignerPlugin::closeFeedbackPopup() { if (m_feedbackWidget) { diff --git a/src/plugins/qmldesigner/qmldesignerplugin.h b/src/plugins/qmldesigner/qmldesignerplugin.h index 78bf02ac042..ee078b273ab 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.h +++ b/src/plugins/qmldesigner/qmldesignerplugin.h @@ -95,6 +95,7 @@ private slots: void handleFeedback(const QString &feedback, int rating); private: // functions + void lauchFeedbackPopupInternal(const QString &identifier); void integrateIntoQtCreator(QWidget *modeWidget); void showDesigner(); void hideDesigner(); @@ -114,7 +115,6 @@ private: // variables QmlDesignerPluginPrivate *d = nullptr; static QmlDesignerPlugin *m_instance; QElapsedTimer m_usageTimer; - StudioConfigSettingsPage m_settingsPage; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp index a80b3148b36..4d327b5491d 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp @@ -39,6 +39,7 @@ #include <coreplugin/icore.h> +#include <QDirIterator> #include <QFileSystemWatcher> #include <QQmlEngine> @@ -175,24 +176,27 @@ class ProjectStorageData public: ProjectStorageData(::ProjectExplorer::Project *project) : database{project->projectDirectory().pathAppended("projectstorage.db").toString()} + , projectPartId{ProjectPartId::create( + pathCache.sourceId(SourcePath{project->projectDirectory().toString() + "/."}).internalId())} {} - Sqlite::Database database; ProjectStorage<Sqlite::Database> storage{database, database.isInitialized()}; ProjectStorageUpdater::PathCache pathCache{storage}; FileSystem fileSystem{pathCache}; FileStatusCache fileStatusCache{fileSystem}; QmlDocumentParser qmlDocumentParser{storage, pathCache}; - QmlTypesParser qmlTypesParser{pathCache, storage}; + QmlTypesParser qmlTypesParser{storage}; ProjectStoragePathWatcher<QFileSystemWatcher, QTimer, ProjectStorageUpdater::PathCache> pathWatcher{pathCache, fileSystem, &updater}; + ProjectPartId projectPartId; ProjectStorageUpdater updater{fileSystem, storage, fileStatusCache, pathCache, qmlDocumentParser, qmlTypesParser, - pathWatcher}; + pathWatcher, + projectPartId}; }; std::unique_ptr<ProjectStorageData> createProjectStorageData(::ProjectExplorer::Project *project) @@ -339,18 +343,25 @@ void projectQmldirPaths(::ProjectExplorer::Target *target, QStringList &qmldirPa qmldirPaths.push_back(QDir::cleanPath(pojectDirectory.absoluteFilePath(importPath)) + "/qmldir"); } -#ifdef QDS_HAS_QMLDOM -bool skipPath(const std::filesystem::path &path) + +#ifdef QDS_HAS_QMLPRIVATE +QStringView currentDirectoryName(const QString &path) { - auto directory = path.filename(); - qDebug() << path.string().data(); + auto found = std::find(path.rbegin(), path.rend(), u'/'); - bool skip = directory == "QtApplicationManager" || directory == "QtInterfaceFramework" - || directory == "QtOpcUa" || directory == "Qt3D" || directory == "Qt3D" - || directory == "Scene2D" || directory == "Scene3D" || directory == "QtWayland" - || directory == "Qt5Compat"; - if (skip) - qDebug() << "skip" << path.string().data(); + if (found == path.rend()) + return {}; + + return QStringView{found.base(), path.end()}; +} +bool skipPath(const QString &path) +{ + auto directory = currentDirectoryName(path); + + bool skip = directory == u"QtApplicationManager" || directory == u"QtInterfaceFramework" + || directory == u"QtOpcUa" || directory == u"Qt3D" || directory == u"Qt3D" + || directory == u"Scene2D" || directory == u"Scene3D" || directory == u"QtWayland" + || directory == u"Qt5Compat"; return skip; } @@ -359,22 +370,17 @@ bool skipPath(const std::filesystem::path &path) void qtQmldirPaths([[maybe_unused]] ::ProjectExplorer::Target *target, [[maybe_unused]] QStringList &qmldirPaths) { -#ifdef QDS_HAS_QMLDOM - const QString installDirectory = qmlPath(target).toString(); +#ifdef QDS_HAS_QMLPRIVATE + if (useProjectStorage()) { + const QString installDirectory = qmlPath(target).toString(); + QDirIterator dirIterator{installDirectory, QDir::Dirs, QDirIterator::Subdirectories}; - const std::filesystem::path installDirectoryPath{installDirectory.toStdString()}; + while (dirIterator.hasNext()) { + auto directoryPath = dirIterator.next(); - auto current = std::filesystem::recursive_directory_iterator{installDirectoryPath}; - auto end = std::filesystem::end(current); - for (; current != end; ++current) { - const auto &entry = *current; - auto path = entry.path(); - if (current.depth() < 3 && !current->is_regular_file() && skipPath(path)) { - current.disable_recursion_pending(); - continue; - } - if (path.filename() == "qmldir") { - qmldirPaths.push_back(QString::fromStdU16String(path.generic_u16string())); + QString qmldirPath = directoryPath + "/qmldir"; + if (!skipPath(directoryPath) && QFileInfo::exists(qmldirPath)) + qmldirPaths.push_back(directoryPath); } } #endif diff --git a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo index 45e01621280..f1aeaa9ebbd 100644 --- a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo +++ b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo @@ -571,6 +571,7 @@ MetaInfo { requiredImport: "QtQuick3D" QmlSource { source: ":/qtquickplugin/source/component3d.qml" } + toolTip: qsTr("Allows you to define 3D components inline, within a QML document.") } } diff --git a/src/plugins/qmldesigner/settingspage.cpp b/src/plugins/qmldesigner/settingspage.cpp index 873811ee28b..8c3222d72c3 100644 --- a/src/plugins/qmldesigner/settingspage.cpp +++ b/src/plugins/qmldesigner/settingspage.cpp @@ -41,7 +41,7 @@ namespace Internal { static QStringList puppetModes() { - static QStringList puppetModeList{"", "all", "editormode", "rendermode", "previewmode"}; + static QStringList puppetModeList{"", "all", "editormode", "rendermode", "previewmode", "bakelightsmode"}; return puppetModeList; } diff --git a/src/plugins/qmldesigner/shortcutmanager.cpp b/src/plugins/qmldesigner/shortcutmanager.cpp index efb3bf49eee..87a873339df 100644 --- a/src/plugins/qmldesigner/shortcutmanager.cpp +++ b/src/plugins/qmldesigner/shortcutmanager.cpp @@ -14,11 +14,12 @@ #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/command.h> -#include <coreplugin/icore.h> -#include <coreplugin/idocument.h> #include <coreplugin/editormanager/documentmodel.h> #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/coreconstants.h> +#include <coreplugin/icore.h> +#include <coreplugin/idocument.h> +#include <coreplugin/modemanager.h> #include <qmljseditor/qmljseditorconstants.h> #include <qmlprojectmanager/qmlprojectmanagerconstants.h> @@ -39,21 +40,24 @@ #include <QApplication> #include <QClipboard> +#include <QMainWindow> +#include <QStandardPaths> namespace QmlDesigner { ShortCutManager::ShortCutManager() - : QObject(), - m_exportAsImageAction(tr("Export as &Image...")), - m_undoAction(tr("&Undo")), - m_redoAction(tr("&Redo")), - m_deleteAction(tr("Delete")), - m_cutAction(tr("Cu&t")), - m_copyAction(tr("&Copy")), - m_pasteAction(tr("&Paste")), - m_duplicateAction(tr("&Duplicate")), - m_selectAllAction(tr("Select &All")), - m_escapeAction(this) + : QObject() + , m_exportAsImageAction(tr("Export as &Image...")) + , m_takeScreenshotAction(tr("Take Screenshot")) + , m_undoAction(tr("&Undo")) + , m_redoAction(tr("&Redo")) + , m_deleteAction(tr("Delete")) + , m_cutAction(tr("Cu&t")) + , m_copyAction(tr("&Copy")) + , m_pasteAction(tr("&Paste")) + , m_duplicateAction(tr("&Duplicate")) + , m_selectAllAction(tr("Select &All")) + , m_escapeAction(this) { } @@ -106,12 +110,33 @@ void ShortCutManager::registerActions(const Core::Context &qmlDesignerMainContex QmlDesignerPlugin::instance()->viewManager().exportAsImage(); }); + // Edit Global Annotations QAction *action = new QAction(tr("Edit Global Annotations..."), this); command = Core::ActionManager::registerAction(action, "Edit.Annotations", qmlDesignerMainContext); Core::ActionManager::actionContainer(Core::Constants::M_EDIT) ->addAction(command, Core::Constants::G_EDIT_OTHER); - connect(action, &QAction::triggered, this, [] { ToolBarBackend::launchGlobalAnnotations(); }); + connect(Core::ModeManager::instance(), &Core::ModeManager::currentModeChanged, this, [action] { + action->setEnabled(Core::ModeManager::currentModeId() == Core::Constants::MODE_DESIGN); + }); + action->setEnabled(false); + + command = Core::ActionManager::registerAction(&m_takeScreenshotAction, + QmlDesigner::Constants::TAKE_SCREENSHOT); + connect(&m_takeScreenshotAction, &QAction::triggered, [] { + const auto folder = Utils::FilePath::fromString( + QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)) + .pathAppended("QtDesignStudio/screenshots/"); + folder.createDir(); + + const auto file = folder.pathAppended(QDateTime::currentDateTime().toString("dddd-hh-mm-ss") + + ".png"); + + QPixmap pixmap = Core::ICore::mainWindow()->grab(); + + const bool b = pixmap.save(file.toString(), "PNG"); + qWarning() << "screenshot" << file << b << pixmap; + }); Core::ActionContainer *exportMenu = Core::ActionManager::actionContainer( QmlProjectManager::Constants::EXPORT_MENU); diff --git a/src/plugins/qmldesigner/shortcutmanager.h b/src/plugins/qmldesigner/shortcutmanager.h index 9b4762748ef..b97fd834ee7 100644 --- a/src/plugins/qmldesigner/shortcutmanager.h +++ b/src/plugins/qmldesigner/shortcutmanager.h @@ -52,6 +52,7 @@ private: QAction m_saveAction; QAction m_saveAsAction; QAction m_exportAsImageAction; + QAction m_takeScreenshotAction; QAction m_closeCurrentEditorAction; QAction m_closeAllEditorsAction; QAction m_closeOtherEditorsAction; diff --git a/src/plugins/qmldesigner/studioplugin/studioplugin.metainfo b/src/plugins/qmldesigner/studioplugin/studioplugin.metainfo index 94f14c296dd..2998ea2f67b 100644 --- a/src/plugins/qmldesigner/studioplugin/studioplugin.metainfo +++ b/src/plugins/qmldesigner/studioplugin/studioplugin.metainfo @@ -34,7 +34,16 @@ MetaInfo { "QtQuick.Templates", "QtQuick.Shapes", "QtQuick.Studio.EventSystem", - "QtQuick.Studio.EventSimulator" + "QtQuick.Studio.EventSimulator", + "QtQuick.Pdf", + "QmlTime", + "Qt.test.controls", + "QtOpcUa", + "QtVncServer", + "QtTextToSpeech", + "QtQuick3D MaterialEditor", + "QtDataVisualization", + "QtQuick3D.ParticleEffects" ] showTagsForImports: [ diff --git a/src/plugins/qmldesigner/utils/filedownloader.cpp b/src/plugins/qmldesigner/utils/filedownloader.cpp index b5c735d87a8..7825be5081b 100644 --- a/src/plugins/qmldesigner/utils/filedownloader.cpp +++ b/src/plugins/qmldesigner/utils/filedownloader.cpp @@ -253,23 +253,23 @@ void FileDownloader::doProbeUrl() m_available = true; emit availableChanged(); + + reply->deleteLater(); }); QNetworkReply::connect(reply, &QNetworkReply::errorOccurred, this, - [this](QNetworkReply::NetworkError) { - QQmlData *data = QQmlData::get(this, false); - if (!data) { - qDebug() << Q_FUNC_INFO << "FileDownloader is nullptr."; - return; - } + [this](QNetworkReply::NetworkError code) { if (QQmlData::wasDeleted(this)) { qDebug() << Q_FUNC_INFO << "FileDownloader was deleted."; return; } + qDebug() << Q_FUNC_INFO << "Network error:" << code + << qobject_cast<QNetworkReply *>(sender())->errorString(); + m_available = false; emit availableChanged(); }); diff --git a/src/plugins/qmldesigner/utils/fileextractor.cpp b/src/plugins/qmldesigner/utils/fileextractor.cpp index c6aa83fb7ae..7c32381b69c 100644 --- a/src/plugins/qmldesigner/utils/fileextractor.cpp +++ b/src/plugins/qmldesigner/utils/fileextractor.cpp @@ -51,7 +51,11 @@ FileExtractor::FileExtractor(QObject *parent) // We can not get the uncompressed size of the archive yet, that is why we use an // approximation. We assume a 50% compression rate. - int progress = std::min(100ll, currentSize * 100 / m_compressedSize * 2); + + int progress = 0; + if (m_compressedSize > 0) + progress = std::min(100ll, currentSize * 100 / m_compressedSize * 2); + if (progress >= 0) { m_progress = progress; emit progressChanged(); @@ -212,6 +216,8 @@ void FileExtractor::extract() m_timer.start(); m_bytesBefore = QStorageInfo(m_targetPath.toFileInfo().dir()).bytesAvailable(); m_compressedSize = QFileInfo(m_sourceFile.toString()).size(); + if (m_compressedSize <= 0) + qWarning() << "Compressed size for file '" << m_sourceFile << "' is zero or invalid: " << m_compressedSize; QObject::connect(archive, &Utils::Archive::outputReceived, this, [this](const QString &output) { m_detailedText += output; diff --git a/src/plugins/qmldesigner/utils/multifiledownloader.cpp b/src/plugins/qmldesigner/utils/multifiledownloader.cpp index c36476f05d6..6a8ac9761e9 100644 --- a/src/plugins/qmldesigner/utils/multifiledownloader.cpp +++ b/src/plugins/qmldesigner/utils/multifiledownloader.cpp @@ -23,7 +23,10 @@ void MultiFileDownloader::setDownloader(FileDownloader *downloader) }); QObject::connect(m_downloader, &FileDownloader::progressChanged, this, [this]() { - m_progress = (m_nextFile + m_downloader->progress()) / m_files.count(); + double curProgress = m_downloader->progress() / 100.0; + double totalProgress = (m_nextFile + curProgress) / m_files.count(); + m_progress = totalProgress * 100; + emit progressChanged(); }); QObject::connect(m_downloader, &FileDownloader::downloadFailed, this, [this]() { @@ -48,6 +51,7 @@ FileDownloader *MultiFileDownloader::downloader() void MultiFileDownloader::start() { + m_canceled = false; emit downloadStarting(); } |