diff --git a/.github/workflows/build_cmake.yml b/.github/workflows/build_cmake.yml index 5d4585d33ad..25e087fb25f 100644 --- a/.github/workflows/build_cmake.yml +++ b/.github/workflows/build_cmake.yml @@ -25,8 +25,8 @@ jobs: matrix: config: - { - name: "Windows Latest MSVC", artifact: "Windows-MSVC", - os: windows-latest, + name: "Windows MSVC 2019", artifact: "Windows-MSVC", + os: windows-2019, cc: "cl", cxx: "cl", environment_script: "C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/VC/Auxiliary/Build/vcvars64.bat", is_msvc: true diff --git a/CMakeLists.txt b/CMakeLists.txt index c6f603c7981..187fccd493c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,6 +66,9 @@ if (Qt5_VERSION VERSION_LESS 6.0.0) if (MSVC AND MSVC_VERSION GREATER_EQUAL 1910) add_compile_options(/permissive- /Zc:__cplusplus) endif() + if (CMAKE_CXX_COMPILER_ID STREQUAL GNU) + add_compile_options(-Wno-missing-field-initializers) +endif() else() # Common intermediate directory for QML modules which are defined via qt_add_qml_module() set(QT_QML_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/qml_modules") diff --git a/doc/qtcreator/src/cmake/creator-projects-cmake-building.qdoc b/doc/qtcreator/src/cmake/creator-projects-cmake-building.qdoc index 2932e1c9d81..45e55f42987 100644 --- a/doc/qtcreator/src/cmake/creator-projects-cmake-building.qdoc +++ b/doc/qtcreator/src/cmake/creator-projects-cmake-building.qdoc @@ -39,6 +39,17 @@ \image qtcreator-cmake-build-settings-initial.png "CMake build settings" + \uicontrol {Initial Configuration} lists the variables that are used to + configure the CMake project for the first time. The default values that + are inherited from the kit's CMake configuration are displayed in italic. + The initial configuration list of variables is saved in the project's source + directory as the \e CMakeLists.txt.user file. + + \uicontrol {Current Configuration} lists the CMake variables in the + \c cmake-file-api JSON export in the \c {.cmake/api/v1/reply} directory. + The variables that are inherited from the initial configuration are + displayed in italic. Mismatched values are displayed in red. + You can view and edit the actual values of the variables that are passed to CMake. Variable names are listed in the \uicontrol Key column and their current values in the \uicontrol Value column. For more information about diff --git a/doc/qtcreator/src/editors/creator-code-syntax.qdoc b/doc/qtcreator/src/editors/creator-code-syntax.qdoc index 65e41022e37..f54337dd903 100644 --- a/doc/qtcreator/src/editors/creator-code-syntax.qdoc +++ b/doc/qtcreator/src/editors/creator-code-syntax.qdoc @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2019 The Qt Company Ltd. +** Copyright (C) 2022 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Creator documentation. @@ -291,7 +291,7 @@ \row \li M106 \li Warning - \li \c Name is already a var + \li \c Name already is a var \li \row @@ -416,6 +416,12 @@ \li A state cannot have the specified child item \li + \row + \li M129 + \li Error + \li Type cannot be instantiated recursively + \li + \row \li M201 \li Hint @@ -430,6 +436,7 @@ \li Use only one statement per line \li + \if defined(qtdesignstudio) \row \li M203 \li Warning @@ -463,60 +470,70 @@ \row \li M208 \li Error - \li This id might be ambiguous and is not supported in \QDS. + \li This id might be ambiguous and is not supported in \QDS \li \row \li M209 \li Error \li This type (type name) is not supported as a root element in - \QDS. + \QDS \li + \endif \row \li M220 \li Error \li This type (type name) is not supported as a root element of a - UI file (.ui.qml). - \li + UI file (.ui.qml) + \li For more information about supported QML types, see \l{UI Files}. \row \li M221 \li Error - \li This type (type name) is not supported in a UI file (.ui.qml). - \li + \li This type (type name) is not supported in a UI file (.ui.qml) + \li For more information about supported QML types, see \l{UI Files}. \row \li M222 \li Error - \li Functions are not supported in a UI file (.ui.qml). + \li Functions are not supported in a UI file (.ui.qml) \li For a list of supported JavaScript functions, see \l{Supported Methods}. \row \li M223 \li Error - \li Java Script blocks are not supported in a UI file (.ui.qml). - \li + \li JavaScript blocks are not supported in a UI file (.ui.qml) + \li For more information about supported features, see \l{UI Files}. \row \li M224 \li Error - \li Behavior type is not supported in a UI file (.ui.qml). - \li + \li Behavior type is not supported in a UI file (.ui.qml) + \li For more information about supported QML types, see \l{UI Files}. \row \li M225 \li Error - \li States are only supported in the root item in a UI file (.ui.qml). - \li + \li States are only supported in the root item in a UI file (.ui.qml) + \li For more information about supported features, see \l{UI Files}. \row \li M226 \li Error \li Referencing the parent of the root item is not supported in a - UI file (.ui.qml). - \li + UI file (.ui.qml) + \li For more information about supported features, see \l{UI Files}. + + \row + \li M227 + \li Error + \li Do not mix translation functions in a UI file (.ui.qml) + \li Even though several different translation functions, such as + \c qsTr and \c qsTrId are supported, you should pick one and + use it consistently within a UI file. For more information, + see \l{Qt QML Methods}. \row \li M300 @@ -674,11 +691,30 @@ and most likely invalid \li + \row + \li M325 + \li Warning + \li Logical value does not depend on actual values + \li + + \row + \li M326 + \li Error + \li Components are only allowed to have a single child element + \li For more information, see \l [QML] {Component}. + + \row + \li M327 + \li Warning + \li Components require a child element + \li For more information, see \l [QML] {Component}. + \row \li M400 \li Warning \li Duplicate import - \li + \li An import statement has been added twice. For more information, + see \l{Import Statements}. \endtable diff --git a/doc/qtcreator/src/howto/creator-only/creator-logging-viewer.qdoc b/doc/qtcreator/src/howto/creator-only/creator-logging-viewer.qdoc index 963449be581..150354c3e37 100644 --- a/doc/qtcreator/src/howto/creator-only/creator-logging-viewer.qdoc +++ b/doc/qtcreator/src/howto/creator-only/creator-logging-viewer.qdoc @@ -83,7 +83,7 @@ By default, logging categories and messages coming directly from Qt are disabled. To display them, select \inlineimage icons/qtlogo-16.png - (\uicontrol {Toggle logging of Qt internal loggings.}) + (\uicontrol {Toggle Qt Internal Logging}). New messages automatically scroll the message display to the bottom. To stop automatic scrolling, toggle \inlineimage icons/arrowdown.png diff --git a/doc/qtcreator/src/qtquick/qtquick-ui-forms.qdoc b/doc/qtcreator/src/qtquick/qtquick-ui-forms.qdoc index fb76d5250cb..a21991f7ba8 100644 --- a/doc/qtcreator/src/qtquick/qtquick-ui-forms.qdoc +++ b/doc/qtcreator/src/qtquick/qtquick-ui-forms.qdoc @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2022 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Creator documentation. @@ -52,6 +52,7 @@ be edited in \QDS only. \endif + The following features are not supported in .ui.qml files: \list @@ -61,6 +62,7 @@ \li States in other components than the root component \li Root components that are not derived from \l QQuickItem or \l [QML]{Item} + \li Referencing the parent of the root component \endlist The following components are not supported: @@ -69,11 +71,9 @@ \li Behavior \li Binding \li Canvas - \li Component \li Shader Effect \li Timer \li Transform - \li Transition \endlist \section1 Supported Methods @@ -165,6 +165,8 @@ \li \l{Qt::}{qsTrNoOp()} \endlist + \note Do not mix translation methods in a UI file. + For more information about using the methods, see \l{https://doc.qt.io/qt/qml-qtqml-qt.html}{Qt QML Methods}. diff --git a/doc/qtdesignstudio/examples/doc/rainSnowParticles.qdoc b/doc/qtdesignstudio/examples/doc/rainSnowParticles.qdoc index 49c42a84603..689f89d903f 100644 --- a/doc/qtdesignstudio/examples/doc/rainSnowParticles.qdoc +++ b/doc/qtdesignstudio/examples/doc/rainSnowParticles.qdoc @@ -74,12 +74,8 @@ In \uicontrol Navigator, select \e particleSystem and in \uicontrol Properties, set \uicontrol Translation > \uicontrol Y to 193. \li Set the \e rain-drop-white-square.png as texture for the particles. - From \uicontrol Library > \uicontrol Components, drag a \uicontrol Texture - to \e spriteParticle. - \li In \uicontrol Navigator, select \uicontrol texture1 and in \uicontrol - Properties, set \uicontrol Source to \e rain-drop-white-square.png. - \li In \uicontrol Navigator, select \uicontrol spriteParticle and in the - \uicontrol Properties, set \uicontrol Sprite to texture. + From \uicontrol Library > \uicontrol Assets, drag + \e {rain-drop-white-square.png} to \e spriteParticle. \li Adjust the apperance and behavior of the sprite further. In \uicontrol Properties, set: \list diff --git a/doc/qtdesignstudio/images/studio-3d-particles-fire-emitter1.png b/doc/qtdesignstudio/images/studio-3d-particles-fire-emitter1.png index ee56c599c7a..6972f27021d 100644 Binary files a/doc/qtdesignstudio/images/studio-3d-particles-fire-emitter1.png and b/doc/qtdesignstudio/images/studio-3d-particles-fire-emitter1.png differ diff --git a/doc/qtdesignstudio/images/studio-3d-particles-fire-properties-rotation.png b/doc/qtdesignstudio/images/studio-3d-particles-fire-properties-rotation.png new file mode 100644 index 00000000000..36709e7520a Binary files /dev/null and b/doc/qtdesignstudio/images/studio-3d-particles-fire-properties-rotation.png differ diff --git a/doc/qtdesignstudio/src/qtdesignstudio-platforms.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-platforms.qdoc index bbd1d0ddd86..9935bcfbc4e 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-platforms.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-platforms.qdoc @@ -38,7 +38,7 @@ \QDS is available in binary packages for the following operating systems: \list - \li \macOS 10.15 + \li \macOS 11.0 \li Linux: \list \li CentOS 8.1 diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-particles.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-particles.qdoc index 42d3843f0d8..9d8c944b6e2 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-particles.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-particles.qdoc @@ -92,22 +92,9 @@ \li Drag-and-drop an instance of the \uicontrol {Particle System} component from \uicontrol Library to a scene component instance in \l Navigator. - \li Drag-and-drop an instance of the \uicontrol Texture component - from \uicontrol Library > \uicontrol Components > - \uicontrol {Qt Quick 3D} to the sprite particle instance - in \uicontrol Navigator. \li Drag-and-drop the sprite image from \uicontrol Library > - \uicontrol Assets to the texture instance in \uicontrol Navigator. - \li Select the sprite particle instance in \uicontrol Navigator to - display its properties in \l Properties. - \li In \uicontrol Sprite, select the texture instance. - \li Select the emitter instance in \uicontrol Navigator to - display its properties in \uicontrol Properties. - \li In \uicontrol Particle, select the particle instance to emit. - \li Select the vector 3D instance in \uicontrol Navigator to - display its properties in \uicontrol Properties. - \li In \uicontrol Direction, set the emitted particle velocity - towards the target vector. + \uicontrol Assets to the sprite particle instance + in \uicontrol Navigator. \endlist Add instances of other components according to your use case. The following @@ -122,6 +109,9 @@ their property values, such as particle source images and their color, life span, and fading effects, to simulate fire. + You can download the completed project from + \l {https://git.qt.io/public-demos/qtdesign-studio/-/tree/master/tutorial%20projects/FireParticles}{here}. + We will need the following \l{Assets}{assets}: \list @@ -199,12 +189,16 @@ \uicontrol {Particle end scale} to \e 5.00 for the flame particles and to \e 6.00 for the smoke particles. - We can now modify the appearance of the particles by setting their color in + We can now modify the appearance of the particles by selecting the + \e spriteParticle in \uicontrol Navigator and then setting their color in \uicontrol Properties > \uicontrol Particle. We select transparent yellow, orange, and transparent gray in \uicontrol Color and set values for \uicontrol {Color variation} to use slightly different colors for the individual particles. + We can also try changing the \uicontrol{Blend Mode} for the particle to get + a more realistic fire effect. + \image studio-3d-particles-fire-properties-particle.png "Particle properties" We set \uicontrol {Fade in effect} and \uicontrol {Fade out effect} values @@ -220,6 +214,11 @@ \image studio-3d-particles-fire-properties-sprite-particle.png "Sprite Particle properties" + In \uicontrol{Particle Rotation}, we also need to set \uicontrol Rotation + and \uicontrol{Velocity Variation} for all emitters to 0. + + \image studio-3d-particles-fire-properties-rotation.png "Emitter rotation properties" + Finally, we will specify the direction in which the particles move by modifying the property values of the \uicontrol {Vector Direction} component instances in \uicontrol Properties > \uicontrol {Particle Vector Direction}. diff --git a/qbs/imports/QtcProduct.qbs b/qbs/imports/QtcProduct.qbs index 85b334daf0d..ee84b46aa3e 100644 --- a/qbs/imports/QtcProduct.qbs +++ b/qbs/imports/QtcProduct.qbs @@ -50,7 +50,7 @@ Product { flags.push("-Wno-deprecated-copy", "-Wno-constant-logical-operand"); } if (!qbs.toolchain.contains("clang")) { - flags.push("-Wno-noexcept-type"); + flags.push("-Wno-missing-field-initializers", "-Wno-noexcept-type"); if (Utilities.versionCompare(cpp.compilerVersion, "9") >= 0) flags.push("-Wno-deprecated-copy", "-Wno-init-list-lifetime"); } diff --git a/qbs/modules/qtc_gtest_gmock/qtc_gtest_gmock.qbs b/qbs/modules/qtc_gtest_gmock/qtc_gtest_gmock.qbs new file mode 100644 index 00000000000..5229db14403 --- /dev/null +++ b/qbs/modules/qtc_gtest_gmock/qtc_gtest_gmock.qbs @@ -0,0 +1,65 @@ +import qbs +import qbs.File +import qbs.FileInfo + +Module { + property bool preferExternalLibs: true + + Depends { name: "pkgconfig"; condition: preferExternalLibs; required: false } + Depends { name: "gtest"; condition: preferExternalLibs; required: false } + Depends { name: "gmock"; condition: preferExternalLibs; required: false } + Depends { name: "cpp" } + + Probe { + id: gtestProbe + + property string repoDir + property string gtestDir + property string gmockDir + property bool hasRepo + + configure: { + repoDir = FileInfo.cleanPath(path + "/../../../tests/unit/unittest/3rdparty/googletest"); + gtestDir = FileInfo.joinPaths(repoDir, "googletest"); + gmockDir = FileInfo.joinPaths(repoDir, "googlemock"); + hasRepo = File.exists(gtestDir); + found = hasRepo; + } + } + + property bool hasRepo: gtestProbe.hasRepo + property bool externalLibsPresent: preferExternalLibs && gtest.present && gmock.present + property bool useRepo: !externalLibsPresent && hasRepo + property string gtestDir: gtestProbe.gtestDir + property string gmockDir: gtestProbe.gmockDir + + Group { + name: "Files from repository" + condition: qtc_gtest_gmock.useRepo + cpp.includePaths: [ + qtc_gtest_gmock.gtestDir, + qtc_gtest_gmock.gmockDir, + FileInfo.joinPaths(qtc_gtest_gmock.gtestDir, "include"), + FileInfo.joinPaths(qtc_gtest_gmock.gmockDir, "include"), + ] + files: [ + FileInfo.joinPaths(qtc_gtest_gmock.gtestDir, "src", "gtest-all.cc"), + FileInfo.joinPaths(qtc_gtest_gmock.gmockDir, "src", "gmock-all.cc"), + ] + } + + Properties { + condition: qtc_gtest_gmock.useRepo + cpp.includePaths: [ + FileInfo.joinPaths(qtc_gtest_gmock.gtestDir, "include"), + FileInfo.joinPaths(qtc_gtest_gmock.gmockDir, "include"), + ] + } + + validate: { + if (!qtc_gtest_gmock.externalLibsPresent && !gtestProbe.found) { + console.warn("No GTest found."); + throw new Error(); + } + } +} diff --git a/share/qtcreator/qml-type-descriptions/qbs-bundle.json b/share/qtcreator/qml-type-descriptions/qbs-bundle.json index cf230cdefee..7f0c6886911 100644 --- a/share/qtcreator/qml-type-descriptions/qbs-bundle.json +++ b/share/qtcreator/qml-type-descriptions/qbs-bundle.json @@ -19,6 +19,7 @@ "qbs.FileInfo", "qbs.ModUtils", "qbs.PathTools", + "qbs.PkgConfig", "qbs.Probes", "qbs.Process", "qbs.PropertyList", diff --git a/share/qtcreator/qml-type-descriptions/qbs.qmltypes b/share/qtcreator/qml-type-descriptions/qbs.qmltypes index bc4244b63fc..941697b02d2 100644 --- a/share/qtcreator/qml-type-descriptions/qbs.qmltypes +++ b/share/qtcreator/qml-type-descriptions/qbs.qmltypes @@ -133,6 +133,7 @@ Module { Property { name: "multiplexedType"; type: "string"; isList: true } Property { name: "name"; type: "string" } Property { name: "profiles"; type: "string"; isList: true } + Property { name: "qbsModuleProviders"; type: "string"; isList: true } Property { name: "qbsSearchPaths"; type: "string"; isList: true } Property { name: "targetName"; type: "string" } Property { name: "type"; type: "string"; isList: true } @@ -155,6 +156,7 @@ Module { Property { name: "minimumQbsVersion"; type: "string" } Property { name: "name"; type: "string" } Property { name: "profile"; type: "string" } + Property { name: "qbsModuleProviders"; type: "string"; isList: true } Property { name: "qbsSearchPaths"; type: "string"; isList: true } Property { name: "references"; type: "string"; isList: true } Property { name: "sourceDirectory"; type: "string" } diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.cpp index c97de4636aa..908e06ea50a 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.cpp @@ -398,6 +398,8 @@ QImage Qt5NodeInstanceServer::grabItem(QQuickItem *item) QQuickItemPrivate *pItem = QQuickItemPrivate::get(item); + const bool rootIs3dNode = rootNodeInstance().isSubclassOf("QQuick3DNode"); + const bool renderEffects = qEnvironmentVariableIsSet("QMLPUPPET_RENDER_EFFECTS"); if (renderEffects) { @@ -426,6 +428,9 @@ QImage Qt5NodeInstanceServer::grabItem(QQuickItem *item) QRectF renderBoundingRect; if (instance.isValid()) renderBoundingRect = instance.boundingRect(); + + else if (rootIs3dNode) + renderBoundingRect = item->boundingRect(); else renderBoundingRect = ServerNodeInstance::effectAdjustedBoundingRect(item); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp index 6fe0807d01a..ae333343726 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp @@ -133,6 +133,14 @@ void Qt5RenderNodeInstanceServer::collectItemChangesAndSendChangeCommands() nodeInstanceClient()->synchronizeWithClientProcess(); } + if (rootNodeInstance().isSubclassOf("QQuick3DNode") && rootNodeInstance().contentItem() + && DesignerSupport::isDirty(rootNodeInstance().contentItem(), + DesignerSupport::ContentUpdateMask) + && nodeInstanceClient()->bytesToWrite() < 10000) { + Internal::QuickItemNodeInstance::updateDirtyNode(rootNodeInstance().contentItem()); + nodeInstanceClient()->pixmapChanged(createPixmapChangedCommand({rootNodeInstance()})); + } + inFunction = false; } } diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dnodeinstance.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dnodeinstance.cpp index ec0beb521df..0c70ccca8f0 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dnodeinstance.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dnodeinstance.cpp @@ -55,6 +55,8 @@ namespace QmlDesigner { namespace Internal { +const QRectF preview3dBoundingRect(0, 0, 640, 480); + Quick3DNodeInstance::Quick3DNodeInstance(QObject *node) : ObjectNodeInstance(node) { @@ -97,7 +99,7 @@ void Quick3DNodeInstance::initialize(const ObjectNodeInstance::Pointer &objectNo #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) // In case this is the scene root, we need to create a dummy View3D for the scene // in preview puppets - if (instanceId() == 0 && nodeInstanceServer()->isPreviewServer()) { + if (instanceId() == 0 && (!nodeInstanceServer()->isInformationServer())) { auto helper = new QmlDesigner::Internal::GeneralHelper(); engine()->rootContext()->setContextProperty("_generalHelper", helper); @@ -111,8 +113,8 @@ void Quick3DNodeInstance::initialize(const ObjectNodeInstance::Pointer &objectNo nodeInstanceServer()->setRootItem(m_dummyRootView); } -#endif -#endif +#endif // QT_VERSION +#endif // QUICK3D_MODULE ObjectNodeInstance::initialize(objectNodeInstance, flags); } @@ -122,7 +124,7 @@ QImage Quick3DNodeInstance::renderImage() const if (!isRootNodeInstance() || !m_dummyRootView) return {}; - QSize size(640, 480); + QSize size = preview3dBoundingRect.size().toSize(); nodeInstanceServer()->quickWindow()->resize(size); m_dummyRootView->setSize(size); @@ -190,13 +192,37 @@ bool Quick3DNodeInstance::isRenderable() const return m_dummyRootView; } +bool Quick3DNodeInstance::hasContent() const +{ + return true; +} + QRectF Quick3DNodeInstance::boundingRect() const { + //The information server has no m_dummyRootView therefore we use the hardcoded value + if (nodeInstanceServer()->isInformationServer()) + return preview3dBoundingRect; + if (m_dummyRootView) return m_dummyRootView->boundingRect(); return ObjectNodeInstance::boundingRect(); } +QRectF Quick3DNodeInstance::contentItemBoundingBox() const +{ + return boundingRect(); +} + +QPointF Quick3DNodeInstance::position() const +{ + return QPointF(0, 0); +} + +QSizeF Quick3DNodeInstance::size() const +{ + return boundingRect().size(); +} + QList Quick3DNodeInstance::stateInstances() const { QList instanceList; @@ -212,6 +238,11 @@ QList Quick3DNodeInstance::stateInstances() const return instanceList; } +QQuickItem *Quick3DNodeInstance::contentItem() const +{ + return m_dummyRootView; +} + Qt5NodeInstanceServer *Quick3DNodeInstance::qt5NodeInstanceServer() const { return qobject_cast(nodeInstanceServer()); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dnodeinstance.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dnodeinstance.h index 322762af26a..b3b4f789724 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dnodeinstance.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dnodeinstance.h @@ -51,10 +51,16 @@ public: QImage renderPreviewImage(const QSize &previewImageSize) const override; bool isRenderable() const override; + bool hasContent() const override; QRectF boundingRect() const override; + QRectF contentItemBoundingBox() const override; + QPointF position() const override; + QSizeF size() const override; QList stateInstances() const override; + QQuickItem *contentItem() const override; + protected: explicit Quick3DNodeInstance(QObject *node); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.cpp index 9803f6d79e6..f6c7f94bcaa 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.cpp @@ -162,6 +162,11 @@ bool ServerNodeInstance::isComponentWrap() const return m_nodeInstance->isComponentWrap(); } +QQuickItem *ServerNodeInstance::contentItem() const +{ + return m_nodeInstance->contentItem(); +} + void ServerNodeInstance::updateDirtyNodeRecursive() { m_nodeInstance->updateAllDirtyNodesRecursive(); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.h index 0e5ca3547db..bc79cb4f689 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.h @@ -188,6 +188,8 @@ public: bool isComponentWrap() const; + QQuickItem *contentItem() const; + private: // functions ServerNodeInstance(const QSharedPointer &abstractInstance); diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml index 872afff1ac8..f254a325508 100644 --- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml @@ -98,6 +98,8 @@ Item { StudioControls.Menu { id: contextMenu + closePolicy: Popup.CloseOnPressOutside | Popup.CloseOnEscape + StudioControls.MenuItem { text: qsTr("Expand All") enabled: allExpandedState !== 1 @@ -167,7 +169,7 @@ Item { Dialog { id: renameFolderDialog - title: qsTr("Rename folder") + title: qsTr("Rename Folder") anchors.centerIn: parent closePolicy: Popup.CloseOnEscape implicitWidth: 280 @@ -192,15 +194,15 @@ Item { } Text { - text: qsTr("Folder Name cannot be empty.") + text: qsTr("Folder name cannot be empty.") color: "#ff0000" visible: folderRename.text === "" && !renameFolderDialog.renameError } Text { - text: qsTr("Could not rename directory. Make sure no folder with the same name exists.") + text: qsTr("Could not rename folder. Make sure no folder with the same name exists.") wrapMode: Text.WordWrap - width: renameFolderDialog.width + width: renameFolderDialog.width - 12 color: "#ff0000" visible: renameFolderDialog.renameError } @@ -259,7 +261,7 @@ Item { Dialog { id: newFolderDialog - title: qsTr("Create new folder") + title: qsTr("Create New Folder") anchors.centerIn: parent closePolicy: Popup.CloseOnEscape modal: true @@ -269,7 +271,7 @@ Item { Row { Text { - text: qsTr("Folder Name: ") + text: qsTr("Folder name: ") anchors.verticalCenter: parent.verticalCenter color: StudioTheme.Values.themeTextColor } @@ -287,7 +289,7 @@ Item { } Text { - text: qsTr("Folder Name cannot be empty.") + text: qsTr("Folder name cannot be empty.") color: "#ff0000" anchors.right: parent.right visible: folderName.text === "" @@ -329,7 +331,7 @@ Item { Dialog { id: confirmDeleteFolderDialog - title: qsTr("Folder not empty") + title: qsTr("Folder Not Empty") anchors.centerIn: parent closePolicy: Popup.CloseOnEscape implicitWidth: 300 @@ -342,7 +344,7 @@ Item { Text { id: folderNotEmpty - text: qsTr("Folder '%1' is not empty. Are you sure you want to delete it?") + text: qsTr("Folder \"%1\" is not empty. Delete it anyway?") .arg(contextDir ? contextDir.dirName : "") color: StudioTheme.Values.themeTextColor wrapMode: Text.WordWrap diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtMultimedia/MediaPlayerSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtMultimedia/MediaPlayerSection.qml index 2e58189a4dd..e71d67f6295 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtMultimedia/MediaPlayerSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtMultimedia/MediaPlayerSection.qml @@ -59,8 +59,8 @@ Section { PropertyLabel { visible: root.showAudioOutput - text: qsTr("Audio Output") - tooltip: qsTr("Holds the target audio output.") + text: qsTr("Audio output") + tooltip: qsTr("Target audio output.") } SecondColumnLayout { @@ -80,8 +80,8 @@ Section { PropertyLabel { visible: root.showVideoOutput - text: qsTr("Video Output") - tooltip: qsTr("Holds the target video output.") + text: qsTr("Video output") + tooltip: qsTr("Target video output.") } SecondColumnLayout { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/AbstractButtonSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/AbstractButtonSection.qml index f5fd2282f01..54cf89cf255 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/AbstractButtonSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/AbstractButtonSection.qml @@ -41,7 +41,7 @@ import QtQuickDesignerTheme 1.0 import StudioTheme 1.0 as StudioTheme Section { - caption: qsTr("Button content") + caption: qsTr("Button Content") anchors.left: parent.left anchors.right: parent.right @@ -49,7 +49,7 @@ Section { SectionLayout { PropertyLabel { text: qsTr("Text") - tooltip: qsTr("The text displayed on the button.") + tooltip: qsTr("Text displayed on the button.") } SecondColumnLayout { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/LoaderSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/LoaderSpecifics.qml index 2417c545876..b4447d2adab 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/LoaderSpecifics.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/LoaderSpecifics.qml @@ -41,7 +41,7 @@ Column { SectionLayout { PropertyLabel { text: qsTr("Active") - tooltip: qsTr("This property is true if the Loader is currently active.") + tooltip: qsTr("Whether the loader is currently active.") } SecondColumnLayout { @@ -57,7 +57,7 @@ Column { PropertyLabel { text: qsTr("Source") - tooltip: qsTr("This property holds the URL of the QML component to instantiate.") + tooltip: qsTr("URL of the component to instantiate.") } SecondColumnLayout { @@ -70,8 +70,8 @@ Column { } PropertyLabel { - text: qsTr("Source Component") - tooltip: qsTr("This property holds the component to instantiate.") + text: qsTr("Source component") + tooltip: qsTr("Component to instantiate.") } SecondColumnLayout { @@ -88,7 +88,7 @@ Column { PropertyLabel { text: qsTr("Asynchronous") - tooltip: qsTr("This property holds whether the component will be instantiated asynchronously.") + tooltip: qsTr("Whether the component will be instantiated asynchronously.") } SecondColumnLayout { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml index 30dbeccea9e..f2df9c0d042 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml @@ -386,6 +386,7 @@ SecondColumnLayout { property ListModel items: ListModel {} + enabled: isBaseState implicitWidth: StudioTheme.Values.colorEditorPopupCmoboBoxWidth width: implicitWidth actionIndicatorVisible: false @@ -442,6 +443,13 @@ SecondColumnLayout { } colorEditor.updateThumbnail() } + + ToolTipArea { + enabled: !isBaseState + anchors.fill: parent + tooltip: qsTr("Fill type can only be changed in base state.") + z: 10 + } } ExpandingSpacer {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComponentSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComponentSection.qml index 7a2225bee7d..5d9a121a913 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComponentSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComponentSection.qml @@ -218,7 +218,7 @@ Section { AbstractButton { id: editAnnotationButton buttonIcon: StudioTheme.Constants.edit - tooltip: qsTr("Edit annotation") + tooltip: qsTr("Edit Annotation") onClicked: annotationEditor.showWidget() onHoveredChanged: annotationEditor.checkAux() } @@ -226,7 +226,7 @@ Section { AbstractButton { id: removeAnnotationButton buttonIcon: StudioTheme.Constants.closeCross - tooltip: qsTr("Remove annotation") + tooltip: qsTr("Remove Annotation") onClicked: annotationEditor.removeFullAnnotation() onHoveredChanged: annotationEditor.checkAux() } diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/common/App.qml.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/common/App.qml.tpl index 1a6597e2271..32b90720938 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/common/App.qml.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/projects/common/App.qml.tpl @@ -35,13 +35,14 @@ import QtQuick.VirtualKeyboard %{QtQuickVersion} @endif Window { - width: Constants.width - height: Constants.height + width: mainScreen.width + height: mainScreen.height visible: true title: "%{ProjectName}" %{UIClassName} { + id: mainScreen } @if %{UseVirtualKeyboard} diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl index f24482bcac8..60aa8715873 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl @@ -62,6 +62,10 @@ Project { Environment { QT_QUICK_CONTROLS_CONF: "qtquickcontrols2.conf" QT_AUTO_SCREEN_SCALE_FACTOR: "1" +@if %{IsQt6Project} +@else + QMLSCENE_CORE_PROFILE: "true" // Required for macOS, but can create issues on embedded Linux +@endif @if %{UseVirtualKeyboard} QT_IM_MODULE: "qtvirtualkeyboard" QT_VIRTUALKEYBOARD_DESKTOP_DISABLE: 1 diff --git a/share/qtcreator/qmldesigner/workspacePresets/Animation-2D.wrk b/share/qtcreator/qmldesigner/workspacePresets/Animation-2D.wrk new file mode 100644 index 00000000000..e5c5d40bc03 --- /dev/null +++ b/share/qtcreator/qmldesigner/workspacePresets/Animation-2D.wrk @@ -0,0 +1 @@ +346 0 365712 00 0914 00 0712 0712 0 0310 914 406712 255 diff --git a/share/qtcreator/qmldesigner/workspacePresets/Animation-3D.wrk b/share/qtcreator/qmldesigner/workspacePresets/Animation-3D.wrk new file mode 100644 index 00000000000..b74da2a048a --- /dev/null +++ b/share/qtcreator/qmldesigner/workspacePresets/Animation-3D.wrk @@ -0,0 +1 @@ +541 0 426353 3530 0919 0919 0707 260968 0 0312 919 408 diff --git a/share/qtcreator/qmldesigner/workspacePresets/Basic.wrk b/share/qtcreator/qmldesigner/workspacePresets/Basic.wrk new file mode 100644 index 00000000000..82556f540a6 --- /dev/null +++ b/share/qtcreator/qmldesigner/workspacePresets/Basic.wrk @@ -0,0 +1 @@ +481 0 486968 0968 0919 00 919968 0968 0 0312 919 408 diff --git a/share/qtcreator/qmldesigner/workspacePresets/Code.wrk b/share/qtcreator/qmldesigner/workspacePresets/Code.wrk new file mode 100644 index 00000000000..c2510cb7dc2 --- /dev/null +++ b/share/qtcreator/qmldesigner/workspacePresets/Code.wrk @@ -0,0 +1 @@ +541 0 426707 0488 218601 4481050 0707 260541 0 426274 1050 315 diff --git a/share/qtcreator/qmldesigner/workspacePresets/3D_Preset.wrk b/share/qtcreator/qmldesigner/workspacePresets/Essentials-3D.wrk similarity index 100% rename from share/qtcreator/qmldesigner/workspacePresets/3D_Preset.wrk rename to share/qtcreator/qmldesigner/workspacePresets/Essentials-3D.wrk diff --git a/share/qtcreator/qmldesigner/workspacePresets/UX-Design.wrk b/share/qtcreator/qmldesigner/workspacePresets/UX-Design.wrk new file mode 100644 index 00000000000..aec1d2e2943 --- /dev/null +++ b/share/qtcreator/qmldesigner/workspacePresets/UX-Design.wrk @@ -0,0 +1 @@ +487 0 480707 0707 0914 0914 0707 260707 0 260310 914 406 diff --git a/share/qtcreator/qmldesigner/workspacePresets/Views-All.wrk b/share/qtcreator/qmldesigner/workspacePresets/Views-All.wrk new file mode 100644 index 00000000000..7a2639e9550 --- /dev/null +++ b/share/qtcreator/qmldesigner/workspacePresets/Views-All.wrk @@ -0,0 +1 @@ +343 353 270353 353353 353448 465457 456707 260343 353 270310 914 406 diff --git a/share/qtcreator/templates/wizards/classes/qtquickui/file.qml.tpl b/share/qtcreator/templates/wizards/classes/qtquickui/file.qml.tpl deleted file mode 100644 index d5287a5f9ee..00000000000 --- a/share/qtcreator/templates/wizards/classes/qtquickui/file.qml.tpl +++ /dev/null @@ -1,4 +0,0 @@ -import QtQuick 2.4 - -%{FormClass} { -} diff --git a/share/qtcreator/templates/wizards/classes/qtquickui/fileForm.ui.qml.tpl b/share/qtcreator/templates/wizards/classes/qtquickui/fileForm.ui.qml.tpl deleted file mode 100644 index 96a8ae4f577..00000000000 --- a/share/qtcreator/templates/wizards/classes/qtquickui/fileForm.ui.qml.tpl +++ /dev/null @@ -1,6 +0,0 @@ -import QtQuick 2.4 - -Item { - width: 400 - height: 400 -} diff --git a/share/qtcreator/templates/wizards/classes/qtquickui/wizard.json b/share/qtcreator/templates/wizards/classes/qtquickui/wizard.json deleted file mode 100644 index e8bd200d759..00000000000 --- a/share/qtcreator/templates/wizards/classes/qtquickui/wizard.json +++ /dev/null @@ -1,91 +0,0 @@ -{ - "version": 1, - "supportedProjectTypes": [ ], - "id": "R.QtQuickUi", - "category": "R.Qt", - "trDescription": "Creates a Qt Quick Designer UI form along with a matching QML file for implementation purposes. You can add the form and file to an existing Qt Quick Project.", - "trDisplayName": "QtQuick UI File", - "trDisplayCategory": "Qt", - "iconText": "ui.qml", - "featuresRequired": [ "QtSupport.Wizards.FeatureQtQuick.UiFiles" ], - "enabled": "%{JS: value('Plugins').indexOf('QmlJSEditor') >= 0}", - - "options" : [ - { "key": "QmlFile", "value": "%{Class}.%{JS: Util.preferredSuffix('text/x-qml')}" }, - { "key": "UiFile", "value": "%{FormClass}.%{JS: Util.preferredSuffix('application/x-qt.ui+qml')}" } - ], - - "pages" : - [ - { - "trDisplayName": "Define Class", - "trShortTitle": "Details", - "typeId": "Fields", - "data" : - [ - { - "name": "Class", - "trDisplayName": "Component name:", - "mandatory": true, - "type": "LineEdit", - "data": { - "validator": "(?:[A-Z_][a-zA-Z_0-9]*|)", - "fixup": "%{JS: '%{INPUT}'.charAt(0).toUpperCase() + '%{INPUT}'.slice(1) }" - } - }, - - { - "name": "Sp1", - "type": "Spacer", - "data": { "factor": 2 } - }, - { - "name": "FormClass", - "trDisplayName": "Component form name:", - "mandatory": true, - "type": "LineEdit", - "data": { - "validator": "(?:[A-Z_][a-zA-Z_0-9]*|)", - "fixup": "%{JS: '%{INPUT}'.charAt(0).toUpperCase() + '%{INPUT}'.slice(1) }", - "trText": "%{Class}Form" - } - }, - { - "name": "TargetPath", - "type": "PathChooser", - "trDisplayName": "Path:", - "mandatory": true, - "data": - { - "kind": "directory", - "basePath": "%{InitialPath}", - "path": "%{InitialPath}" - } - } - ] - }, - { - "trDisplayName": "Project Management", - "trShortTitle": "Summary", - "typeId": "Summary" - } - ], - "generators" : - [ - { - "typeId": "File", - "data": [ - { - "source": "file.qml.tpl", - "target": "%{TargetPath}/%{QmlFile}", - "openInEditor": true - }, - { - "source": "fileForm.ui.qml.tpl", - "target": "%{TargetPath}/%{UiFile}", - "openInEditor": true - } - ] - } - ] -} diff --git a/share/qtcreator/translations/qtcreator_ru.ts b/share/qtcreator/translations/qtcreator_ru.ts index 1305808ff41..0a7cda15516 100644 --- a/share/qtcreator/translations/qtcreator_ru.ts +++ b/share/qtcreator/translations/qtcreator_ru.ts @@ -1758,12 +1758,12 @@ Cancelling pending operations... Android::Internal::AndroidServiceWidget::AndroidServiceModel - The name of the class implementing the service - Имя класса, реализующего сервис + The name of the class implementing the service. + Имя класса, реализующего сервис. - Checked if the service is run in an external process - Отмечено, если сервис работает во внешнем процессе + Checked if the service is run in an external process. + Отмечено, если сервис работает во внешнем процессе. The name of the external process. @@ -1772,52 +1772,52 @@ Prefix with : if the process is private, use a lowercase name if the process is Если имя начинается с двоеточия, то процесс приватный, если написано в нижнем регистре, то процесс глобальный. - Checked if the service is in a separate dynamic library - Отмечено, если сервис в отдельной динамической библиотеке + Checked if the service is in a separate dynamic library. + Отмечено, если сервис в отдельной динамической библиотеке. - The name of the separate dynamic library - Имя отдельной динамической библиотеки + The name of the separate dynamic library. + Имя отдельной динамической библиотеки. - The arguments for telling the app to run the service instead of the main activity - Параметры запуска сервиса вместо основной функциональности приложения + The arguments for telling the app to run the service instead of the main activity. + Параметры запуска сервиса вместо основной функциональности приложения. - Service class name - Имя класса сервиса + Service class name. + Имя класса сервиса. - Run in external process - Работа во внешнем процессе + Run in external process. + Работа во внешнем процессе. - Process name - Имя процесса + Process name. + Имя процесса. - Run in external library - Работа во внешней библиотеке + Run in external library. + Работа во внешней библиотеке. - Library name - Имя библиотеки + Library name. + Имя библиотеки. - Service arguments - Параметры сервиса + Service arguments. + Параметры сервиса. - The class name must be set - Должно быть задано имя класса + The class name must be set. + Должно быть задано имя класса. - The process name must be set for a service run in an external process - Имя процесса должно быть задано для сервиса, работающего во внешнем процессе + The process name must be set for a service run in an external process. + Имя процесса должно быть задано для сервиса, работающего во внешнем процессе. - The library name must be set for a service run in an external library - Имя библиотеки должно быть задано для сервиса, работающего во внешней библиотеке + The library name must be set for a service run in an external library. + Имя библиотеки должно быть задано для сервиса, работающего во внешней библиотеке. The service arguments must be set for a service not run in an external library diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index b77138468e2..e30d9fe6abd 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -26,6 +26,7 @@ add_qtc_executable(qtcreator extend_qtc_executable(qtcreator CONDITION APPLE + DEPENDS ${FWFoundation} SOURCES main_mac.mm ) diff --git a/src/app/app.qbs b/src/app/app.qbs index 42172aa09f6..a0b30ae2ae4 100644 --- a/src/app/app.qbs +++ b/src/app/app.qbs @@ -43,6 +43,8 @@ QtcProduct { project.sharedSourcesDir + "/qtlockedfile", ] + cpp.frameworks: base.concat(qbs.targetOS.contains("macos") ? ["Foundation"] : []) + Depends { name: "app_version_header" } Depends { name: "Qt"; submodules: ["widgets", "network"] } Depends { name: "Utils" } @@ -97,4 +99,12 @@ QtcProduct { "../shared/qtlockedfile/qtlockedfile_win.cpp" ] } + + Group { + name: "main_macos" + condition: qbs.targetOS.contains("macos") + files: [ + "main_mac.mm" + ] + } } diff --git a/src/libs/advanceddockingsystem/dockmanager.h b/src/libs/advanceddockingsystem/dockmanager.h index 174239e22a7..acf29a5616e 100644 --- a/src/libs/advanceddockingsystem/dockmanager.h +++ b/src/libs/advanceddockingsystem/dockmanager.h @@ -60,7 +60,7 @@ QT_END_NAMESPACE namespace ADS { namespace Constants { -const char DEFAULT_WORKSPACE[] = "Essentials"; // This needs to align with a name of the shipped presets +const char DEFAULT_WORKSPACE[] = "Basic"; // This needs to align with a name of the shipped presets const char STARTUP_WORKSPACE_SETTINGS_KEY[] = "QML/Designer/StartupWorkspace"; const char AUTO_RESTORE_WORKSPACE_SETTINGS_KEY[] = "QML/Designer/AutoRestoreLastWorkspace"; } // namespace Constants diff --git a/src/libs/nanotrace/nanotrace.cpp b/src/libs/nanotrace/nanotrace.cpp index 36c7fd74590..d6e6ca30a30 100644 --- a/src/libs/nanotrace/nanotrace.cpp +++ b/src/libs/nanotrace/nanotrace.cpp @@ -60,7 +60,7 @@ struct ConvertArgValueToString { std::string Arg::value() const { - return std::visit(ConvertArgValueToString(), m_value); + return Utils::visit(ConvertArgValueToString(), m_value); } diff --git a/src/libs/nanotrace/nanotrace.h b/src/libs/nanotrace/nanotrace.h index 0df2be76df1..ea26487e408 100644 --- a/src/libs/nanotrace/nanotrace.h +++ b/src/libs/nanotrace/nanotrace.h @@ -33,9 +33,9 @@ # define NANOTRACESHARED_EXPORT Q_DECL_IMPORT #endif +#include // revert when macos minimum target is >= 10.14 #include -#include #include @@ -80,7 +80,7 @@ using TimePoint = std::chrono::time_point< Clock >; class NANOTRACESHARED_EXPORT Arg { public: - using SupportedType = std::variant; + using SupportedType = Utils::variant; Arg(const std::string &name, const SupportedType &val); std::string name() const; diff --git a/src/libs/qlitehtml b/src/libs/qlitehtml index 1d73f76face..62a1650652f 160000 --- a/src/libs/qlitehtml +++ b/src/libs/qlitehtml @@ -1 +1 @@ -Subproject commit 1d73f76faceb4b70b4eedab110400db7fddf6eb9 +Subproject commit 62a1650652fe5aa45f3e79e4b1a26f3bf92c6d4b diff --git a/src/libs/qmljs/qmljsbind.cpp b/src/libs/qmljs/qmljsbind.cpp index 512776ff550..96b0ee2908f 100644 --- a/src/libs/qmljs/qmljsbind.cpp +++ b/src/libs/qmljs/qmljsbind.cpp @@ -185,7 +185,7 @@ ObjectValue *Bind::bindObject(UiQualifiedId *qualifiedTypeNameId, UiObjectInitia void Bind::throwRecursionDepthError() { - _diagnosticMessages->append(DiagnosticMessage(Severity::Error, SourceLocation(), tr("Hit maximal recursion depth in AST visit"))); + _diagnosticMessages->append(DiagnosticMessage(Severity::Error, SourceLocation(), tr("Hit maximal recursion depth in AST visit."))); } void Bind::accept(Node *node) diff --git a/src/libs/qmljs/qmljsstaticanalysismessage.cpp b/src/libs/qmljs/qmljsstaticanalysismessage.cpp index a28867cc8c9..fd9b8bac378 100644 --- a/src/libs/qmljs/qmljsstaticanalysismessage.cpp +++ b/src/libs/qmljs/qmljsstaticanalysismessage.cpp @@ -71,7 +71,7 @@ static inline QString msgInvalidConstructor(const char *what) StaticAnalysisMessages::StaticAnalysisMessages() { // When changing a message or severity, update the documentation, currently - // in creator-editors.qdoc, accordingly. + // in creator-code-syntax.qdoc, accordingly. newMsg(ErrInvalidEnumValue, Error, tr("Invalid value for enum.")); newMsg(ErrEnumValueMustBeStringOrNumber, Error, @@ -211,36 +211,36 @@ StaticAnalysisMessages::StaticAnalysisMessages() newMsg(ErrInvalidArrayValueLength, Error, tr("%1 elements expected in array value."), 1); newMsg(WarnImperativeCodeNotEditableInVisualDesigner, Warning, - tr("Imperative code is not supported in the Qt Quick Designer.")); + tr("Imperative code is not supported in Qt Design Studio.")); newMsg(WarnUnsupportedTypeInVisualDesigner, Warning, - tr("This type (%1) is not supported in the Qt Quick Designer."), 1); + tr("This type (%1) is not supported in Qt Design Studio."), 1); newMsg(WarnReferenceToParentItemNotSupportedByVisualDesigner, Warning, - tr("Reference to parent item cannot be resolved correctly by the Qt Quick Designer.")); + tr("Reference to parent item cannot be resolved correctly by Qt Design Studio.")); newMsg(WarnUndefinedValueForVisualDesigner, Warning, tr("This visual property binding cannot be evaluated in the local context " - "and might not show up in Qt Quick Designer as expected.")); + "and might not show up in Qt Design Studio as expected.")); newMsg(WarnStatesOnlyInRootItemForVisualDesigner, Warning, - tr("Qt Quick Designer only supports states in the root item.")); + tr("Qt Design Studio only supports states in the root item.")); newMsg(ErrInvalidIdeInVisualDesigner, Error, - tr("This id might be ambiguous and is not supported in the Qt Quick Designer.")); + tr("This id might be ambiguous and is not supported in Qt Design Studio.")); newMsg(ErrUnsupportedRootTypeInVisualDesigner, Error, - tr("This type (%1) is not supported as a root element by Qt Quick Designer."), 1); + tr("This type (%1) is not supported as a root element by Qt Design Studio."), 1); newMsg(ErrUnsupportedRootTypeInQmlUi, Error, - tr("This type (%1) is not supported as a root element of a Qt Quick UI form."), 1); + tr("This type (%1) is not supported as a root element of a UI file (.ui.qml)."), 1); newMsg(ErrUnsupportedTypeInQmlUi, Error, - tr("This type (%1) is not supported in a Qt Quick UI form."), 1); + tr("This type (%1) is not supported in a UI file (.ui.qml)."), 1); newMsg(ErrFunctionsNotSupportedInQmlUi, Error, - tr("Functions are not supported in a Qt Quick UI form.")); + tr("Functions are not supported in a UI file (.ui.qml).")); newMsg(ErrBlocksNotSupportedInQmlUi, Error, - tr("JavaScript blocks are not supported in a Qt Quick UI form.")); + tr("JavaScript blocks are not supported in a UI file (.ui.qml).")); newMsg(ErrBehavioursNotSupportedInQmlUi, Error, - tr("Behavior type is not supported in a Qt Quick UI form.")); + tr("Behavior type is not supported in a UI file (.ui.qml).")); newMsg(ErrStatesOnlyInRootItemInQmlUi, Error, - tr("States are only supported in the root item in a Qt Quick UI form.")); + tr("States are only supported in the root item in a UI file (.ui.qml).")); newMsg(ErrReferenceToParentItemNotSupportedInQmlUi, Error, - tr("Referencing the parent of the root item is not supported in a Qt Quick UI form.")); + tr("Referencing the parent of the root item is not supported in a UI file (.ui.qml).")); newMsg(ErrDoNotMixTranslationFunctionsInQmlUi, Error, - tr("Do not mix translation functions in a Qt Quick UI form.")); + tr("Do not mix translation functions in a UI file (.ui.qml).")); newMsg(StateCannotHaveChildItem, Error, tr("A State cannot have a child item (%1)."), 1); newMsg(WarnDuplicateImport, Warning, @@ -250,7 +250,7 @@ StaticAnalysisMessages::StaticAnalysisMessages() newMsg(ErrTypeIsInstantiatedRecursively, Error, tr("Type cannot be instantiated recursively (%1)."), 1); newMsg(WarnLogicalValueDoesNotDependOnValues, Warning, - tr("Logical value does not depend on actual values")); + tr("Logical value does not depend on actual values.")); newMsg(ErrToManyComponentChildren, Error, tr("Components are only allowed to have a single child element.")); newMsg(WarnComponentRequiresChildren, Warning, diff --git a/src/libs/utils/CMakeLists.txt b/src/libs/utils/CMakeLists.txt index 652ec459cf0..d098dc6f44f 100644 --- a/src/libs/utils/CMakeLists.txt +++ b/src/libs/utils/CMakeLists.txt @@ -34,6 +34,7 @@ add_qtc_library(Utils differ.cpp differ.h displayname.cpp displayname.h dropsupport.cpp dropsupport.h + dynamiclicensecheck.h elfreader.cpp elfreader.h elidinglabel.cpp elidinglabel.h environment.cpp environment.h diff --git a/src/libs/utils/dynamiclicensecheck.h b/src/libs/utils/dynamiclicensecheck.h new file mode 100644 index 00000000000..634c6b28d11 --- /dev/null +++ b/src/libs/utils/dynamiclicensecheck.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include +#include +#include + +#include +#include + +#include + +namespace Utils { + +enum FoundLicense { + community, + professional, + enterprise +}; + +FoundLicense checkLicense() { + const ExtensionSystem::PluginSpec *pluginSpec = Utils::findOrDefault( + ExtensionSystem::PluginManager::plugins(), + Utils::equal(&ExtensionSystem::PluginSpec::name, QString("LicenseChecker"))); + + if (!pluginSpec) + return community; + + ExtensionSystem::IPlugin *plugin = pluginSpec->plugin(); + + if (!plugin) + return community; + + bool retVal = false; + bool success = QMetaObject::invokeMethod(plugin, + "qdsEnterpriseLicense", + Qt::DirectConnection, + Q_RETURN_ARG(bool, retVal)); + if (success && retVal) + return enterprise; + + return professional; +} +} // namespace Utils diff --git a/src/libs/utils/launchersocket.cpp b/src/libs/utils/launchersocket.cpp index 06cbd33a4aa..23f19f5f366 100644 --- a/src/libs/utils/launchersocket.cpp +++ b/src/libs/utils/launchersocket.cpp @@ -889,7 +889,7 @@ void LauncherSocket::handleSocketDisconnected() { QTC_ASSERT(isCalledFromLaunchersThread(), return); handleError(QCoreApplication::translate("Utils::LauncherSocket", - "Launcher socket closed unexpectedly")); + "Launcher socket closed unexpectedly.")); } void LauncherSocket::handleError(const QString &error) diff --git a/src/libs/utils/layoutbuilder.cpp b/src/libs/utils/layoutbuilder.cpp index d82617ea463..9a2c067cb29 100644 --- a/src/libs/utils/layoutbuilder.cpp +++ b/src/libs/utils/layoutbuilder.cpp @@ -106,29 +106,36 @@ LayoutBuilder::LayoutItem::LayoutItem(const QString &text) : text(text) {} -static QLayout *createLayoutFromType(LayoutBuilder::LayoutType layoutType) +QLayout *LayoutBuilder::createLayout() const { - switch (layoutType) { + QLayout *layout = nullptr; + switch (m_layoutType) { case LayoutBuilder::FormLayout: { auto formLayout = new QFormLayout; formLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); - return formLayout; + layout = formLayout; + break; } case LayoutBuilder::GridLayout: { auto gridLayout = new QGridLayout; - return gridLayout; + layout = gridLayout; + break; } case LayoutBuilder::HBoxLayout: { auto hboxLayout = new QHBoxLayout; - return hboxLayout; + layout = hboxLayout; + break; } case LayoutBuilder::VBoxLayout: { auto vboxLayout = new QVBoxLayout; - return vboxLayout; + layout = vboxLayout; + break; } } - QTC_CHECK(false); - return nullptr; + QTC_ASSERT(layout, return nullptr); + if (m_spacing) + layout->setSpacing(*m_spacing); + return layout; } static void setMargins(bool on, QLayout *layout) @@ -246,7 +253,7 @@ static void doLayoutHelper(QLayout *layout, */ LayoutBuilder::LayoutItem::LayoutItem(const LayoutBuilder &builder) { - layout = createLayoutFromType(builder.m_layoutType); + layout = builder.createLayout(); doLayoutHelper(layout, builder.m_items); setMargins(builder.m_withMargins, layout); } @@ -287,6 +294,12 @@ LayoutBuilder::LayoutBuilder(LayoutType layoutType, const LayoutItems &items) addItem(item); } +LayoutBuilder &LayoutBuilder::setSpacing(int spacing) +{ + m_spacing = spacing; + return *this; +} + LayoutBuilder::LayoutBuilder() = default; /*! @@ -343,7 +356,7 @@ LayoutBuilder &LayoutBuilder::addItem(const LayoutItem &item) void LayoutBuilder::doLayout(QWidget *parent) { - QLayout *layout = createLayoutFromType(m_layoutType); + QLayout *layout = createLayout(); parent->setLayout(layout); doLayoutHelper(layout, m_items); diff --git a/src/libs/utils/layoutbuilder.h b/src/libs/utils/layoutbuilder.h index a258690416e..5953fa09f95 100644 --- a/src/libs/utils/layoutbuilder.h +++ b/src/libs/utils/layoutbuilder.h @@ -27,6 +27,8 @@ #include "utils_global.h" +#include "optional.h" + #include #include #include @@ -97,6 +99,8 @@ public: ~LayoutBuilder(); + LayoutBuilder &setSpacing(int spacing); + LayoutBuilder &addItem(const LayoutItem &item); LayoutBuilder &addItems(const LayoutItems &items); @@ -148,10 +152,12 @@ public: protected: explicit LayoutBuilder(); // Adds to existing layout. + QLayout *createLayout() const; void doLayout(QWidget *parent); LayoutItems m_items; LayoutType m_layoutType; + Utils::optional m_spacing; bool m_withMargins = false; }; diff --git a/src/libs/utils/qtcprocess.cpp b/src/libs/utils/qtcprocess.cpp index c33a93d1857..fc7c8583acb 100644 --- a/src/libs/utils/qtcprocess.cpp +++ b/src/libs/utils/qtcprocess.cpp @@ -980,12 +980,12 @@ static bool askToKill(const QString &command) #ifdef QT_GUI_LIB if (QThread::currentThread() != QCoreApplication::instance()->thread()) return true; - const QString title = QtcProcess::tr("Process not Responding"); + const QString title = QtcProcess::tr("Process Not Responding"); QString msg = command.isEmpty() ? QtcProcess::tr("The process is not responding.") : QtcProcess::tr("The process \"%1\" is not responding.").arg(command); msg += ' '; - msg += QtcProcess::tr("Would you like to terminate it?"); + msg += QtcProcess::tr("Terminate the process?"); // Restore the cursor that is set to wait while running. const bool hasOverrideCursor = QApplication::overrideCursor() != nullptr; if (hasOverrideCursor) diff --git a/src/libs/utils/utils.qbs b/src/libs/utils/utils.qbs index 99dc266d6bb..1d2523e3a0f 100644 --- a/src/libs/utils/utils.qbs +++ b/src/libs/utils/utils.qbs @@ -13,6 +13,8 @@ Project { var libs = []; if (qbs.targetOS.contains("windows")) { libs.push("user32", "iphlpapi", "ws2_32", "shell32", "ole32"); + if (qbs.toolchainType === "mingw") + libs.push("uuid"); } else if (qbs.targetOS.contains("unix")) { if (!qbs.targetOS.contains("macos")) libs.push("X11"); diff --git a/src/plugins/android/androiddeployqtstep.cpp b/src/plugins/android/androiddeployqtstep.cpp index 16177ca1afa..1d76083a6e0 100644 --- a/src/plugins/android/androiddeployqtstep.cpp +++ b/src/plugins/android/androiddeployqtstep.cpp @@ -164,14 +164,15 @@ bool AndroidDeployQtStep::init() if (selectedAbis.isEmpty()) selectedAbis.append(bs->extraData(buildKey, Constants::AndroidAbi).toString()); - // TODO: use AndroidDevice directly instead of AndroidDeviceInfo. if (!info.isValid()) { - const IDevice *dev = DeviceKitAspect::device(kit()).data(); + const auto dev = + static_cast(DeviceKitAspect::device(kit()).data()); if (!dev) { reportWarningOrError(tr("No valid deployment device is set."), Task::Error); return false; } + // TODO: use AndroidDevice directly instead of AndroidDeviceInfo. info = AndroidDevice::androidDeviceInfoFromIDevice(dev); m_deviceInfo = info; // Keep around for later steps @@ -181,18 +182,17 @@ bool AndroidDeployQtStep::init() return false; } - const AndroidDevice *androidDev = static_cast(dev); - if (androidDev && !androidDev->canSupportAbis(selectedAbis)) { + if (!dev->canSupportAbis(selectedAbis)) { const QString error = tr("The deployment device \"%1\" does not support the " "architectures used by the kit.\n" "The kit supports \"%2\", but the device uses \"%3\".") .arg(dev->displayName()).arg(selectedAbis.join(", ")) - .arg(androidDev->supportedAbis().join(", ")); + .arg(dev->supportedAbis().join(", ")); reportWarningOrError(error, Task::Error); return false; } - if (androidDev && !androidDev->canHandleDeployments()) { + if (!dev->canHandleDeployments()) { reportWarningOrError(tr("The deployment device \"%1\" is disconnected.") .arg(dev->displayName()), Task::Error); return false; diff --git a/src/plugins/android/androidservicewidget.cpp b/src/plugins/android/androidservicewidget.cpp index f7e1f0c499f..02f5935c608 100644 --- a/src/plugins/android/androidservicewidget.cpp +++ b/src/plugins/android/androidservicewidget.cpp @@ -204,31 +204,31 @@ QVariant AndroidServiceWidget::AndroidServiceModel::headerData(int section, Qt:: { if (role == Qt::ToolTipRole && orientation == Qt::Horizontal) { if (section == 0) - return tr("The name of the class implementing the service"); + return tr("The name of the class implementing the service."); else if (section == 1) - return tr("Checked if the service is run in an external process"); + return tr("Checked if the service is run in an external process."); else if (section == 2) return tr("The name of the external process.\n" "Prefix with : if the process is private, use a lowercase name if the process is global."); else if (section == 3) - return tr("Checked if the service is in a separate dynamic library"); + return tr("Checked if the service is in a separate dynamic library."); else if (section == 4) - return tr("The name of the separate dynamic library"); + return tr("The name of the separate dynamic library."); else if (section == 5) - return tr("The arguments for telling the app to run the service instead of the main activity"); + return tr("The arguments for telling the app to run the service instead of the main activity."); } else if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { if (section == 0) - return tr("Service class name"); + return tr("Service class name."); else if (section == 1) - return tr("Run in external process"); + return tr("Run in external process."); else if (section == 2) - return tr("Process name"); + return tr("Process name."); else if (section == 3) - return tr("Run in external library"); + return tr("Run in external library."); else if (section == 4) - return tr("Library name"); + return tr("Library name."); else if (section == 5) - return tr("Service arguments"); + return tr("Service arguments."); } return {}; } @@ -247,22 +247,22 @@ QVariant AndroidServiceWidget::AndroidServiceModel::data(const QModelIndex &inde if (index.column() == 0) return m_services[index.row()].className(); else if (index.column() == 1) - return tr("Run in external process"); + return tr("Run in external process."); else if (index.column() == 2) return m_services[index.row()].externalProcessName(); else if (index.column() == 3) - return tr("Run in external library"); + return tr("Run in external library."); else if (index.column() == 4) return m_services[index.row()].externalLibraryName(); else if (index.column() == 5) return m_services[index.row()].serviceArguments(); } else if (role == Qt::ToolTipRole) { if (index.column() == 0 && m_services[index.row()].className().isEmpty()) - return tr("The class name must be set"); + return tr("The class name must be set."); else if (index.column() == 2 && m_services[index.row()].isRunInExternalProcess()) - return tr("The process name must be set for a service run in an external process"); + return tr("The process name must be set for a service run in an external process."); else if (index.column() == 4 && m_services[index.row()].isRunInExternalLibrary()) - return tr("The library name must be set for a service run in an external library"); + return tr("The library name must be set for a service run in an external library."); } else if (role == Qt::EditRole) { if (index.column() == 0) return m_services[index.row()].className(); diff --git a/src/plugins/android/androidsettingswidget.ui b/src/plugins/android/androidsettingswidget.ui index 1dd2d8779ac..fcfdc63a1d2 100644 --- a/src/plugins/android/androidsettingswidget.ui +++ b/src/plugins/android/androidsettingswidget.ui @@ -65,7 +65,7 @@ - Force a specific NDK installation to be used by all Android kits.<br/>Note that the forced NDK might not be compatible with all Qt registered versions. + Force a specific NDK installation to be used by all Android kits.<br/>Note that the forced NDK might not be compatible with all registered Qt versions. diff --git a/src/plugins/clangcodemodel/clangdclient.cpp b/src/plugins/clangcodemodel/clangdclient.cpp index fe495d9eed6..0b009d9b5c0 100644 --- a/src/plugins/clangcodemodel/clangdclient.cpp +++ b/src/plugins/clangcodemodel/clangdclient.cpp @@ -1402,7 +1402,10 @@ ClangdClient::ClangdClient(Project *project, const Utils::FilePath &jsonDbDir) setClientCapabilities(caps); setLocatorsEnabled(false); setAutoRequestCodeActions(false); // clangd sends code actions inside diagnostics - setProgressTitleForToken(indexingToken(), tr("Parsing C/C++ Files (clangd)")); + if (project) { + setProgressTitleForToken(indexingToken(), + tr("Indexing %1 with clangd").arg(project->displayName())); + } setCurrentProject(project); setDocumentChangeUpdateThreshold(d->settings.documentUpdateThreshold); @@ -2958,9 +2961,10 @@ void ClangdClient::Private::handleSemanticTokens(TextDocument *doc, int version, bool force) { SubtaskTimer t(highlightingTimer); - qCDebug(clangdLog) << "handling LSP tokens" << doc->filePath() << tokens.size(); + qCInfo(clangdLogHighlight) << "handling LSP tokens" << doc->filePath() + << version << tokens.size(); if (version != q->documentVersion(doc->filePath())) { - qCDebug(clangdLogHighlight) << "LSP tokens outdated; aborting highlighting procedure" + qCInfo(clangdLogHighlight) << "LSP tokens outdated; aborting highlighting procedure" << version << q->documentVersion(doc->filePath()); return; } @@ -2968,7 +2972,7 @@ void ClangdClient::Private::handleSemanticTokens(TextDocument *doc, const auto previous = previousTokens.find(doc); if (previous != previousTokens.end()) { if (!force && previous->first == tokens && previous->second == version) { - qCDebug(clangdLogHighlight) << "tokens and version same as last time; nothing to do"; + qCInfo(clangdLogHighlight) << "tokens and version same as last time; nothing to do"; return; } previous->first = tokens; @@ -2985,7 +2989,7 @@ void ClangdClient::Private::handleSemanticTokens(TextDocument *doc, if (!q->documentOpen(doc)) return; if (version != q->documentVersion(doc->filePath())) { - qCDebug(clangdLogHighlight) << "AST not up to date; aborting highlighting procedure" + qCInfo(clangdLogHighlight) << "AST not up to date; aborting highlighting procedure" << version << q->documentVersion(doc->filePath()); return; } diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp index 7daf90aa540..687d13c4476 100644 --- a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp +++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp @@ -44,6 +44,7 @@ #include #include +#include #include #include #include @@ -58,6 +59,7 @@ #include #include #include +#include #include #include @@ -85,21 +87,20 @@ static CppEditor::CppModelManager *cppModelManager() return CppEditor::CppModelManager::instance(); } -static const QList allCppEditors() +static ProjectExplorer::Project *fallbackProject() { - QList cppEditors; - for (const Core::DocumentModel::Entry * const entry : Core::DocumentModel::entries()) { - const auto textDocument = qobject_cast(entry->document); - if (!textDocument) - continue; - if (const auto cppEditor = qobject_cast(Utils::findOrDefault( - Core::DocumentModel::editorsForDocument(textDocument), [](Core::IEditor *editor) { - return CppEditor::CppModelManager::isCppEditor(editor); - }))) { - cppEditors << cppEditor; - } - } - return cppEditors; + if (ProjectExplorer::Project * const p = ProjectExplorer::ProjectTree::currentProject()) + return p; + return ProjectExplorer::SessionManager::startupProject(); +} + +static const QList allCppDocuments() +{ + const auto isCppDocument = Utils::equal(&Core::IDocument::id, + Utils::Id(CppEditor::Constants::CPPEDITOR_ID)); + const QList documents + = Utils::filtered(Core::DocumentModel::openedDocuments(), isCppDocument); + return Utils::qobject_container_cast(documents); } ClangModelManagerSupport::ClangModelManagerSupport() @@ -149,10 +150,7 @@ ClangModelManagerSupport::ClangModelManagerSupport() connect(sessionManager, &ProjectExplorer::SessionManager::aboutToRemoveProject, this, &ClangModelManagerSupport::onAboutToRemoveProject); connect(sessionManager, &ProjectExplorer::SessionManager::projectRemoved, - this, [this] { - if (ClangdClient * const fallbackClient = clientForProject(nullptr)) - claimNonProjectSources(fallbackClient); - }); + this, [this] { claimNonProjectSources(clientForProject(fallbackProject())); }); CppEditor::ClangdSettings::setDefaultClangdPath(Core::ICore::clangdExecutable(CLANG_BINDIR)); connect(&CppEditor::ClangdSettings::instance(), &CppEditor::ClangdSettings::changed, @@ -369,13 +367,15 @@ void ClangModelManagerSupport::updateLanguageClient( // Acquaint the client with all open C++ documents for this project. bool hasDocuments = false; - for (TextEditor::BaseTextEditor * const editor : allCppEditors()) { - const Utils::FilePath filePath = editor->textDocument()->filePath(); - if (!project->isKnownFile(filePath)) - continue; - LanguageClientManager::openDocumentWithClient(editor->textDocument(), client); - ClangEditorDocumentProcessor::clearTextMarks(filePath); - hasDocuments = true; + for (TextEditor::TextDocument * const doc : allCppDocuments()) { + const Client * const currentClient = LanguageClientManager::clientForDocument(doc); + if (!currentClient || !currentClient->project() + || currentClient->state() != Client::Initialized + || project->isKnownFile(doc->filePath())) { + LanguageClientManager::openDocumentWithClient(doc, client); + ClangEditorDocumentProcessor::clearTextMarks(doc->filePath()); + hasDocuments = true; + } } if (client->state() == Client::Initialized) @@ -444,7 +444,7 @@ ClangdClient *ClangModelManagerSupport::clientForProject( ClangdClient *ClangModelManagerSupport::clientForFile(const Utils::FilePath &file) const { - return clientForProject(ProjectExplorer::SessionManager::projectForFile(file)); + return qobject_cast(LanguageClientManager::clientForFilePath(file)); } ClangdClient *ClangModelManagerSupport::createClient(ProjectExplorer::Project *project, @@ -455,15 +455,18 @@ ClangdClient *ClangModelManagerSupport::createClient(ProjectExplorer::Project *p return client; } -void ClangModelManagerSupport::claimNonProjectSources(ClangdClient *fallbackClient) +void ClangModelManagerSupport::claimNonProjectSources(ClangdClient *client) { - for (TextEditor::BaseTextEditor * const editor : allCppEditors()) { - if (ProjectExplorer::SessionManager::projectForFile(editor->textDocument()->filePath())) + if (!client) + return; + for (TextEditor::TextDocument * const doc : allCppDocuments()) { + if (Client * const currentClient = LanguageClientManager::clientForDocument(doc); + currentClient && currentClient->state() == Client::Initialized + && (currentClient == client || currentClient->project())) { continue; - if (!fallbackClient->documentOpen(editor->textDocument())) { - ClangEditorDocumentProcessor::clearTextMarks(editor->textDocument()->filePath()); - fallbackClient->openDocument(editor->textDocument()); } + ClangEditorDocumentProcessor::clearTextMarks(doc->filePath()); + client->openDocument(doc); } } @@ -563,8 +566,10 @@ void ClangModelManagerSupport::onEditorOpened(Core::IEditor *editor) // TODO: Ensure that not fully loaded documents are updated? - ProjectExplorer::Project * const project + ProjectExplorer::Project * project = ProjectExplorer::SessionManager::projectForFile(document->filePath()); + if (!project) + project = fallbackProject(); if (ClangdClient * const client = clientForProject(project)) LanguageClientManager::openDocumentWithClient(textDocument, client); } diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.h b/src/plugins/clangcodemodel/clangmodelmanagersupport.h index 8307ece252f..b957ae69b90 100644 --- a/src/plugins/clangcodemodel/clangmodelmanagersupport.h +++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.h @@ -134,7 +134,7 @@ private: void updateLanguageClient(ProjectExplorer::Project *project, const CppEditor::ProjectInfo::ConstPtr &projectInfo); ClangdClient *createClient(ProjectExplorer::Project *project, const Utils::FilePath &jsonDbDir); - void claimNonProjectSources(ClangdClient *fallbackClient); + void claimNonProjectSources(ClangdClient *client); void watchForExternalChanges(); void watchForInternalChanges(); diff --git a/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp b/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp index cdcb78742e4..4fa51c94b7c 100644 --- a/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp +++ b/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp @@ -35,10 +35,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -514,6 +516,13 @@ namespace ClangCodeModel { namespace Internal { namespace Tests { +void ClangCodeCompletionTest::initTestCase() +{ + CppEditor::ClangdSettings::setUseClangd(false); + for (LanguageClient::Client * const c : LanguageClient::LanguageClientManager::clients()) + LanguageClient::LanguageClientManager::shutdownClient(c); +} + void ClangCodeCompletionTest::testCompleteDoxygenKeywords() { ProjectLessCompletionTest t("doxygenKeywordsCompletion.cpp"); diff --git a/src/plugins/clangcodemodel/test/clangcodecompletion_test.h b/src/plugins/clangcodemodel/test/clangcodecompletion_test.h index cf8bac668e5..ad2dd83ada2 100644 --- a/src/plugins/clangcodemodel/test/clangcodecompletion_test.h +++ b/src/plugins/clangcodemodel/test/clangcodecompletion_test.h @@ -38,6 +38,8 @@ class ClangCodeCompletionTest : public QObject Q_OBJECT private slots: + void initTestCase(); + void testCompleteDoxygenKeywords(); void testCompletePreprocessorKeywords(); void testCompleteIncludeDirective(); diff --git a/src/plugins/clangcodemodel/test/clangdtests.cpp b/src/plugins/clangcodemodel/test/clangdtests.cpp index 44f71140cee..d03fcc2c896 100644 --- a/src/plugins/clangcodemodel/test/clangdtests.cpp +++ b/src/plugins/clangcodemodel/test/clangdtests.cpp @@ -2036,7 +2036,7 @@ void ClangdTestExternalChanges::test() QVERIFY(waitForSignalOrTimeout(ClangModelManagerSupport::instance(), &ClangModelManagerSupport::createdClient, timeOutInMs())); ClangdClient * const newClient = ClangModelManagerSupport::instance() - ->clientForFile(filePath("main.cpp")); + ->clientForProject(project()); QVERIFY(newClient); QVERIFY(newClient != oldClient); newClient->enableTesting(); diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp index 69e4d01dd09..6513dadab92 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp @@ -303,7 +303,7 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildConfiguration *bc) m_resetButton->setEnabled(false); m_batchEditButton = new QPushButton(tr("Batch Edit...")); - m_batchEditButton->setToolTip(tr("Set or reset multiple values in the CMake Configuration.")); + m_batchEditButton->setToolTip(tr("Set or reset multiple values in the CMake configuration.")); m_showAdvancedCheckBox = new QCheckBox(tr("Advanced")); @@ -339,16 +339,18 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildConfiguration *bc) qmlDebugAspect }, m_warningMessageLabel, - Space(10), - Row{m_kitConfiguration, m_configurationStates}, - Group { - cmakeConfiguration, - Row { - bc->aspect(), - bc->aspect() - }, - m_reconfigureButton, - } + m_kitConfiguration, + Column { + m_configurationStates, + Group { + cmakeConfiguration, + Row { + bc->aspect(), + bc->aspect() + }, + m_reconfigureButton, + } + }.setSpacing(0) }.attachTo(details, false); updateAdvancedCheckBox(); diff --git a/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp b/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp index 5b677533ebb..cd315af53b8 100644 --- a/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp +++ b/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp @@ -326,7 +326,7 @@ QSet CMakeKitAspect::availableFeatures(const Kit *k) const QString CMakeKitAspect::msgUnsupportedVersion(const QByteArray &versionString) { - return tr("CMake version %1 is unsupported. Please update to " + return tr("CMake version %1 is unsupported. Update to " "version 3.14 (with file-api) or later.") .arg(QString::fromUtf8(versionString)); } diff --git a/src/plugins/coreplugin/coreplugin.cpp b/src/plugins/coreplugin/coreplugin.cpp index 6b3195d16d7..483c8c3c2b9 100644 --- a/src/plugins/coreplugin/coreplugin.cpp +++ b/src/plugins/coreplugin/coreplugin.cpp @@ -283,7 +283,7 @@ QObject *CorePlugin::remoteCommand(const QStringList & /* options */, }); return nullptr; } - const FilePaths filePaths = Utils::transform(args, FilePath::fromString); + const FilePaths filePaths = Utils::transform(args, FilePath::fromUserInput); IDocument *res = MainWindow::openFiles( filePaths, ICore::OpenFilesFlags(ICore::SwitchMode | ICore::CanContainLineAndColumnNumbers | ICore::SwitchSplitIfAlreadyVisible), diff --git a/src/plugins/coreplugin/dialogs/ioptionspage.h b/src/plugins/coreplugin/dialogs/ioptionspage.h index cd0e59b016e..6c989ccad45 100644 --- a/src/plugins/coreplugin/dialogs/ioptionspage.h +++ b/src/plugins/coreplugin/dialogs/ioptionspage.h @@ -83,13 +83,16 @@ protected: void setSettings(Utils::AspectContainer *settings); void setLayouter(const std::function &layouter); + // Used in FontSettingsPage. FIXME? + QPointer m_widget; // Used in conjunction with m_widgetCreator + +private: Utils::Id m_id; Utils::Id m_category; QString m_displayName; QString m_displayCategory; Utils::Icon m_categoryIcon; WidgetCreator m_widgetCreator; - QPointer m_widget; // Used in conjunction with m_widgetCreator mutable bool m_keywordsInitialized = false; mutable QStringList m_keywords; diff --git a/src/plugins/coreplugin/documentmanager.cpp b/src/plugins/coreplugin/documentmanager.cpp index 15fb2edfdc1..43ddad0ceef 100644 --- a/src/plugins/coreplugin/documentmanager.cpp +++ b/src/plugins/coreplugin/documentmanager.cpp @@ -199,6 +199,7 @@ public: IDocument *m_blockedIDocument = nullptr; QAction *m_saveAllAction; + QString fileDialogFilterOverride; }; static DocumentManager *m_instance; @@ -770,6 +771,16 @@ bool DocumentManager::saveDocument(IDocument *document, return ret; } +QString DocumentManager::fileDialogFilter(QString *selectedFilter) +{ + if (!d->fileDialogFilterOverride.isEmpty()) { + if (selectedFilter) + *selectedFilter = d->fileDialogFilterOverride.split(";;").first(); + return d->fileDialogFilterOverride; + } + return allDocumentFactoryFiltersString(selectedFilter); +} + QString DocumentManager::allDocumentFactoryFiltersString(QString *allFilesFilter = nullptr) { QSet uniqueFilters; @@ -1531,6 +1542,11 @@ void DocumentManager::notifyFilesChangedInternally(const FilePaths &filePaths) emit m_instance->filesChangedInternally(filePaths); } +void DocumentManager::setFileDialogFilter(const QString &filter) +{ + d->fileDialogFilterOverride = filter; +} + void DocumentManager::registerSaveAllAction() { d->registerSaveAllAction(); diff --git a/src/plugins/coreplugin/documentmanager.h b/src/plugins/coreplugin/documentmanager.h index 01fa660272c..46667cf8ded 100644 --- a/src/plugins/coreplugin/documentmanager.h +++ b/src/plugins/coreplugin/documentmanager.h @@ -142,6 +142,10 @@ public: lead to any editors to reload or any other editor manager actions. */ static void notifyFilesChangedInternally(const Utils::FilePaths &filePaths); + static void setFileDialogFilter(const QString &filter); + + static QString fileDialogFilter(QString *selectedFilter = nullptr); + signals: /* Used to notify e.g. the code model to update the given files. Does *not* lead to any editors to reload or any other editor manager actions. */ diff --git a/src/plugins/coreplugin/editormanager/editormanager.cpp b/src/plugins/coreplugin/editormanager/editormanager.cpp index e328bd50435..150d5d957b2 100644 --- a/src/plugins/coreplugin/editormanager/editormanager.cpp +++ b/src/plugins/coreplugin/editormanager/editormanager.cpp @@ -3231,7 +3231,7 @@ void EditorManager::addCloseEditorListener(const std::function FilePaths EditorManager::getOpenFilePaths() { QString selectedFilter; - const QString &fileFilters = DocumentManager::allDocumentFactoryFiltersString(&selectedFilter); + const QString &fileFilters = DocumentManager::fileDialogFilter(&selectedFilter); return DocumentManager::getOpenFileNames(fileFilters, {}, &selectedFilter); } diff --git a/src/plugins/coreplugin/loggingviewer.cpp b/src/plugins/coreplugin/loggingviewer.cpp index 89046e39b2e..677bffd455c 100644 --- a/src/plugins/coreplugin/loggingviewer.cpp +++ b/src/plugins/coreplugin/loggingviewer.cpp @@ -425,7 +425,7 @@ LoggingViewManagerWidget::LoggingViewManagerWidget(QWidget *parent) buttonsLayout->addWidget(stop); auto qtInternal = new QToolButton; qtInternal->setIcon(Core::Icons::QTLOGO.icon()); - qtInternal->setToolTip(tr("Toggle logging of Qt internal loggings")); + qtInternal->setToolTip(tr("Toggle Qt Internal Logging")); qtInternal->setCheckable(true); qtInternal->setChecked(false); buttonsLayout->addWidget(qtInternal); @@ -616,7 +616,7 @@ void LoggingViewManagerWidget::saveLoggingsToFile() const if (enabled) m_manager->setEnabled(false); const Utils::FilePath fp = Utils::FileUtils::getSaveFilePath(ICore::dialogParent(), - tr("Save logs as")); + tr("Save Logs As")); if (fp.isEmpty()) return; const bool useTS = m_timestamps->isChecked(); @@ -628,7 +628,7 @@ void LoggingViewManagerWidget::saveLoggingsToFile() const if (res == -1) { QMessageBox::critical( ICore::dialogParent(), tr("Error"), - tr("Failed to write logs to '%1'.").arg(fp.toUserOutput())); + tr("Failed to write logs to \"%1\".").arg(fp.toUserOutput())); break; } } @@ -636,14 +636,14 @@ void LoggingViewManagerWidget::saveLoggingsToFile() const } else { QMessageBox::critical( ICore::dialogParent(), tr("Error"), - tr("Failed to open file '%1' for writing logs.").arg(fp.toUserOutput())); + tr("Failed to open file \"%1\" for writing logs.").arg(fp.toUserOutput())); } } void LoggingViewManagerWidget::saveEnabledCategoryPreset() const { Utils::FilePath fp = Utils::FileUtils::getSaveFilePath(ICore::dialogParent(), - tr("Save enabled categories as")); + tr("Save Enabled Categories As")); if (fp.isEmpty()) return; const QList enabled = m_categoryModel->enabledCategories(); @@ -663,13 +663,13 @@ void LoggingViewManagerWidget::saveEnabledCategoryPreset() const if (!fp.writeFileContents(doc.toJson(QJsonDocument::Compact))) QMessageBox::critical( ICore::dialogParent(), tr("Error"), - tr("Failed to write preset file '%1'.").arg(fp.toUserOutput())); + tr("Failed to write preset file \"%1\".").arg(fp.toUserOutput())); } void LoggingViewManagerWidget::loadAndUpdateFromPreset() { Utils::FilePath fp = Utils::FileUtils::getOpenFilePath(ICore::dialogParent(), - tr("Load enabled categories from")); + tr("Load Enabled Categories From")); if (fp.isEmpty()) return; // read file, update categories @@ -677,7 +677,7 @@ void LoggingViewManagerWidget::loadAndUpdateFromPreset() QJsonDocument doc = QJsonDocument::fromJson(fp.fileContents(), &error); if (error.error != QJsonParseError::NoError) { QMessageBox::critical(ICore::dialogParent(), tr("Error"), - tr("Failed to read preset file '%1': %2").arg(fp.toUserOutput()) + tr("Failed to read preset file \"%1\": %2").arg(fp.toUserOutput()) .arg(error.errorString())); return; } diff --git a/src/plugins/cppeditor/clangdiagnosticconfigsselectionwidget.cpp b/src/plugins/cppeditor/clangdiagnosticconfigsselectionwidget.cpp index 4692a158ab8..a1de9d125dd 100644 --- a/src/plugins/cppeditor/clangdiagnosticconfigsselectionwidget.cpp +++ b/src/plugins/cppeditor/clangdiagnosticconfigsselectionwidget.cpp @@ -41,7 +41,7 @@ namespace CppEditor { ClangDiagnosticConfigsSelectionWidget::ClangDiagnosticConfigsSelectionWidget(QWidget *parent) : QWidget(parent) - , m_label(new QLabel(tr("Diagnostic Configuration:"))) + , m_label(new QLabel(tr("Diagnostic configuration:"))) , m_button(new QPushButton) { auto *layout = new QHBoxLayout(this); diff --git a/src/plugins/cppeditor/insertionpointlocator.cpp b/src/plugins/cppeditor/insertionpointlocator.cpp index 7d657bacc3b..fceb9a96055 100644 --- a/src/plugins/cppeditor/insertionpointlocator.cpp +++ b/src/plugins/cppeditor/insertionpointlocator.cpp @@ -177,6 +177,8 @@ QList collectAccessRanges(const CPlusPlus::TranslationUnit *tu, if (AccessDeclarationAST *xsDecl = decl->asAccessDeclaration()) { const unsigned token = xsDecl->access_specifier_token; + if (tu->tokenAt(token).generated()) + continue; InsertionPointLocator::AccessSpec newXsSpec = initialXs; bool isSlot = xsDecl->slots_token && tu->tokenKind(xsDecl->slots_token) == T_Q_SLOTS; diff --git a/src/plugins/debugger/debuggeractions.cpp b/src/plugins/debugger/debuggeractions.cpp index bab0d00133d..736869cc591 100644 --- a/src/plugins/debugger/debuggeractions.cpp +++ b/src/plugins/debugger/debuggeractions.cpp @@ -120,7 +120,7 @@ DebuggerSettings::DebuggerSettings() forceLoggingToConsole.setSettingsKey(debugModeGroup, "ForceLoggingToConsole"); forceLoggingToConsole.setLabelText(tr("Force logging to console")); - forceLoggingToConsole.setToolTip(tr("This sets QT_LOGGING_TO_CONSOLE=1 in the environment " + forceLoggingToConsole.setToolTip(tr("Sets QT_LOGGING_TO_CONSOLE=1 in the environment " "of the debugged program, preventing storing debug output " "in system logs.")); @@ -165,7 +165,7 @@ DebuggerSettings::DebuggerSettings() CommonOptionsPage::msgSetBreakpointAtFunction(Constants::CRT_DEBUG_REPORT)); cdbBreakOnCrtDbgReport.setToolTip( CommonOptionsPage::msgSetBreakpointAtFunctionToolTip(Constants::CRT_DEBUG_REPORT, - tr("This is useful to catch runtime error messages for example caused by assert()."))); + tr("Catches runtime error messages caused by assert(), for example."))); useCdbConsole.setSettingsKey(cdbSettingsGroup, "CDB_Console"); useCdbConsole.setToolTip("

" + tr( @@ -421,7 +421,7 @@ DebuggerSettings::DebuggerSettings() useAnnotationsInMainEditor.setSettingsKey(debugModeGroup, "UseAnnotations"); useAnnotationsInMainEditor.setLabelText(tr("Use annotations in main editor when debugging")); - useAnnotationsInMainEditor.setToolTip(tr("

Checking this will show simple variable values " + useAnnotationsInMainEditor.setToolTip(tr("

Shows simple variable values " "as annotations in the main editor during debugging.")); useAnnotationsInMainEditor.setDefaultValue(true); @@ -440,7 +440,7 @@ DebuggerSettings::DebuggerSettings() useToolTipsInMainEditor.setSettingsKey(debugModeGroup, "UseToolTips"); useToolTipsInMainEditor.setLabelText(tr("Use tooltips in main editor when debugging")); - useToolTipsInMainEditor.setToolTip(tr("

Checking this will enable tooltips for variable " + useToolTipsInMainEditor.setToolTip(tr("

Enables tooltips for variable " "values during debugging. Since this can slow down debugging and " "does not provide reliable information as it does not use scope " "information, it is switched off by default.")); @@ -448,17 +448,17 @@ DebuggerSettings::DebuggerSettings() useToolTipsInLocalsView.setSettingsKey(debugModeGroup, "UseToolTipsInLocalsView"); useToolTipsInLocalsView.setLabelText(tr("Use Tooltips in Locals View when Debugging")); - useToolTipsInLocalsView.setToolTip(tr("

Checking this will enable tooltips in the locals " + useToolTipsInLocalsView.setToolTip(tr("

Enables tooltips in the locals " "view during debugging.")); useToolTipsInBreakpointsView.setSettingsKey(debugModeGroup, "UseToolTipsInBreakpointsView"); useToolTipsInBreakpointsView.setLabelText(tr("Use Tooltips in Breakpoints View when Debugging")); - useToolTipsInBreakpointsView.setToolTip(tr("

Checking this will enable tooltips in the breakpoints " + useToolTipsInBreakpointsView.setToolTip(tr("

Enables tooltips in the breakpoints " "view during debugging.")); useToolTipsInStackView.setSettingsKey(debugModeGroup, "UseToolTipsInStackView"); useToolTipsInStackView.setLabelText(tr("Use Tooltips in Stack View when Debugging")); - useToolTipsInStackView.setToolTip(tr("

Checking this will enable tooltips in the stack " + useToolTipsInStackView.setToolTip(tr("

Enables tooltips in the stack " "view during debugging.")); useToolTipsInStackView.setDefaultValue(true); diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp index e12763c08ac..4b11dd72e05 100644 --- a/src/plugins/docker/dockerdevice.cpp +++ b/src/plugins/docker/dockerdevice.cpp @@ -333,7 +333,7 @@ public: m_runAsOutsideUser = new QCheckBox(tr("Run as outside user")); m_runAsOutsideUser->setToolTip(tr("Uses user ID and group ID of the user running Qt Creator " - "in the Docker container.")); + "in the docker container.")); m_runAsOutsideUser->setChecked(data.useLocalUidGid); m_runAsOutsideUser->setEnabled(HostOsInfo::isLinuxHost()); @@ -361,7 +361,7 @@ public: m_pathsListEdit = new PathListEditor; m_pathsListEdit->setToolTip(tr("Maps paths in this list one-to-one to the " - "Docker container.")); + "docker container.")); m_pathsListEdit->setPathList(data.mounts); connect(m_pathsListEdit, &PathListEditor::changed, this, [dockerDevice, this]() { @@ -378,12 +378,12 @@ public: auto searchDirsComboBox = new QComboBox; searchDirsComboBox->addItem(tr("Search in PATH")); - searchDirsComboBox->addItem(tr("Search in selected directories")); + searchDirsComboBox->addItem(tr("Search in Selected Directories")); auto searchDirsLineEdit = new QLineEdit; searchDirsLineEdit->setText("/usr/bin;/opt"); searchDirsLineEdit->setToolTip( - tr("Select the paths in the Docker image that should be scanned for Kit entries")); + tr("Select the paths in the docker image that should be scanned for kit entries.")); auto searchPaths = [this, searchDirsComboBox, searchDirsLineEdit, dockerDevice] { FilePaths paths; @@ -491,7 +491,7 @@ Tasks DockerDevice::validate() const Tasks result; if (d->m_data.mounts.isEmpty()) { result << Task(Task::Error, - tr("The Docker device has not set up shared directories." + tr("The docker device has not set up shared directories." "This will not work for building."), {}, -1, {}); } @@ -529,7 +529,7 @@ DockerDevice::DockerDevice(const DockerDeviceData &data) Q_UNUSED(env); // TODO: That's the runnable's environment in general. Use it via -e below. updateContainerAccess(); if (d->m_container.isEmpty()) { - MessageManager::writeDisrupting(tr("Error starting remote shell. No container")); + MessageManager::writeDisrupting(tr("Error starting remote shell. No container.")); return; } @@ -910,7 +910,7 @@ void DockerDevicePrivate::startContainer() if (exitCode > 120) { DockerPlugin::setGlobalDaemonState(false); LOG("DOCKER DAEMON NOT RUNNING?"); - MessageManager::writeFlashing(tr("Docker Daemon appears to be not running. " + MessageManager::writeFlashing(tr("Docker daemon appears to be not running. " "Verify daemon is up and running and reset the " "docker daemon on the docker device settings page " "or restart Qt Creator.")); @@ -974,7 +974,7 @@ void DockerDevicePrivate::updateFileSystemAccess() if (!QFileInfo(m_mergedDir).isReadable()) { MessageManager::writeFlashing( - tr("Local read access to Docker container %1 unavailable through directory \"%2\".") + tr("Local read access to docker container %1 unavailable through directory \"%2\".") .arg(m_container, m_mergedDir) + '\n' + tr("Output: \"%1\"").arg(out) + '\n' + tr("Error: \"%1\"").arg(proc.stdErr())); diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp index 8158254163e..ec9bed459e5 100644 --- a/src/plugins/languageclient/client.cpp +++ b/src/plugins/languageclient/client.cpp @@ -112,6 +112,11 @@ Client::Client(BaseClientInterface *clientInterface) m_tokenSupport.setTokenTypesMap(SemanticTokens::defaultTokenTypesMap()); m_tokenSupport.setTokenModifiersMap(SemanticTokens::defaultTokenModifiersMap()); + + m_shutdownTimer.setInterval(20 /*seconds*/ * 1000); + connect(&m_shutdownTimer, &QTimer::timeout, this, [this] { + LanguageClientManager::deleteClient(this); + }); } QString Client::name() const @@ -317,7 +322,6 @@ void Client::initialize() // directly send message otherwise the state check of sendContent would fail sendMessage(initRequest.toBaseMessage()); - m_clientInterface->sendMessage(initRequest.toBaseMessage()); m_state = InitializeRequested; } @@ -331,6 +335,7 @@ void Client::shutdown() }); sendContent(shutdown); m_state = ShutdownRequested; + m_shutdownTimer.start(); } Client::State Client::state() const @@ -364,7 +369,7 @@ void Client::setClientCapabilities(const LanguageServerProtocol::ClientCapabilit void Client::openDocument(TextEditor::TextDocument *document) { using namespace TextEditor; - if (!isSupportedDocument(document)) + if (m_openedDocument.contains(document) || !isSupportedDocument(document)) return; if (m_state != Initialized) { @@ -372,8 +377,6 @@ void Client::openDocument(TextEditor::TextDocument *document) return; } - QTC_ASSERT(!m_openedDocument.contains(document), return); - const FilePath &filePath = document->filePath(); const QString method(DidOpenTextDocumentNotification::methodName); if (Utils::optional registered = m_dynamicCapabilities.isRegistered(method)) { @@ -1622,6 +1625,7 @@ void Client::initializeCallback(const InitializeRequest::Response &initResponse) void Client::shutDownCallback(const ShutdownRequest::Response &shutdownResponse) { + m_shutdownTimer.stop(); QTC_ASSERT(m_state == ShutdownRequested, return); QTC_ASSERT(m_clientInterface, return); optional errorValue = shutdownResponse.error(); @@ -1634,6 +1638,7 @@ void Client::shutDownCallback(const ShutdownRequest::Response &shutdownResponse) sendMessage(ExitNotification().toBaseMessage()); qCDebug(LOGLSPCLIENT) << "language server " << m_displayName << " shutdown"; m_state = Shutdown; + m_shutdownTimer.start(); } bool Client::sendWorkspceFolderChanges() const diff --git a/src/plugins/languageclient/client.h b/src/plugins/languageclient/client.h index cffdbb0ac93..4a7356e7c50 100644 --- a/src/plugins/languageclient/client.h +++ b/src/plugins/languageclient/client.h @@ -316,6 +316,7 @@ private: LogTarget m_logTarget = LogTarget::Ui; bool m_locatorsEnabled = true; bool m_autoRequestCodeActions = true; + QTimer m_shutdownTimer; }; } // namespace LanguageClient diff --git a/src/plugins/languageclient/languageclientmanager.cpp b/src/plugins/languageclient/languageclientmanager.cpp index 8ac21b6ef4e..992138f06a1 100644 --- a/src/plugins/languageclient/languageclientmanager.cpp +++ b/src/plugins/languageclient/languageclientmanager.cpp @@ -232,7 +232,7 @@ void LanguageClientManager::deleteClient(Client *client) QTC_ASSERT(managerInstance, return); QTC_ASSERT(client, return); qCDebug(Log) << "delete client: " << client->name() << client; - client->disconnect(); + client->disconnect(managerInstance); managerInstance->m_clients.removeAll(client); for (QVector &clients : managerInstance->m_clientsForSetting) clients.removeAll(client); diff --git a/src/plugins/mcusupport/CMakeLists.txt b/src/plugins/mcusupport/CMakeLists.txt index c82bf2b1f45..6d7602c2f0e 100644 --- a/src/plugins/mcusupport/CMakeLists.txt +++ b/src/plugins/mcusupport/CMakeLists.txt @@ -8,8 +8,10 @@ add_qtc_plugin(McuSupport mcusupportconstants.h mcusupportdevice.cpp mcusupportdevice.h mcusupportoptions.cpp mcusupportoptions.h mcuabstractpackage.h + mcukitmanager.cpp mcukitmanager.h mcusupportoptionspage.cpp mcusupportoptionspage.h mcupackage.cpp mcupackage.h + mcutarget.cpp mcutarget.h mcusupportplugin.cpp mcusupportplugin.h mcusupportsdk.cpp mcusupportsdk.h mcusupportrunconfiguration.cpp mcusupportrunconfiguration.h diff --git a/src/plugins/mcusupport/mcuabstractpackage.h b/src/plugins/mcusupport/mcuabstractpackage.h index 27b8b75c97d..9406b474fb9 100644 --- a/src/plugins/mcusupport/mcuabstractpackage.h +++ b/src/plugins/mcusupport/mcuabstractpackage.h @@ -58,14 +58,10 @@ public: virtual const QString &environmentVariableName() const = 0; virtual void setAddToPath(bool) = 0; virtual bool addToPath() const = 0; - virtual void writeGeneralSettings() const = 0; virtual bool writeToSettings() const = 0; virtual void setRelativePathModifier(const QString &) = 0; virtual void setVersions(const QStringList &) = 0; - virtual bool automaticKitCreationEnabled() const = 0; - virtual void setAutomaticKitCreationEnabled(const bool enabled) = 0; - virtual QWidget *widget() = 0; signals: diff --git a/src/plugins/mcusupport/mcukitmanager.cpp b/src/plugins/mcusupport/mcukitmanager.cpp new file mode 100644 index 00000000000..5fffe2b8da3 --- /dev/null +++ b/src/plugins/mcusupport/mcukitmanager.cpp @@ -0,0 +1,596 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "mcukitmanager.h" +#include "mcusupportoptions.h" + +#include "mcusupportconstants.h" +#include "mcukitinformation.h" +#include "mcupackage.h" +#include "mcutarget.h" +#include "mcusupportplugin.h" +#include "mcusupportsdk.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using CMakeProjectManager::CMakeConfigItem; +using CMakeProjectManager::CMakeConfigurationKitAspect; +using namespace ProjectExplorer; +using namespace Utils; + +namespace McuSupport { +namespace Internal { +namespace McuKitManager { + +static const int KIT_VERSION = 9; // Bumps up whenever details in Kit creation change + +static FilePath qulDocsDir() +{ + const FilePath qulDir = McuSupportOptions::qulDirFromSettings(); + if (qulDir.isEmpty() || !qulDir.exists()) + return {}; + const FilePath docsDir = qulDir.pathAppended("docs"); + return docsDir.exists() ? docsDir : FilePath(); +} + +static void setKitToolchains(Kit *k, const McuToolChainPackage *tcPackage) +{ + switch (tcPackage->type()) { + case McuToolChainPackage::Type::Unsupported: + return; + + case McuToolChainPackage::Type::GHS: + case McuToolChainPackage::Type::GHSArm: + return; // No Green Hills toolchain, because support for it is missing. + + case McuToolChainPackage::Type::IAR: + case McuToolChainPackage::Type::KEIL: + case McuToolChainPackage::Type::MSVC: + case McuToolChainPackage::Type::GCC: + case McuToolChainPackage::Type::ArmGcc: + ToolChainKitAspect::setToolChain(k, + tcPackage->toolChain( + ProjectExplorer::Constants::C_LANGUAGE_ID)); + ToolChainKitAspect::setToolChain(k, + tcPackage->toolChain( + ProjectExplorer::Constants::CXX_LANGUAGE_ID)); + return; + + default: + Q_UNREACHABLE(); + } +} + + +static void setKitProperties(const QString &kitName, + Kit *k, + const McuTarget *mcuTarget, + const FilePath &sdkPath) +{ + using namespace Constants; + + k->setUnexpandedDisplayName(kitName); + k->setValue(KIT_MCUTARGET_VENDOR_KEY, mcuTarget->platform().vendor); + k->setValue(KIT_MCUTARGET_MODEL_KEY, mcuTarget->platform().name); + k->setValue(KIT_MCUTARGET_COLORDEPTH_KEY, mcuTarget->colorDepth()); + k->setValue(KIT_MCUTARGET_SDKVERSION_KEY, mcuTarget->qulVersion().toString()); + k->setValue(KIT_MCUTARGET_KITVERSION_KEY, KIT_VERSION); + k->setValue(KIT_MCUTARGET_OS_KEY, static_cast(mcuTarget->os())); + k->setValue(KIT_MCUTARGET_TOOCHAIN_KEY, mcuTarget->toolChainPackage()->toolChainName()); + k->setAutoDetected(false); + k->makeSticky(); + if (mcuTarget->toolChainPackage()->isDesktopToolchain()) + k->setDeviceTypeForIcon(DEVICE_TYPE); + k->setValue(QtSupport::SuppliesQtQuickImportPath::id(), true); + k->setValue(QtSupport::KitQmlImportPath::id(), sdkPath.pathAppended("include/qul").toVariant()); + k->setValue(QtSupport::KitHasMergedHeaderPathsWithQmlImportPaths::id(), true); + QSet irrelevant = { + SysRootKitAspect::id(), + QtSupport::SuppliesQtQuickImportPath::id(), + QtSupport::KitQmlImportPath::id(), + QtSupport::KitHasMergedHeaderPathsWithQmlImportPaths::id(), + }; + if (!McuSupportOptions::kitsNeedQtVersion()) + irrelevant.insert(QtSupport::QtKitAspect::id()); + k->setIrrelevantAspects(irrelevant); +} + + +static void setKitDebugger(Kit *k, const McuToolChainPackage *tcPackage) +{ + if (tcPackage->isDesktopToolchain()) { + // Qt Creator seems to be smart enough to deduce the right Kit debugger from the ToolChain + return; + } + + switch (tcPackage->type()) { + case McuToolChainPackage::Type::Unsupported: + case McuToolChainPackage::Type::GHS: + case McuToolChainPackage::Type::GHSArm: + case McuToolChainPackage::Type::IAR: + return; // No Green Hills and IAR debugger, because support for it is missing. + + case McuToolChainPackage::Type::KEIL: + case McuToolChainPackage::Type::MSVC: + case McuToolChainPackage::Type::GCC: + case McuToolChainPackage::Type::ArmGcc: { + const QVariant debuggerId = tcPackage->debuggerId(); + if (debuggerId.isValid()) { + Debugger::DebuggerKitAspect::setDebugger(k, debuggerId); + } + return; + } + + default: + Q_UNREACHABLE(); + } +} + +static void setKitDevice(Kit *k, const McuTarget *mcuTarget) +{ + // "Device Type" Desktop is the default. We use that for the Qt for MCUs Desktop Kit + if (mcuTarget->toolChainPackage()->isDesktopToolchain()) + return; + + DeviceTypeKitAspect::setDeviceTypeId(k, Constants::DEVICE_TYPE); +} + +static bool expectsCmakeVars(const McuTarget *mcuTarget) +{ + return mcuTarget->qulVersion() >= QVersionNumber{2, 0}; +} + +static void setKitDependencies(Kit *k, + const McuTarget *mcuTarget, + const McuAbstractPackage *qtForMCUsSdkPackage) +{ + NameValueItems dependencies; + + auto processPackage = [&dependencies](const McuAbstractPackage *package) { + if (!package->environmentVariableName().isEmpty()) + dependencies.append({package->environmentVariableName(), + QDir::toNativeSeparators(package->detectionPath())}); + }; + for (auto package : mcuTarget->packages()) + processPackage(package); + processPackage(qtForMCUsSdkPackage); + + McuDependenciesKitAspect::setDependencies(k, dependencies); + + auto irrelevant = k->irrelevantAspects(); + irrelevant.insert(McuDependenciesKitAspect::id()); + k->setIrrelevantAspects(irrelevant); +} + +static void setKitCMakeOptions(Kit *k, const McuTarget *mcuTarget, const FilePath &qulDir) +{ + using namespace CMakeProjectManager; + + CMakeConfig config = CMakeConfigurationKitAspect::configuration(k); + // CMake ToolChain file for ghs handles CMAKE_*_COMPILER autonomously + if (mcuTarget->toolChainPackage()->type() != McuToolChainPackage::Type::GHS + && mcuTarget->toolChainPackage()->type() != McuToolChainPackage::Type::GHSArm) { + config.append(CMakeConfigItem("CMAKE_CXX_COMPILER", "%{Compiler:Executable:Cxx}")); + config.append(CMakeConfigItem("CMAKE_C_COMPILER", "%{Compiler:Executable:C}")); + } + + if (!mcuTarget->toolChainPackage()->isDesktopToolchain()) { + const FilePath cMakeToolchainFile = qulDir.pathAppended( + "lib/cmake/Qul/toolchain/" + mcuTarget->toolChainPackage()->cmakeToolChainFileName()); + + config.append( + CMakeConfigItem("CMAKE_TOOLCHAIN_FILE", cMakeToolchainFile.toString().toUtf8())); + if (!cMakeToolchainFile.exists()) { + printMessage(McuTarget::tr( + "Warning for target %1: missing CMake toolchain file expected at %2.") + .arg(kitName(mcuTarget), + cMakeToolchainFile.toUserOutput()), + false); + } + } + + const FilePath generatorsPath = qulDir.pathAppended("/lib/cmake/Qul/QulGenerators.cmake"); + config.append(CMakeConfigItem("QUL_GENERATORS", generatorsPath.toString().toUtf8())); + if (!generatorsPath.exists()) { + printMessage(McuTarget::tr("Warning for target %1: missing QulGenerators expected at %2.") + .arg(kitName(mcuTarget), generatorsPath.toUserOutput()), + false); + } + + config.append(CMakeConfigItem("QUL_PLATFORM", mcuTarget->platform().name.toUtf8())); + + if (mcuTarget->colorDepth() != McuTarget::UnspecifiedColorDepth) + config.append(CMakeConfigItem("QUL_COLOR_DEPTH", + QString::number(mcuTarget->colorDepth()).toLatin1())); + if (McuSupportOptions::kitsNeedQtVersion()) + config.append(CMakeConfigItem("CMAKE_PREFIX_PATH", "%{Qt:QT_INSTALL_PREFIX}")); + CMakeConfigurationKitAspect::setConfiguration(k, config); + + if (HostOsInfo::isWindowsHost()) { + auto type = mcuTarget->toolChainPackage()->type(); + if (type == McuToolChainPackage::Type::GHS || type == McuToolChainPackage::Type::GHSArm) { + // See https://bugreports.qt.io/browse/UL-4247?focusedCommentId=565802&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-565802 + // and https://bugreports.qt.io/browse/UL-4247?focusedCommentId=565803&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-565803 + CMakeGeneratorKitAspect::setGenerator(k, "NMake Makefiles JOM"); + } + } +} + +static void setKitQtVersionOptions(Kit *k) +{ + if (!McuSupportOptions::kitsNeedQtVersion()) + QtSupport::QtKitAspect::setQtVersion(k, nullptr); + // else: auto-select a Qt version +} + +QString kitName(const McuTarget *mcuTarget) +{ + const McuToolChainPackage *tcPkg = mcuTarget->toolChainPackage(); + const QString compilerName = tcPkg && !tcPkg->isDesktopToolchain() + ? QString::fromLatin1(" (%1)").arg( + tcPkg->toolChainName().toUpper()) + : ""; + const QString colorDepth = mcuTarget->colorDepth() != McuTarget::UnspecifiedColorDepth + ? QString::fromLatin1(" %1bpp").arg(mcuTarget->colorDepth()) + : ""; + const QString targetName = mcuTarget->platform().displayName.isEmpty() + ? mcuTarget->platform().name + : mcuTarget->platform().displayName; + return QString::fromLatin1("Qt for MCUs %1.%2 - %3%4%5") + .arg(QString::number(mcuTarget->qulVersion().majorVersion()), + QString::number(mcuTarget->qulVersion().minorVersion()), + targetName, + colorDepth, + compilerName); +} + +QList existingKits(const McuTarget *mcuTarget) +{ + using namespace Constants; + return Utils::filtered(KitManager::kits(), [mcuTarget](Kit *kit) { + return kit->value(KIT_MCUTARGET_KITVERSION_KEY) == KIT_VERSION + && (!mcuTarget + || (kit->value(KIT_MCUTARGET_VENDOR_KEY) == mcuTarget->platform().vendor + && kit->value(KIT_MCUTARGET_MODEL_KEY) == mcuTarget->platform().name + && kit->value(KIT_MCUTARGET_COLORDEPTH_KEY) == mcuTarget->colorDepth() + && kit->value(KIT_MCUTARGET_OS_KEY).toInt() + == static_cast(mcuTarget->os()) + && kit->value(KIT_MCUTARGET_TOOCHAIN_KEY) + == mcuTarget->toolChainPackage()->toolChainName())); + }); +} + +QList matchingKits(const McuTarget *mcuTarget, + const McuAbstractPackage *qtForMCUsSdkPackage) +{ + return Utils::filtered(existingKits(mcuTarget), [mcuTarget, qtForMCUsSdkPackage](Kit *kit) { + return kitIsUpToDate(kit, mcuTarget, qtForMCUsSdkPackage); + }); +} + +QList upgradeableKits(const McuTarget *mcuTarget, + const McuAbstractPackage *qtForMCUsSdkPackage) +{ + return Utils::filtered(existingKits(mcuTarget), [mcuTarget, qtForMCUsSdkPackage](Kit *kit) { + return !kitIsUpToDate(kit, mcuTarget, qtForMCUsSdkPackage); + }); +} + +QList kitsWithMismatchedDependencies(const McuTarget *mcuTarget) +{ + return Utils::filtered(existingKits(mcuTarget), [mcuTarget](Kit *kit) { + const auto environment = Utils::NameValueDictionary( + Utils::NameValueItem::toStringList(EnvironmentKitAspect::environmentChanges(kit))); + return Utils::anyOf(mcuTarget->packages(), + [&environment](const McuAbstractPackage *package) { + return !package->environmentVariableName().isEmpty() + && environment.value(package->environmentVariableName()) + != package->path().toUserOutput(); + }); + }); +} + +QList outdatedKits() +{ + return Utils::filtered(KitManager::kits(), [](Kit *kit) { + return !kit->value(Constants::KIT_MCUTARGET_VENDOR_KEY).isNull() + && kit->value(Constants::KIT_MCUTARGET_KITVERSION_KEY) != KIT_VERSION; + }); +} + +void removeOutdatedKits() +{ + for (auto kit : outdatedKits()) + KitManager::deregisterKit(kit); +} + +Kit *newKit(const McuTarget *mcuTarget, const McuAbstractPackage *qtForMCUsSdk) +{ + const auto init = [mcuTarget, qtForMCUsSdk](Kit *k) { + KitGuard kitGuard(k); + + setKitProperties(kitName(mcuTarget), k, mcuTarget, qtForMCUsSdk->path()); + setKitDevice(k, mcuTarget); + setKitToolchains(k, mcuTarget->toolChainPackage()); + setKitDebugger(k, mcuTarget->toolChainPackage()); + McuSupportOptions::setKitEnvironment(k, mcuTarget, qtForMCUsSdk); + setKitDependencies(k, mcuTarget, qtForMCUsSdk); + setKitCMakeOptions(k, mcuTarget, qtForMCUsSdk->path()); + setKitQtVersionOptions(k); + + k->setup(); + k->fix(); + }; + + return KitManager::registerKit(init); +} + +QVersionNumber kitQulVersion(const Kit *kit) +{ + return QVersionNumber::fromString( + kit->value(McuSupport::Constants::KIT_MCUTARGET_SDKVERSION_KEY).toString()); +} + +static FilePath kitDependencyPath(const Kit *kit, const QString &variableName) +{ + for (const NameValueItem &nameValueItem : EnvironmentKitAspect::environmentChanges(kit)) { + if (nameValueItem.name == variableName) + return FilePath::fromUserInput(nameValueItem.value); + } + return FilePath(); +} + +bool kitIsUpToDate(const Kit *kit, + const McuTarget *mcuTarget, + const McuAbstractPackage *qtForMCUsSdkPackage) +{ + return kitQulVersion(kit) == mcuTarget->qulVersion() + && kitDependencyPath(kit, qtForMCUsSdkPackage->environmentVariableName()).toUserOutput() + == qtForMCUsSdkPackage->path().toUserOutput(); +} + +void createAutomaticKits() +{ + auto qtForMCUsPackage = Sdk::createQtForMCUsPackage(); + + const auto createKits = [qtForMCUsPackage]() { + if (McuSupportOptions::automaticKitCreationFromSettings()) { + qtForMCUsPackage->updateStatus(); + if (!qtForMCUsPackage->validStatus()) { + switch (qtForMCUsPackage->status()) { + case McuAbstractPackage::Status::ValidPathInvalidPackage: { + const QString displayPath + = FilePath::fromString(qtForMCUsPackage->detectionPath()).toUserOutput(); + printMessage(McuPackage::tr("Path %1 exists, but does not contain %2.") + .arg(qtForMCUsPackage->path().toUserOutput(), displayPath), + true); + break; + } + case McuAbstractPackage::Status::InvalidPath: { + printMessage(McuPackage::tr("Path %1 does not exist. Add the path in Tools > Options > " + "Devices > MCU.") + .arg(qtForMCUsPackage->path().toUserOutput()), + true); + break; + } + case McuAbstractPackage::Status::EmptyPath: { + printMessage(McuPackage::tr("Missing %1. Add the path in Tools > Options > Devices > MCU.") + .arg(qtForMCUsPackage->detectionPath()), + true); + return; + } + default: + break; + } + return; + } + + if (CMakeProjectManager::CMakeToolManager::cmakeTools().isEmpty()) { + printMessage(McuPackage::tr("No CMake tool was detected. Add a CMake tool in Tools > Options > " + "Kits > CMake."), + true); + return; + } + + FilePath dir = qtForMCUsPackage->path(); + McuSdkRepository repo; + Sdk::targetsAndPackages(dir, &repo); + + bool needsUpgrade = false; + for (const auto &target : qAsConst(repo.mcuTargets)) { + // if kit already exists, skip + if (!matchingKits(target, qtForMCUsPackage).empty()) + continue; + if (!upgradeableKits(target, qtForMCUsPackage).empty()) { + // if kit exists but wrong version/path + needsUpgrade = true; + } else { + // if no kits for this target, create + if (target->isValid()) + newKit(target, qtForMCUsPackage); + target->printPackageProblems(); + } + } + + repo.deletePackagesAndTargets(); + + if (needsUpgrade) + McuSupportPlugin::askUserAboutMcuSupportKitsUpgrade(); + } + }; + + createKits(); + delete qtForMCUsPackage; +} + +void upgradeKitsByCreatingNewPackage(UpgradeOption upgradeOption) +{ + if (upgradeOption == UpgradeOption::Ignore) + return; + + auto qtForMCUsPackage = Sdk::createQtForMCUsPackage(); + + auto dir = qtForMCUsPackage->path(); + McuSdkRepository repo; + Sdk::targetsAndPackages(dir, &repo); + + for (const auto &target : qAsConst(repo.mcuTargets)) { + if (!matchingKits(target, qtForMCUsPackage).empty()) + // already up-to-date + continue; + + const auto kits = upgradeableKits(target, qtForMCUsPackage); + if (!kits.empty()) { + if (upgradeOption == UpgradeOption::Replace) { + for (auto existingKit : kits) + KitManager::deregisterKit(existingKit); + } + + if (target->isValid()) + newKit(target, qtForMCUsPackage); + target->printPackageProblems(); + } + } + + repo.deletePackagesAndTargets(); + delete qtForMCUsPackage; +} + +void upgradeKitInPlace(ProjectExplorer::Kit *kit, + const McuTarget *mcuTarget, + const McuAbstractPackage *qtForMCUsSdk) +{ + setKitProperties(kitName(mcuTarget), kit, mcuTarget, qtForMCUsSdk->path()); + McuSupportOptions::setKitEnvironment(kit, mcuTarget, qtForMCUsSdk); + setKitDependencies(kit, mcuTarget, qtForMCUsSdk); +} + +void fixKitsDependencies() +{ + auto qtForMCUsPackage = Sdk::createQtForMCUsPackage(); + + FilePath dir = qtForMCUsPackage->path(); + McuSdkRepository repo; + Sdk::targetsAndPackages(dir, &repo); + for (const auto &target : qAsConst(repo.mcuTargets)) { + if (target->isValid()) { + for (auto *kit : kitsWithMismatchedDependencies(target)) { + McuSupportOptions::updateKitEnvironment(kit, target); + } + } + } + + repo.deletePackagesAndTargets(); + delete qtForMCUsPackage; +} + +/** + * @brief Fix/update existing kits if needed + */ +void fixExistingKits() +{ + for (Kit *kit : KitManager::kits()) { + if (!kit->hasValue(Constants::KIT_MCUTARGET_KITVERSION_KEY)) + continue; + + if (kit->isAutoDetected()) { + kit->setAutoDetected(false); + } + + // Check if the MCU kits are flagged as supplying a QtQuick import path, in order + // to tell the QMLJS code-model that it won't need to add a fall-back import + // path. + const auto bringsQtQuickImportPath = QtSupport::SuppliesQtQuickImportPath::id(); + auto irrelevantAspects = kit->irrelevantAspects(); + if (!irrelevantAspects.contains(bringsQtQuickImportPath)) { + irrelevantAspects.insert(bringsQtQuickImportPath); + kit->setIrrelevantAspects(irrelevantAspects); + } + if (!kit->hasValue(bringsQtQuickImportPath)) { + kit->setValue(bringsQtQuickImportPath, true); + } + + // Check if the MCU kit supplies its import path. + const auto kitQmlImportPath = QtSupport::KitQmlImportPath::id(); + if (!irrelevantAspects.contains(kitQmlImportPath)) { + irrelevantAspects.insert(kitQmlImportPath); + kit->setIrrelevantAspects(irrelevantAspects); + } + if (!kit->hasValue(kitQmlImportPath)) { + auto config = CMakeProjectManager::CMakeConfigurationKitAspect::configuration(kit); + for (const auto &cfgItem : qAsConst(config)) { + if (cfgItem.key == "QUL_GENERATORS") { + auto idx = cfgItem.value.indexOf("/lib/cmake/Qul"); + auto qulDir = cfgItem.value.left(idx); + kit->setValue(kitQmlImportPath, QVariant(qulDir + "/include/qul")); + break; + } + } + } + + // Check if the MCU kit has the flag for merged header/qml-import paths set. + const auto mergedPaths = QtSupport::KitHasMergedHeaderPathsWithQmlImportPaths::id(); + if (!irrelevantAspects.contains(mergedPaths)) { + irrelevantAspects.insert(mergedPaths); + kit->setIrrelevantAspects(irrelevantAspects); + } + if (!kit->value(mergedPaths, false).toBool()) { + kit->setValue(mergedPaths, true); + } + } + + // Fix kit dependencies for known targets + auto qtForMCUsPackage = Sdk::createQtForMCUsPackage(); + qtForMCUsPackage->updateStatus(); + if (qtForMCUsPackage->validStatus()) { + FilePath dir = qtForMCUsPackage->path(); + McuSdkRepository repo; + Sdk::targetsAndPackages(dir, &repo); + for (const auto &target : qAsConst(repo.mcuTargets)) + for (auto kit : existingKits(target)) { + if (McuDependenciesKitAspect::dependencies(kit).isEmpty()) { + setKitDependencies(kit, target, qtForMCUsPackage); + } + } + + repo.deletePackagesAndTargets(); + } + delete qtForMCUsPackage; +} + +} // namespace McuKitManager +} // namespace Internal +} // namespace McuSupport diff --git a/src/plugins/mcusupport/mcukitmanager.h b/src/plugins/mcusupport/mcukitmanager.h new file mode 100644 index 00000000000..1814fb26432 --- /dev/null +++ b/src/plugins/mcusupport/mcukitmanager.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include +#include "mcusupport_global.h" + +#include +#include +#include +#include + +namespace ProjectExplorer { +class Kit; +} // namespace ProjectExplorer + +namespace McuSupport { +namespace Internal { + +class McuAbstractPackage; +class McuToolChainPackage; +class McuTarget; + +namespace McuKitManager +{ + enum class UpgradeOption { + Ignore, + Keep, + Replace + }; + + // Creating kits: + ProjectExplorer::Kit *newKit(const McuTarget *mcuTarget, const McuAbstractPackage *qtForMCUsSdk); + void createAutomaticKits(); + + // Querying the kits: + QList existingKits(const McuTarget *mcuTarget); + QList matchingKits(const McuTarget *mcuTarget, + const McuAbstractPackage *qtForMCUsSdkPackage); + QList upgradeableKits( + const McuTarget *mcuTarget, const McuAbstractPackage *qtForMCUsSdkPackage); + QList kitsWithMismatchedDependencies(const McuTarget *mcuTarget); + + // Upgrading kits: + void upgradeKitsByCreatingNewPackage(UpgradeOption upgradeOption); + void upgradeKitInPlace(ProjectExplorer::Kit *kit, const McuTarget *mcuTarget, const McuAbstractPackage *qtForMCUsSdk); + + // Fixing kits: + void fixKitsDependencies(); + void fixExistingKits(); + + // Outdated kits: + QList outdatedKits(); + void removeOutdatedKits(); + + // Querying kits: + QString kitName(const McuTarget* mcuTarget); + QVersionNumber kitQulVersion(const ProjectExplorer::Kit *kit); + bool kitIsUpToDate(const ProjectExplorer::Kit *kit, const McuTarget *mcuTarget, const McuAbstractPackage *qtForMCUsSdkPackage); + +} // namespace McuKitManager +} // namespace Internal +} // namespace McuSupport diff --git a/src/plugins/mcusupport/mcupackage.cpp b/src/plugins/mcusupport/mcupackage.cpp index 4e5521e06b0..f108ca67f83 100644 --- a/src/plugins/mcusupport/mcupackage.cpp +++ b/src/plugins/mcusupport/mcupackage.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2020 The Qt Company Ltd. +** Copyright (C) 2022 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt Creator. @@ -25,6 +25,7 @@ #include "mcupackage.h" #include "mcusupportconstants.h" +#include "mcusupportversiondetection.h" #include "mcusupportsdk.h" #include @@ -40,15 +41,6 @@ using namespace Utils; namespace McuSupport::Internal { -static bool automaticKitCreationFromSettings(QSettings::Scope scope = QSettings::UserScope) -{ - QSettings *settings = Core::ICore::settings(scope); - const QString key = QLatin1String(Constants::SETTINGS_GROUP) + '/' - + QLatin1String(Constants::SETTINGS_KEY_AUTOMATIC_KIT_CREATION); - bool automaticKitCreation = settings->value(key, true).toBool(); - return automaticKitCreation; -} - McuPackage::McuPackage(const QString &label, const FilePath &defaultPath, const QString &detectionPath, @@ -65,7 +57,6 @@ McuPackage::McuPackage(const QString &label, , m_downloadUrl(downloadUrl) { m_path = Sdk::packagePathFromSettings(settingsKey, QSettings::UserScope, m_defaultPath); - m_automaticKitCreation = automaticKitCreationFromSettings(QSettings::UserScope); } FilePath McuPackage::basePath() const @@ -161,14 +152,6 @@ bool McuPackage::addToPath() const return m_addToPath; } -void McuPackage::writeGeneralSettings() const -{ - const QString key = QLatin1String(Constants::SETTINGS_GROUP) + '/' - + QLatin1String(Constants::SETTINGS_KEY_AUTOMATIC_KIT_CREATION); - QSettings *settings = Core::ICore::settings(); - settings->setValue(key, m_automaticKitCreation); -} - bool McuPackage::writeToSettings() const { const FilePath savedPath = Sdk::packagePathFromSettings(m_settingsKey, @@ -191,16 +174,6 @@ void McuPackage::setVersions(const QStringList &versions) m_versions = versions; } -bool McuPackage::automaticKitCreationEnabled() const -{ - return m_automaticKitCreation; -} - -void McuPackage::setAutomaticKitCreationEnabled(const bool enabled) -{ - m_automaticKitCreation = enabled; -} - void McuPackage::updatePath() { m_path = m_fileChooser->rawFilePath(); @@ -269,9 +242,10 @@ QString McuPackage::statusText() const .arg(displayPackagePath, displayDetectedPath); break; case Status::ValidPackageMismatchedVersion: { - const QString versionWarning = m_versions.size() == 1 ? - tr("but only version %1 is supported").arg(m_versions.first()) : - tr("but only versions %1 are supported").arg(displayVersions); + const QString versionWarning + = m_versions.size() == 1 + ? tr("but only version %1 is supported").arg(m_versions.first()) + : tr("but only versions %1 are supported").arg(displayVersions); response = tr("Path %1 is valid, %2 was found, %3.") .arg(displayPackagePath, displayDetectedPath, versionWarning); break; @@ -313,4 +287,5 @@ bool McuToolChainPackage::isDesktopToolchain() const return m_type == Type::MSVC || m_type == Type::GCC; } -} // namespace McuSupport::Internal + +} // namespace McuSupport::Internal diff --git a/src/plugins/mcusupport/mcupackage.h b/src/plugins/mcusupport/mcupackage.h index b7d415fd3f3..dcc9917c1ce 100644 --- a/src/plugins/mcusupport/mcupackage.h +++ b/src/plugins/mcusupport/mcupackage.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2020 The Qt Company Ltd. +** Copyright (C) 2022 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt Creator. @@ -26,10 +26,8 @@ #pragma once #include "mcuabstractpackage.h" -#include "mcusupportversiondetection.h" #include -#include #include @@ -42,11 +40,14 @@ class ToolChain; namespace Utils { class PathChooser; class InfoLabel; +class Id; } // namespace Utils namespace McuSupport { namespace Internal { +class McuPackageVersionDetector; + class McuPackage : public McuAbstractPackage { Q_OBJECT @@ -73,23 +74,14 @@ public: bool validStatus() const override; void setAddToPath(bool addToPath) override; bool addToPath() const override; - void writeGeneralSettings() const override; bool writeToSettings() const override; void setRelativePathModifier(const QString &path) override; void setVersions(const QStringList &versions) override; - //TODO(piotr.mucko): Why every package knows about automatic kit creation. This should be outside of this class. - bool automaticKitCreationEnabled() const override; - void setAutomaticKitCreationEnabled(const bool enabled) override; - QWidget *widget() override; const QString &environmentVariableName() const override; -signals: - void changed(); - void statusChanged(); - private: void updatePath(); void updateStatusUi(); @@ -111,7 +103,6 @@ private: const QString m_environmentVariableName; const QString m_downloadUrl; bool m_addToPath = false; - bool m_automaticKitCreation = true; Status m_status = Status::InvalidPath; }; diff --git a/src/plugins/mcusupport/mcusupport.qbs b/src/plugins/mcusupport/mcusupport.qbs index 1710166a975..b7b8e146102 100644 --- a/src/plugins/mcusupport/mcusupport.qbs +++ b/src/plugins/mcusupport/mcusupport.qbs @@ -18,6 +18,8 @@ QtcPlugin { "mcuabstractpackage.h", "mcupackage.cpp", "mcupackage.h", + "mcutarget.cpp", + "mcutarget.h", "mcusupport.qrc", "mcusupport_global.h", "mcusupportconstants.h", @@ -25,6 +27,8 @@ QtcPlugin { "mcusupportdevice.h", "mcusupportoptions.cpp", "mcusupportoptions.h", + "mcukitmanager.cpp", + "mcukitmanager.h", "mcusupportoptionspage.cpp", "mcusupportoptionspage.h", "mcusupportplugin.cpp", diff --git a/src/plugins/mcusupport/mcusupportoptions.cpp b/src/plugins/mcusupport/mcusupportoptions.cpp index e0640b155d6..f47a3ddf951 100644 --- a/src/plugins/mcusupport/mcusupportoptions.cpp +++ b/src/plugins/mcusupport/mcusupportoptions.cpp @@ -24,47 +24,27 @@ ****************************************************************************/ #include "mcusupportoptions.h" -#include "mcukitinformation.h" + #include "mcupackage.h" +#include "mcutarget.h" +#include "mcukitmanager.h" +#include "mcukitinformation.h" #include "mcusupportcmakemapper.h" #include "mcusupportconstants.h" -#include "mcusupportplugin.h" #include "mcusupportsdk.h" +#include "mcusupportplugin.h" -#include #include #include #include #include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include #include #include -#include -#include -#include -#include #include #include -#include -#include -#include using CMakeProjectManager::CMakeConfigItem; using CMakeProjectManager::CMakeConfigurationKitAspect; @@ -74,263 +54,6 @@ using namespace Utils; namespace McuSupport { namespace Internal { -static const int KIT_VERSION = 9; // Bumps up whenever details in Kit creation change - -static bool kitNeedsQtVersion() -{ - // Only on Windows, Qt is linked into the distributed qul Desktop libs. Also, the host tools - // are missing the Qt runtime libraries on non-Windows. - return !HostOsInfo::isWindowsHost(); -} - -static ToolChain *msvcToolChain(Id language) -{ - ToolChain *toolChain = ToolChainManager::toolChain([language](const ToolChain *t) { - const Abi abi = t->targetAbi(); - // TODO: Should Abi::WindowsMsvc2022Flavor be added too? - return (abi.osFlavor() == Abi::WindowsMsvc2017Flavor - || abi.osFlavor() == Abi::WindowsMsvc2019Flavor) - && abi.architecture() == Abi::X86Architecture && abi.wordWidth() == 64 - && t->language() == language; - }); - return toolChain; -} - -static ToolChain *gccToolChain(Id language) -{ - ToolChain *toolChain = ToolChainManager::toolChain([language](const ToolChain *t) { - const Abi abi = t->targetAbi(); - return abi.os() != Abi::WindowsOS && abi.architecture() == Abi::X86Architecture - && abi.wordWidth() == 64 && t->language() == language; - }); - return toolChain; -} - -static ToolChain *armGccToolChain(const FilePath &path, Id language) -{ - ToolChain *toolChain = ToolChainManager::toolChain([&path, language](const ToolChain *t) { - return t->compilerCommand() == path && t->language() == language; - }); - if (!toolChain) { - ToolChainFactory *gccFactory - = Utils::findOrDefault(ToolChainFactory::allToolChainFactories(), - [](ToolChainFactory *f) { - return f->supportedToolChainType() - == ProjectExplorer::Constants::GCC_TOOLCHAIN_TYPEID; - }); - if (gccFactory) { - const QList detected = gccFactory->detectForImport({path, language}); - if (!detected.isEmpty()) { - toolChain = detected.first(); - toolChain->setDetection(ToolChain::ManualDetection); - toolChain->setDisplayName("Arm GCC"); - ToolChainManager::registerToolChain(toolChain); - } - } - } - - return toolChain; -} - -static ToolChain *iarToolChain(const FilePath &path, Id language) -{ - ToolChain *toolChain = ToolChainManager::toolChain([language](const ToolChain *t) { - return t->typeId() == BareMetal::Constants::IAREW_TOOLCHAIN_TYPEID - && t->language() == language; - }); - if (!toolChain) { - ToolChainFactory *iarFactory - = Utils::findOrDefault(ToolChainFactory::allToolChainFactories(), - [](ToolChainFactory *f) { - return f->supportedToolChainType() - == BareMetal::Constants::IAREW_TOOLCHAIN_TYPEID; - }); - if (iarFactory) { - Toolchains detected = iarFactory->autoDetect(ToolchainDetector({}, {})); - if (detected.isEmpty()) - detected = iarFactory->detectForImport({path, language}); - for (auto tc : detected) { - if (tc->language() == language) { - toolChain = tc; - toolChain->setDetection(ToolChain::ManualDetection); - toolChain->setDisplayName("IAREW"); - ToolChainManager::registerToolChain(toolChain); - } - } - } - } - - return toolChain; -} - -ToolChain *McuToolChainPackage::toolChain(Id language) const -{ - switch (m_type) { - case Type::MSVC: - return msvcToolChain(language); - case Type::GCC: - return gccToolChain(language); - case Type::IAR: { - const FilePath compiler = path().pathAppended("/bin/iccarm").withExecutableSuffix(); - return iarToolChain(compiler, language); - } - case Type::ArmGcc: - case Type::KEIL: - case Type::GHS: - case Type::GHSArm: - case Type::Unsupported: { - const QLatin1String compilerName( - language == ProjectExplorer::Constants::C_LANGUAGE_ID ? "gcc" : "g++"); - const QString comp = QLatin1String(m_type == Type::ArmGcc ? "/bin/arm-none-eabi-%1" - : "/bar/foo-keil-%1") - .arg(compilerName); - const FilePath compiler = path().pathAppended(comp).withExecutableSuffix(); - - return armGccToolChain(compiler, language); - } - default: - Q_UNREACHABLE(); - } -} - -QString McuToolChainPackage::toolChainName() const -{ - switch (m_type) { - case Type::ArmGcc: - return QLatin1String("armgcc"); - case Type::IAR: - return QLatin1String("iar"); - case Type::KEIL: - return QLatin1String("keil"); - case Type::GHS: - return QLatin1String("ghs"); - case Type::GHSArm: - return QLatin1String("ghs-arm"); - default: - return QLatin1String("unsupported"); - } -} - -QString McuToolChainPackage::cmakeToolChainFileName() const -{ - return toolChainName() + QLatin1String(".cmake"); -} - -QVariant McuToolChainPackage::debuggerId() const -{ - using namespace Debugger; - - QString sub, displayName; - DebuggerEngineType engineType; - - switch (m_type) { - case Type::ArmGcc: { - sub = QString::fromLatin1("bin/arm-none-eabi-gdb-py"); - displayName = McuPackage::tr("Arm GDB at %1"); - engineType = Debugger::GdbEngineType; - break; - } - case Type::IAR: { - sub = QString::fromLatin1("../common/bin/CSpyBat"); - displayName = QLatin1String("CSpy"); - engineType = Debugger::NoEngineType; // support for IAR missing - break; - } - case Type::KEIL: { - sub = QString::fromLatin1("UV4/UV4"); - displayName = QLatin1String("KEIL uVision Debugger"); - engineType = Debugger::UvscEngineType; - break; - } - default: - return QVariant(); - } - - const FilePath command = path().pathAppended(sub).withExecutableSuffix(); - if (const DebuggerItem *debugger = DebuggerItemManager::findByCommand(command)) { - return debugger->id(); - } - - DebuggerItem newDebugger; - newDebugger.setCommand(command); - newDebugger.setUnexpandedDisplayName(displayName.arg(command.toUserOutput())); - newDebugger.setEngineType(engineType); - return DebuggerItemManager::registerDebugger(newDebugger); -} - -McuTarget::McuTarget(const QVersionNumber &qulVersion, - const Platform &platform, - OS os, - const QVector &packages, - const McuToolChainPackage *toolChainPackage, - int colorDepth) - : m_qulVersion(qulVersion) - , m_platform(platform) - , m_os(os) - , m_packages(packages) - , m_toolChainPackage(toolChainPackage) - , m_colorDepth(colorDepth) -{} - -const QVector &McuTarget::packages() const -{ - return m_packages; -} - -const McuToolChainPackage *McuTarget::toolChainPackage() const -{ - return m_toolChainPackage; -} - -McuTarget::OS McuTarget::os() const -{ - return m_os; -} - -const McuTarget::Platform &McuTarget::platform() const -{ - return m_platform; -} - -bool McuTarget::isValid() const -{ - return Utils::allOf(packages(), [](McuAbstractPackage *package) { - package->updateStatus(); - return package->validStatus(); - }); -} - -void McuTarget::printPackageProblems() const -{ - for (auto package : packages()) { - package->updateStatus(); - if (!package->validStatus()) { - printMessage(tr("Error creating kit for target %1, package %2: %3") - .arg(McuSupportOptions::kitName(this), - package->label(), - package->statusText()), - true); - } - if (package->status() == McuAbstractPackage::Status::ValidPackageMismatchedVersion) { - printMessage(tr("Warning creating kit for target %1, package %2: %3") - .arg(McuSupportOptions::kitName(this), - package->label(), - package->statusText()), - false); - } - } -} - -const QVersionNumber &McuTarget::qulVersion() const -{ - return m_qulVersion; -} - -int McuTarget::colorDepth() const -{ - return m_colorDepth; -} - void McuSdkRepository::deletePackagesAndTargets() { qDeleteAll(packages); @@ -347,6 +70,7 @@ McuSupportOptions::McuSupportOptions(QObject *parent) &McuAbstractPackage::changed, this, &McuSupportOptions::populatePackagesAndTargets); + m_automaticKitCreation = automaticKitCreationFromSettings(); } McuSupportOptions::~McuSupportOptions() @@ -402,7 +126,7 @@ void McuSupportOptions::registerExamples() const QVersionNumber &McuSupportOptions::minimalQulVersion() { - static const QVersionNumber v({1, 3}); + static const QVersionNumber v({2, 0}); return v; } @@ -413,48 +137,16 @@ void McuSupportOptions::setQulDir(const FilePath &dir) if (qtForMCUsSdkPackage->validStatus()) Sdk::targetsAndPackages(dir, &sdkRepository); for (const auto &package : qAsConst(sdkRepository.packages)) - connect(package, &McuAbstractPackage::changed, this, &McuSupportOptions::changed); + connect(package, &McuAbstractPackage::changed, this, &McuSupportOptions::packagesChanged); - emit changed(); + emit packagesChanged(); } FilePath McuSupportOptions::qulDirFromSettings() { return Sdk::packagePathFromSettings(Constants::SETTINGS_KEY_PACKAGE_QT_FOR_MCUS_SDK, - QSettings::UserScope); -} - -static void setKitProperties(const QString &kitName, - Kit *k, - const McuTarget *mcuTarget, - const FilePath &sdkPath) -{ - using namespace Constants; - - k->setUnexpandedDisplayName(kitName); - k->setValue(KIT_MCUTARGET_VENDOR_KEY, mcuTarget->platform().vendor); - k->setValue(KIT_MCUTARGET_MODEL_KEY, mcuTarget->platform().name); - k->setValue(KIT_MCUTARGET_COLORDEPTH_KEY, mcuTarget->colorDepth()); - k->setValue(KIT_MCUTARGET_SDKVERSION_KEY, mcuTarget->qulVersion().toString()); - k->setValue(KIT_MCUTARGET_KITVERSION_KEY, KIT_VERSION); - k->setValue(KIT_MCUTARGET_OS_KEY, static_cast(mcuTarget->os())); - k->setValue(KIT_MCUTARGET_TOOCHAIN_KEY, mcuTarget->toolChainPackage()->toolChainName()); - k->setAutoDetected(false); - k->makeSticky(); - if (mcuTarget->toolChainPackage()->isDesktopToolchain()) - k->setDeviceTypeForIcon(DEVICE_TYPE); - k->setValue(QtSupport::SuppliesQtQuickImportPath::id(), true); - k->setValue(QtSupport::KitQmlImportPath::id(), sdkPath.pathAppended("include/qul").toVariant()); - k->setValue(QtSupport::KitHasMergedHeaderPathsWithQmlImportPaths::id(), true); - QSet irrelevant = { - SysRootKitAspect::id(), - QtSupport::SuppliesQtQuickImportPath::id(), - QtSupport::KitQmlImportPath::id(), - QtSupport::KitHasMergedHeaderPathsWithQmlImportPaths::id(), - }; - if (!kitNeedsQtVersion()) - irrelevant.insert(QtSupport::QtKitAspect::id()); - k->setIrrelevantAspects(irrelevant); + QSettings::UserScope, + {}); } void McuSupportOptions::remapQul2xCmakeVars(Kit *kit, const EnvironmentItems &envItems) @@ -465,14 +157,13 @@ void McuSupportOptions::remapQul2xCmakeVars(Kit *kit, const EnvironmentItems &en // First filter out all Qul2.x CMake vars auto config = Utils::filtered(CMakeConfigurationKitAspect::configuration(kit), [&](const auto &configItem) { - return !cmakeVarNames.contains(configItem.key); - }); + return !cmakeVarNames.contains(configItem.key); + }); // Then append them with new values config.append(cmakeVars); CMakeConfigurationKitAspect::setConfiguration(kit, config); } - static void setKitToolchains(Kit *k, const McuToolChainPackage *tcPackage) { switch (tcPackage->type()) { @@ -569,15 +260,7 @@ void McuSupportOptions::setKitEnvironment(Kit *k, processPackage(package); processPackage(qtForMCUsSdkPackage); - // Clang not needed in version 1.7+ - if (mcuTarget->qulVersion() < QVersionNumber{1, 7}) { - const QString path = QLatin1String(HostOsInfo::isWindowsHost() ? "Path" : "PATH"); - pathAdditions.append("${" + path + "}"); - pathAdditions.append(Core::ICore::libexecPath("clang/bin").toUserOutput()); - changes.append({path, pathAdditions.join(HostOsInfo::pathListSeparator())}); - } - - if (kitNeedsQtVersion()) + if (McuSupportOptions::kitsNeedQtVersion()) changes.append({QLatin1String("LD_LIBRARY_PATH"), "%{Qt:QT_INSTALL_LIBS}"}); // Hack, this problem should be solved in lower layer @@ -657,7 +340,7 @@ static void setKitCMakeOptions(Kit *k, const McuTarget *mcuTarget, const FilePat if (!cMakeToolchainFile.exists()) { printMessage(McuTarget::tr( "Warning for target %1: missing CMake toolchain file expected at %2.") - .arg(McuSupportOptions::kitName(mcuTarget), + .arg(McuKitManager::kitName(mcuTarget), cMakeToolchainFile.toUserOutput()), false); } @@ -667,19 +350,16 @@ static void setKitCMakeOptions(Kit *k, const McuTarget *mcuTarget, const FilePat config.append(CMakeConfigItem("QUL_GENERATORS", generatorsPath.toString().toUtf8())); if (!generatorsPath.exists()) { printMessage(McuTarget::tr("Warning for target %1: missing QulGenerators expected at %2.") - .arg(McuSupportOptions::kitName(mcuTarget), generatorsPath.toUserOutput()), + .arg(McuKitManager::kitName(mcuTarget), generatorsPath.toUserOutput()), false); } config.append(CMakeConfigItem("QUL_PLATFORM", mcuTarget->platform().name.toUtf8())); - if (mcuTarget->qulVersion() <= QVersionNumber{1, 3} // OS variable was removed in Qul 1.4 - && mcuTarget->os() == McuTarget::OS::FreeRTOS) - config.append(CMakeConfigItem("OS", "FreeRTOS")); if (mcuTarget->colorDepth() != McuTarget::UnspecifiedColorDepth) config.append(CMakeConfigItem("QUL_COLOR_DEPTH", QString::number(mcuTarget->colorDepth()).toLatin1())); - if (kitNeedsQtVersion()) + if (McuSupportOptions::kitsNeedQtVersion()) config.append(CMakeConfigItem("CMAKE_PREFIX_PATH", "%{Qt:QT_INSTALL_PREFIX}")); CMakeConfigurationKitAspect::setConfiguration(k, config); @@ -695,134 +375,11 @@ static void setKitCMakeOptions(Kit *k, const McuTarget *mcuTarget, const FilePat static void setKitQtVersionOptions(Kit *k) { - if (!kitNeedsQtVersion()) + if (!McuSupportOptions::kitsNeedQtVersion()) QtSupport::QtKitAspect::setQtVersion(k, nullptr); // else: auto-select a Qt version } -QString McuSupportOptions::kitName(const McuTarget *mcuTarget) -{ - QString os; - if (mcuTarget->qulVersion() <= QVersionNumber{1, 3} - && mcuTarget->os() == McuTarget::OS::FreeRTOS) - // Starting from Qul 1.4 each OS is a separate platform - os = QLatin1String(" FreeRTOS"); - - const McuToolChainPackage *tcPkg = mcuTarget->toolChainPackage(); - const QString compilerName = tcPkg && !tcPkg->isDesktopToolchain() - ? QString::fromLatin1(" (%1)").arg( - tcPkg->toolChainName().toUpper()) - : ""; - const QString colorDepth = mcuTarget->colorDepth() != McuTarget::UnspecifiedColorDepth - ? QString::fromLatin1(" %1bpp").arg(mcuTarget->colorDepth()) - : ""; - const QString targetName = mcuTarget->platform().displayName.isEmpty() - ? mcuTarget->platform().name - : mcuTarget->platform().displayName; - return QString::fromLatin1("Qt for MCUs %1.%2 - %3%4%5%6") - .arg(QString::number(mcuTarget->qulVersion().majorVersion()), - QString::number(mcuTarget->qulVersion().minorVersion()), - targetName, - os, - colorDepth, - compilerName); -} - -QList McuSupportOptions::existingKits(const McuTarget *mcuTarget) -{ - using namespace Constants; - return Utils::filtered(KitManager::kits(), [mcuTarget](Kit *kit) { - return kit->value(KIT_MCUTARGET_KITVERSION_KEY) == KIT_VERSION - && (!mcuTarget - || (kit->value(KIT_MCUTARGET_VENDOR_KEY) == mcuTarget->platform().vendor - && kit->value(KIT_MCUTARGET_MODEL_KEY) == mcuTarget->platform().name - && kit->value(KIT_MCUTARGET_COLORDEPTH_KEY) == mcuTarget->colorDepth() - && kit->value(KIT_MCUTARGET_OS_KEY).toInt() - == static_cast(mcuTarget->os()) - && kit->value(KIT_MCUTARGET_TOOCHAIN_KEY) - == mcuTarget->toolChainPackage()->toolChainName())); - }); -} - -QList McuSupportOptions::matchingKits(const McuTarget *mcuTarget, - const McuAbstractPackage *qtForMCUsSdkPackage) -{ - return Utils::filtered(existingKits(mcuTarget), [mcuTarget, qtForMCUsSdkPackage](Kit *kit) { - return kitUpToDate(kit, mcuTarget, qtForMCUsSdkPackage); - }); -} - -QList McuSupportOptions::upgradeableKits(const McuTarget *mcuTarget, - const McuAbstractPackage *qtForMCUsSdkPackage) -{ - return Utils::filtered(existingKits(mcuTarget), [mcuTarget, qtForMCUsSdkPackage](Kit *kit) { - return !kitUpToDate(kit, mcuTarget, qtForMCUsSdkPackage); - }); -} - -QList McuSupportOptions::kitsWithMismatchedDependencies(const McuTarget *mcuTarget) -{ - return Utils::filtered(existingKits(mcuTarget), [mcuTarget](Kit *kit) { - const auto environment = Utils::NameValueDictionary( - Utils::NameValueItem::toStringList(EnvironmentKitAspect::environmentChanges(kit))); - return Utils::anyOf(mcuTarget->packages(), [&environment](const McuAbstractPackage *package) { - return !package->environmentVariableName().isEmpty() && - environment.value(package->environmentVariableName()) != package->path().toUserOutput(); - }); - }); -} - -QList McuSupportOptions::outdatedKits() -{ - return Utils::filtered(KitManager::kits(), [](Kit *kit) { - return !kit->value(Constants::KIT_MCUTARGET_VENDOR_KEY).isNull() - && kit->value(Constants::KIT_MCUTARGET_KITVERSION_KEY) != KIT_VERSION; - }); -} - -void McuSupportOptions::removeOutdatedKits() -{ - for (auto kit : McuSupportOptions::outdatedKits()) - KitManager::deregisterKit(kit); -} - -Kit *McuSupportOptions::newKit(const McuTarget *mcuTarget, const McuAbstractPackage *qtForMCUsSdk) -{ - const auto init = [mcuTarget, qtForMCUsSdk](Kit *k) { - KitGuard kitGuard(k); - - setKitProperties(kitName(mcuTarget), k, mcuTarget, qtForMCUsSdk->path()); - setKitDevice(k, mcuTarget); - setKitToolchains(k, mcuTarget->toolChainPackage()); - setKitDebugger(k, mcuTarget->toolChainPackage()); - setKitEnvironment(k, mcuTarget, qtForMCUsSdk); - setKitDependencies(k, mcuTarget, qtForMCUsSdk); - setKitCMakeOptions(k, mcuTarget, qtForMCUsSdk->path()); - setKitQtVersionOptions(k); - - k->setup(); - k->fix(); - }; - - return KitManager::registerKit(init); -} - -void printMessage(const QString &message, bool important) -{ - const QString displayMessage = QCoreApplication::translate("QtForMCUs", "Qt for MCUs: %1") - .arg(message); - if (important) - Core::MessageManager::writeFlashing(displayMessage); - else - Core::MessageManager::writeSilently(displayMessage); -} - -QVersionNumber McuSupportOptions::kitQulVersion(const Kit *kit) -{ - return QVersionNumber::fromString( - kit->value(McuSupport::Constants::KIT_MCUTARGET_SDKVERSION_KEY).toString()); -} - static FilePath kitDependencyPath(const Kit *kit, const QString &variableName) { for (const NameValueItem &nameValueItem : EnvironmentKitAspect::environmentChanges(kit)) { @@ -832,21 +389,7 @@ static FilePath kitDependencyPath(const Kit *kit, const QString &variableName) return FilePath(); } -bool McuSupportOptions::kitUpToDate(const Kit *kit, - const McuTarget *mcuTarget, - const McuAbstractPackage *qtForMCUsSdkPackage) -{ - return kitQulVersion(kit) == mcuTarget->qulVersion() - && kitDependencyPath(kit, qtForMCUsSdkPackage->environmentVariableName()).toUserOutput() - == qtForMCUsSdkPackage->path().toUserOutput(); -} - -void McuSupportOptions::deletePackagesAndTargets() -{ - sdkRepository.deletePackagesAndTargets(); -} - -McuSupportOptions::UpgradeOption McuSupportOptions::askForKitUpgrades() +McuKitManager::UpgradeOption McuSupportOptions::askForKitUpgrades() { QMessageBox upgradePopup(Core::ICore::dialogParent()); upgradePopup.setStandardButtons(QMessageBox::Cancel); @@ -859,86 +402,17 @@ McuSupportOptions::UpgradeOption McuSupportOptions::askForKitUpgrades() upgradePopup.exec(); if (upgradePopup.clickedButton() == keepButton) - return Keep; + return McuKitManager::UpgradeOption::Keep; if (upgradePopup.clickedButton() == replaceButton) - return Replace; + return McuKitManager::UpgradeOption::Replace; - return Ignore; + return McuKitManager::UpgradeOption::Ignore; } -void McuSupportOptions::createAutomaticKits() +void McuSupportOptions::deletePackagesAndTargets() { - auto qtForMCUsPackage = Sdk::createQtForMCUsPackage(); - - const auto createKits = [qtForMCUsPackage]() { - if (qtForMCUsPackage->automaticKitCreationEnabled()) { - qtForMCUsPackage->updateStatus(); - if (!qtForMCUsPackage->validStatus()) { - switch (qtForMCUsPackage->status()) { - case McuAbstractPackage::Status::ValidPathInvalidPackage: { - const QString displayPath = FilePath::fromString(qtForMCUsPackage->detectionPath()) - .toUserOutput(); - printMessage(tr("Path %1 exists, but does not contain %2.") - .arg(qtForMCUsPackage->path().toUserOutput(), displayPath), - true); - break; - } - case McuAbstractPackage::Status::InvalidPath: { - printMessage(tr("Path %1 does not exist. Add the path in Tools > Options > " - "Devices > MCU.") - .arg(qtForMCUsPackage->path().toUserOutput()), - true); - break; - } - case McuAbstractPackage::Status::EmptyPath: { - printMessage(tr("Missing %1. Add the path in Tools > Options > Devices > MCU.") - .arg(qtForMCUsPackage->detectionPath()), - true); - return; - } - default: - break; - } - return; - } - - if (CMakeProjectManager::CMakeToolManager::cmakeTools().isEmpty()) { - printMessage(tr("No CMake tool was detected. Add a CMake tool in Tools > Options > " - "Kits > CMake."), - true); - return; - } - - FilePath dir = qtForMCUsPackage->path(); - McuSdkRepository repo; - Sdk::targetsAndPackages(dir, &repo); - - bool needsUpgrade = false; - for (const auto &target : qAsConst(repo.mcuTargets)) { - // if kit already exists, skip - if (!matchingKits(target, qtForMCUsPackage).empty()) - continue; - if (!upgradeableKits(target, qtForMCUsPackage).empty()) { - // if kit exists but wrong version/path - needsUpgrade = true; - } else { - // if no kits for this target, create - if (target->isValid()) - newKit(target, qtForMCUsPackage); - target->printPackageProblems(); - } - } - - repo.deletePackagesAndTargets(); - - if (needsUpgrade) - McuSupportPlugin::askUserAboutMcuSupportKitsUpgrade(); - } - }; - - createKits(); - delete qtForMCUsPackage; + sdkRepository.deletePackagesAndTargets(); } void McuSupportOptions::checkUpgradeableKits() @@ -947,144 +421,44 @@ void McuSupportOptions::checkUpgradeableKits() return; if (Utils::anyOf(sdkRepository.mcuTargets, [this](const McuTarget *target) { - return !upgradeableKits(target, this->qtForMCUsSdkPackage).empty() - && matchingKits(target, this->qtForMCUsSdkPackage).empty(); + return !McuKitManager::upgradeableKits(target, this->qtForMCUsSdkPackage).empty() + && McuKitManager::matchingKits(target, this->qtForMCUsSdkPackage).empty(); })) - upgradeKits(askForKitUpgrades()); + McuKitManager::upgradeKitsByCreatingNewPackage(askForKitUpgrades()); } -void McuSupportOptions::upgradeKits(UpgradeOption upgradeOption) +bool McuSupportOptions::kitsNeedQtVersion() { - if (upgradeOption == Ignore) - return; - - auto qtForMCUsPackage = Sdk::createQtForMCUsPackage(); - - auto dir = qtForMCUsPackage->path(); - McuSdkRepository repo; - Sdk::targetsAndPackages(dir, &repo); - - for (const auto &target : qAsConst(repo.mcuTargets)) { - if (!matchingKits(target, qtForMCUsPackage).empty()) - // already up-to-date - continue; - - const auto kits = upgradeableKits(target, qtForMCUsPackage); - if (!kits.empty()) { - if (upgradeOption == Replace) - for (auto existingKit : kits) - KitManager::deregisterKit(existingKit); - - if (target->isValid()) - newKit(target, qtForMCUsPackage); - target->printPackageProblems(); - } - } - - repo.deletePackagesAndTargets(); - delete qtForMCUsPackage; + // Only on Windows, Qt is linked into the distributed qul Desktop libs. Also, the host tools + // are missing the Qt runtime libraries on non-Windows. + return !HostOsInfo::isWindowsHost(); } -void McuSupportOptions::upgradeKitInPlace(ProjectExplorer::Kit *kit, - const McuTarget *mcuTarget, - const McuAbstractPackage *qtForMCUsSdk) +bool McuSupportOptions::automaticKitCreationEnabled() const { - setKitProperties(kitName(mcuTarget), kit, mcuTarget, qtForMCUsSdk->path()); - setKitEnvironment(kit, mcuTarget, qtForMCUsSdk); - setKitDependencies(kit, mcuTarget, qtForMCUsSdk); + return m_automaticKitCreation; } -void McuSupportOptions::fixKitsDependencies() +void McuSupportOptions::setAutomaticKitCreationEnabled(const bool enabled) { - auto qtForMCUsPackage = Sdk::createQtForMCUsPackage(); - - FilePath dir = qtForMCUsPackage->path(); - McuSdkRepository repo; - Sdk::targetsAndPackages(dir, &repo); - for (const auto &target : qAsConst(repo.mcuTargets)) { - if (target->isValid()) { - for (auto* kit : kitsWithMismatchedDependencies(target)) { - updateKitEnvironment(kit, target); - } - } - } - - repo.deletePackagesAndTargets(); - delete qtForMCUsPackage; + m_automaticKitCreation = enabled; } -/** - * @brief Fix/update existing kits if needed - */ -void McuSupportOptions::fixExistingKits() +void McuSupportOptions::writeGeneralSettings() const { - for (Kit *kit : KitManager::kits()) { - if (!kit->hasValue(Constants::KIT_MCUTARGET_KITVERSION_KEY)) - continue; + const QString key = QLatin1String(Constants::SETTINGS_GROUP) + '/' + + QLatin1String(Constants::SETTINGS_KEY_AUTOMATIC_KIT_CREATION); + QSettings *settings = Core::ICore::settings(QSettings::UserScope); + settings->setValue(key, m_automaticKitCreation); +} - if (kit->isAutoDetected()) { - kit->setAutoDetected(false); - } - - // Check if the MCU kits are flagged as supplying a QtQuick import path, in order - // to tell the QMLJS code-model that it won't need to add a fall-back import - // path. - const auto bringsQtQuickImportPath = QtSupport::SuppliesQtQuickImportPath::id(); - auto irrelevantAspects = kit->irrelevantAspects(); - if (!irrelevantAspects.contains(bringsQtQuickImportPath)) { - irrelevantAspects.insert(bringsQtQuickImportPath); - kit->setIrrelevantAspects(irrelevantAspects); - } - if (!kit->hasValue(bringsQtQuickImportPath)) { - kit->setValue(bringsQtQuickImportPath, true); - } - - // Check if the MCU kit supplies its import path. - const auto kitQmlImportPath = QtSupport::KitQmlImportPath::id(); - if (!irrelevantAspects.contains(kitQmlImportPath)) { - irrelevantAspects.insert(kitQmlImportPath); - kit->setIrrelevantAspects(irrelevantAspects); - } - if (!kit->hasValue(kitQmlImportPath)) { - auto config = CMakeProjectManager::CMakeConfigurationKitAspect::configuration(kit); - for (const auto &cfgItem : qAsConst(config)) { - if (cfgItem.key == "QUL_GENERATORS") { - auto idx = cfgItem.value.indexOf("/lib/cmake/Qul"); - auto qulDir = cfgItem.value.left(idx); - kit->setValue(kitQmlImportPath, QVariant(qulDir + "/include/qul")); - break; - } - } - } - - // Check if the MCU kit has the flag for merged header/qml-import paths set. - const auto mergedPaths = QtSupport::KitHasMergedHeaderPathsWithQmlImportPaths::id(); - if (!irrelevantAspects.contains(mergedPaths)) { - irrelevantAspects.insert(mergedPaths); - kit->setIrrelevantAspects(irrelevantAspects); - } - if (!kit->value(mergedPaths, false).toBool()) { - kit->setValue(mergedPaths, true); - } - } - - // Fix kit dependencies for known targets - auto qtForMCUsPackage = Sdk::createQtForMCUsPackage(); - qtForMCUsPackage->updateStatus(); - if (qtForMCUsPackage->validStatus()) { - FilePath dir = qtForMCUsPackage->path(); - McuSdkRepository repo; - Sdk::targetsAndPackages(dir, &repo); - for (const auto &target : qAsConst(repo.mcuTargets)) - for (auto kit : existingKits(target)) { - if (McuDependenciesKitAspect::dependencies(kit).isEmpty()) { - setKitDependencies(kit, target, qtForMCUsPackage); - } - } - - repo.deletePackagesAndTargets(); - } - delete qtForMCUsPackage; +bool McuSupportOptions::automaticKitCreationFromSettings() +{ + QSettings *settings = Core::ICore::settings(QSettings::UserScope); + const QString key = QLatin1String(Constants::SETTINGS_GROUP) + '/' + + QLatin1String(Constants::SETTINGS_KEY_AUTOMATIC_KIT_CREATION); + const bool automaticKitCreation = settings->value(key, true).toBool(); + return automaticKitCreation; } } // namespace Internal diff --git a/src/plugins/mcusupport/mcusupportoptions.h b/src/plugins/mcusupport/mcusupportoptions.h index 51dba46b951..04dbb24b6ae 100644 --- a/src/plugins/mcusupport/mcusupportoptions.h +++ b/src/plugins/mcusupport/mcusupportoptions.h @@ -26,6 +26,9 @@ #pragma once #include +#include "mcusupport_global.h" +#include "mcukitmanager.h" + #include #include #include @@ -48,49 +51,7 @@ namespace Internal { class McuAbstractPackage; class McuToolChainPackage; - -void printMessage(const QString &message, bool important); - -class McuTarget : public QObject -{ - Q_OBJECT - -public: - enum class OS { Desktop, BareMetal, FreeRTOS }; - - struct Platform - { - QString name; - QString displayName; - QString vendor; - }; - - enum { UnspecifiedColorDepth = -1 }; - - McuTarget(const QVersionNumber &qulVersion, - const Platform &platform, - OS os, - const QVector &packages, - const McuToolChainPackage *toolChainPackage, - int colorDepth = UnspecifiedColorDepth); - - const QVersionNumber &qulVersion() const; - const QVector &packages() const; - const McuToolChainPackage *toolChainPackage() const; - const Platform &platform() const; - OS os() const; - int colorDepth() const; - bool isValid() const; - void printPackageProblems() const; - -private: - const QVersionNumber m_qulVersion; - const Platform m_platform; - const OS m_os; - const QVector m_packages; - const McuToolChainPackage *m_toolChainPackage; - const int m_colorDepth; -}; // class McuTarget +class McuTarget; class McuSdkRepository { @@ -106,8 +67,6 @@ class McuSupportOptions : public QObject Q_OBJECT public: - enum UpgradeOption { Ignore, Keep, Replace }; - explicit McuSupportOptions(QObject *parent = nullptr); ~McuSupportOptions() override; @@ -115,47 +74,36 @@ public: McuSdkRepository sdkRepository; void setQulDir(const Utils::FilePath &dir); - static void setKitEnvironment(ProjectExplorer::Kit *, const McuTarget *, const McuAbstractPackage *); + static void setKitEnvironment(ProjectExplorer::Kit *, + const McuTarget *, + const McuAbstractPackage *); static void updateKitEnvironment(ProjectExplorer::Kit *, const McuTarget *); static void remapQul2xCmakeVars(ProjectExplorer::Kit *, const Utils::EnvironmentItems &); static Utils::FilePath qulDirFromSettings(); + static McuKitManager::UpgradeOption askForKitUpgrades(); - static QString kitName(const McuTarget *mcuTarget); - - static QList existingKits(const McuTarget *mcuTarget); - static QList matchingKits(const McuTarget *mcuTarget, - const McuAbstractPackage *qtForMCUsSdkPackage); - static QList upgradeableKits(const McuTarget *mcuTarget, const McuAbstractPackage *qtForMCUsSdkPackage); - static QList kitsWithMismatchedDependencies(const McuTarget *mcuTarget); - static QList outdatedKits(); - static void removeOutdatedKits(); - static ProjectExplorer::Kit *newKit(const McuTarget *mcuTarget, const McuAbstractPackage *qtForMCUsSdk); - static void createAutomaticKits(); - static UpgradeOption askForKitUpgrades(); - static void upgradeKits(UpgradeOption upgradeOption); - static void upgradeKitInPlace(ProjectExplorer::Kit *kit, - const McuTarget *mcuTarget, - const McuAbstractPackage *qtForMCUsSdk); - static void fixKitsDependencies(); - void checkUpgradeableKits(); - static void fixExistingKits(); - void populatePackagesAndTargets(); static void registerQchFiles(); static void registerExamples(); static const QVersionNumber &minimalQulVersion(); - static QVersionNumber kitQulVersion(const ProjectExplorer::Kit *kit); - static bool kitUpToDate(const ProjectExplorer::Kit *kit, - const McuTarget *mcuTarget, - const McuAbstractPackage *qtForMCUsSdkPackage); + void checkUpgradeableKits(); + void populatePackagesAndTargets(); + static bool kitsNeedQtVersion(); + + bool automaticKitCreationEnabled() const; + void setAutomaticKitCreationEnabled(const bool enabled); + void writeGeneralSettings() const; + static bool automaticKitCreationFromSettings(); private: void deletePackagesAndTargets(); + bool m_automaticKitCreation = true; signals: - void changed(); + void packagesChanged(); }; + } // namespace Internal } // namespace McuSupport diff --git a/src/plugins/mcusupport/mcusupportoptionspage.cpp b/src/plugins/mcusupport/mcusupportoptionspage.cpp index 88f22e5a890..a4dfd3a6ceb 100644 --- a/src/plugins/mcusupport/mcusupportoptionspage.cpp +++ b/src/plugins/mcusupport/mcusupportoptionspage.cpp @@ -24,10 +24,12 @@ ****************************************************************************/ #include "mcusupportoptionspage.h" +#include "mcukitmanager.h" #include "mcupackage.h" #include "mcusupportconstants.h" #include "mcusupportoptions.h" #include "mcusupportsdk.h" +#include "mcutarget.h" #include #include @@ -121,8 +123,10 @@ McuSupportOptionsWidget::McuSupportOptionsWidget() &QComboBox::currentTextChanged, this, &McuSupportOptionsWidget::showMcuTargetPackages); - connect(m_options.qtForMCUsSdkPackage, &McuAbstractPackage::changed, - this, &McuSupportOptionsWidget::populateMcuTargetsComboBox); + connect(m_options.qtForMCUsSdkPackage, + &McuAbstractPackage::changed, + this, + &McuSupportOptionsWidget::populateMcuTargetsComboBox); } { @@ -142,7 +146,7 @@ McuSupportOptionsWidget::McuSupportOptionsWidget() m_kitAutomaticCreationCheckBox = new QCheckBox( tr("Automatically create kits for all available targets on start")); connect(m_kitAutomaticCreationCheckBox, &QCheckBox::stateChanged, this, [this](int state) { - m_options.qtForMCUsSdkPackage->setAutomaticKitCreationEnabled( + m_options.setAutomaticKitCreationEnabled( state == Qt::CheckState::Checked); }); mainLayout->addWidget(m_kitAutomaticCreationCheckBox); @@ -158,16 +162,16 @@ McuSupportOptionsWidget::McuSupportOptionsWidget() m_kitCreationPushButton = new QPushButton(tr("Create Kit")); m_kitCreationPushButton->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred); connect(m_kitCreationPushButton, &QPushButton::clicked, this, [this] { - McuSupportOptions::newKit(currentMcuTarget(), m_options.qtForMCUsSdkPackage); + McuKitManager::newKit(currentMcuTarget(), m_options.qtForMCUsSdkPackage); McuSupportOptions::registerQchFiles(); updateStatus(); }); m_kitUpdatePushButton = new QPushButton(tr("Update Kit")); m_kitUpdatePushButton->setSizePolicy(m_kitCreationPushButton->sizePolicy()); connect(m_kitUpdatePushButton, &QPushButton::clicked, this, [this] { - for (auto kit : McuSupportOptions::upgradeableKits(currentMcuTarget(), - m_options.qtForMCUsSdkPackage)) - m_options.upgradeKitInPlace(kit, currentMcuTarget(), m_options.qtForMCUsSdkPackage); + for (auto kit : + McuKitManager::upgradeableKits(currentMcuTarget(), m_options.qtForMCUsSdkPackage)) + McuKitManager::upgradeKitInPlace(kit, currentMcuTarget(), m_options.qtForMCUsSdkPackage); updateStatus(); }); vLayout->addWidget(m_kitCreationPushButton); @@ -176,7 +180,10 @@ McuSupportOptionsWidget::McuSupportOptionsWidget() mainLayout->addStretch(); - connect(&m_options, &McuSupportOptions::changed, this, &McuSupportOptionsWidget::updateStatus); + connect(&m_options, + &McuSupportOptions::packagesChanged, + this, + &McuSupportOptionsWidget::updateStatus); showMcuTargetPackages(); } @@ -215,12 +222,10 @@ void McuSupportOptionsWidget::updateStatus() m_kitUpdatePushButton->setVisible(mcuTargetValid); if (mcuTargetValid) { const bool hasMatchingKits - = !McuSupportOptions::matchingKits(mcuTarget, m_options.qtForMCUsSdkPackage) - .isEmpty(); - const bool hasUpgradeableKits - = !hasMatchingKits - && !McuSupportOptions::upgradeableKits(mcuTarget, m_options.qtForMCUsSdkPackage) - .isEmpty(); + = !McuKitManager::matchingKits(mcuTarget, m_options.qtForMCUsSdkPackage).isEmpty(); + const bool hasUpgradeableKits = !hasMatchingKits && + !McuKitManager::upgradeableKits( + mcuTarget, m_options.qtForMCUsSdkPackage).isEmpty(); m_kitCreationPushButton->setEnabled(!hasMatchingKits); m_kitUpdatePushButton->setEnabled(hasUpgradeableKits); @@ -241,8 +246,7 @@ void McuSupportOptionsWidget::updateStatus() } // Automatic Kit creation - m_kitAutomaticCreationCheckBox->setChecked( - m_options.qtForMCUsSdkPackage->automaticKitCreationEnabled()); + m_kitAutomaticCreationCheckBox->setChecked(m_options.automaticKitCreationEnabled()); // Status label in the bottom { @@ -296,14 +300,14 @@ void McuSupportOptionsWidget::apply() { bool pathsChanged = false; - m_options.qtForMCUsSdkPackage->writeGeneralSettings(); + m_options.writeGeneralSettings(); pathsChanged |= m_options.qtForMCUsSdkPackage->writeToSettings(); for (auto package : qAsConst(m_options.sdkRepository.packages)) pathsChanged |= package->writeToSettings(); if (pathsChanged) { m_options.checkUpgradeableKits(); - m_options.fixKitsDependencies(); + McuKitManager::fixKitsDependencies(); } } @@ -313,7 +317,7 @@ void McuSupportOptionsWidget::populateMcuTargetsComboBox() m_mcuTargetsComboBox->clear(); m_mcuTargetsComboBox->addItems( Utils::transform(m_options.sdkRepository.mcuTargets, - [](McuTarget *t) { return McuSupportOptions::kitName(t); })); + [](McuTarget *t) { return McuKitManager::kitName(t); })); updateStatus(); } diff --git a/src/plugins/mcusupport/mcusupportplugin.cpp b/src/plugins/mcusupport/mcusupportplugin.cpp index 3c2c8953c50..7cf3ccc73e6 100644 --- a/src/plugins/mcusupport/mcusupportplugin.cpp +++ b/src/plugins/mcusupport/mcusupportplugin.cpp @@ -28,6 +28,7 @@ #include "mcusupportconstants.h" #include "mcusupportdevice.h" #include "mcusupportoptions.h" +#include "mcukitmanager.h" #include "mcusupportoptionspage.h" #include "mcusupportrunconfiguration.h" @@ -38,6 +39,7 @@ #include #include #include +#include #include #include @@ -53,6 +55,16 @@ using namespace ProjectExplorer; namespace McuSupport { namespace Internal { +void printMessage(const QString &message, bool important) +{ + const QString displayMessage = QCoreApplication::translate("QtForMCUs", "Qt for MCUs: %1") + .arg(message); + if (important) + Core::MessageManager::writeFlashing(displayMessage); + else + Core::MessageManager::writeSilently(displayMessage); +} + class McuSupportPluginPrivate { public: @@ -65,7 +77,7 @@ public: McuDependenciesKitAspect environmentPathsKitAspect; }; // class McuSupportPluginPrivate -static McuSupportPluginPrivate* dd{nullptr}; +static McuSupportPluginPrivate *dd{nullptr}; McuSupportPlugin::~McuSupportPlugin() { @@ -93,9 +105,9 @@ void McuSupportPlugin::extensionsInitialized() ProjectExplorer::DeviceManager::instance()->addDevice(McuSupportDevice::create()); connect(KitManager::instance(), &KitManager::kitsLoaded, []() { - McuSupportOptions::removeOutdatedKits(); - McuSupportOptions::createAutomaticKits(); - McuSupportOptions::fixExistingKits(); + McuKitManager::removeOutdatedKits(); + McuKitManager::createAutomaticKits(); + McuKitManager::fixExistingKits(); McuSupportPlugin::askUserAboutMcuSupportKitsSetup(); }); } @@ -106,7 +118,7 @@ void McuSupportPlugin::askUserAboutMcuSupportKitsSetup() if (!ICore::infoBar()->canInfoBeAdded(setupMcuSupportKits) || McuSupportOptions::qulDirFromSettings().isEmpty() - || !McuSupportOptions::existingKits(nullptr).isEmpty()) + || !McuKitManager::existingKits(nullptr).isEmpty()) return; Utils::InfoBarEntry info(setupMcuSupportKits, @@ -130,18 +142,19 @@ void McuSupportPlugin::askUserAboutMcuSupportKitsUpgrade() Utils::InfoBarEntry info(upgradeMcuSupportKits, tr("New version of Qt for MCUs detected. Upgrade existing Kits?"), Utils::InfoBarEntry::GlobalSuppression::Enabled); + static McuKitManager::UpgradeOption selectedOption = McuKitManager::UpgradeOption::Keep; - static McuSupportOptions::UpgradeOption selectedOption; const QStringList options = {tr("Create new kits"), tr("Replace existing kits")}; - selectedOption = McuSupportOptions::UpgradeOption::Keep; info.setComboInfo(options, [options](const QString &selected) { - selectedOption = options.indexOf(selected) == 0 ? McuSupportOptions::UpgradeOption::Keep - : McuSupportOptions::UpgradeOption::Replace; + selectedOption = options.indexOf(selected) == 0 ? McuKitManager::UpgradeOption::Keep + : McuKitManager::UpgradeOption::Replace; }); info.addCustomButton(tr("Proceed"), [upgradeMcuSupportKits] { ICore::infoBar()->removeInfo(upgradeMcuSupportKits); - QTimer::singleShot(0, []() { McuSupportOptions::upgradeKits(selectedOption); }); + QTimer::singleShot(0, []() { + McuKitManager::upgradeKitsByCreatingNewPackage(selectedOption); + }); }); ICore::infoBar()->addInfo(info); diff --git a/src/plugins/mcusupport/mcusupportplugin.h b/src/plugins/mcusupport/mcusupportplugin.h index 2286ed0aad2..21767a02f07 100644 --- a/src/plugins/mcusupport/mcusupportplugin.h +++ b/src/plugins/mcusupport/mcusupportplugin.h @@ -31,6 +31,8 @@ namespace McuSupport::Internal { +void printMessage(const QString &message, bool important); + class McuSupportPlugin final : public ExtensionSystem::IPlugin { Q_OBJECT diff --git a/src/plugins/mcusupport/mcusupportsdk.cpp b/src/plugins/mcusupport/mcusupportsdk.cpp index 74d5ccedc33..812f181b97b 100644 --- a/src/plugins/mcusupport/mcusupportsdk.cpp +++ b/src/plugins/mcusupport/mcusupportsdk.cpp @@ -24,10 +24,13 @@ ****************************************************************************/ #include "mcusupportsdk.h" +#include "mcukitmanager.h" #include "mcupackage.h" #include "mcusupportconstants.h" #include "mcusupportoptions.h" +#include "mcusupportplugin.h" #include "mcusupportversiondetection.h" +#include "mcutarget.h" #include "mcutargetdescription.h" #include @@ -527,10 +530,9 @@ protected: QVector mcuTargets; McuToolChainPackage *tcPkg = tcPkgs.value(desc.toolchain.id); - if (tcPkg) { - if (QVersionNumber::fromString(desc.qulVersion) >= QVersionNumber({1, 8})) - tcPkg->setVersions(desc.toolchain.versions); - } else + if (tcPkg) + tcPkg->setVersions(desc.toolchain.versions); + else tcPkg = createUnsupportedToolChainPackage(); for (int colorDepth : desc.platform.colorDepths) { QVector required3rdPartyPkgs; @@ -551,8 +553,7 @@ protected: boardSdkPkgs.insert(desc.boardSdk.envVar, boardSdkPkg); } auto boardSdkPkg = boardSdkPkgs.value(desc.boardSdk.envVar); - if (QVersionNumber::fromString(desc.qulVersion) >= QVersionNumber({1, 8})) - boardSdkPkg->setVersions(desc.boardSdk.versions); + boardSdkPkg->setVersions(desc.boardSdk.versions); boardSdkDefaultPath = boardSdkPkg->defaultPath(); required3rdPartyPkgs.append(boardSdkPkg); } @@ -641,9 +642,12 @@ static QFileInfoList targetDescriptionFiles(const Utils::FilePath &dir) return kitsDir.entryInfoList(); } -static McuTargetDescription parseDescriptionJsonCommon(const QString &qulVersion, - const QJsonObject &target) +McuTargetDescription parseDescriptionJson(const QByteArray &data) { + const QJsonDocument document = QJsonDocument::fromJson(data); + const QJsonObject target = document.object(); + const QString qulVersion = target.value("qulVersion").toString(); + const QJsonObject platform = target.value("platform").toObject(); const QString compatVersion = target.value("compatVersion").toString(); const QJsonObject toolchain = target.value("toolchain").toObject(); const QJsonObject boardSdk = target.value("boardSdk").toObject(); @@ -660,9 +664,23 @@ static McuTargetDescription parseDescriptionJsonCommon(const QString &qulVersion return version.toString(); }); + const QVariantList colorDepths = platform.value("colorDepths").toArray().toVariantList(); + const auto colorDepthsVector = Utils::transform>(colorDepths, + [&](const QVariant &colorDepth) { + return colorDepth.toInt(); + }); + const QString platformName = platform.value("platformName").toString(); + return {qulVersion, compatVersion, - {}, + { + platform.value("id").toString(), + platformName, + platform.value("vendor").toString(), + colorDepthsVector, + platformName == "Desktop" ? McuTargetDescription::TargetType::Desktop + : McuTargetDescription::TargetType::MCU, + }, { toolchain.value("id").toString(), toolchainVersionsList, @@ -679,86 +697,36 @@ static McuTargetDescription parseDescriptionJsonCommon(const QString &qulVersion }}; } -static McuTargetDescription parseDescriptionJsonV1x(const QString &qulVersion, - const QJsonObject &target) -{ - auto description = parseDescriptionJsonCommon(qulVersion, target); - - const QVariantList colorDepths = target.value("colorDepths").toArray().toVariantList(); - const auto colorDepthsVector = Utils::transform>(colorDepths, - [&](const QVariant &colorDepth) { - return colorDepth.toInt(); - }); - - description.platform = {target.value("platform").toString(), - target.value("platformName").toString(), - target.value("platformVendor").toString(), - colorDepthsVector, - description.boardSdk.envVar.isEmpty() - ? McuTargetDescription::TargetType::Desktop - : McuTargetDescription::TargetType::MCU}; - return description; -} - -static McuTargetDescription parseDescriptionJsonV2x(const QString &qulVersion, - const QJsonObject &target) -{ - const QJsonObject platform = target.value("platform").toObject(); - - const QVariantList colorDepths = platform.value("colorDepths").toArray().toVariantList(); - const auto colorDepthsVector = Utils::transform>(colorDepths, - [&](const QVariant &colorDepth) { - return colorDepth.toInt(); - }); - const QString platformName = platform.value("platformName").toString(); - McuTargetDescription description = parseDescriptionJsonCommon(qulVersion, target); - description.platform = { - platform.value("id").toString(), - platformName, - platform.value("vendor").toString(), - colorDepthsVector, - platformName == "Desktop" ? McuTargetDescription::TargetType::Desktop - : McuTargetDescription::TargetType::MCU, - }; - - return description; -} - -McuTargetDescription parseDescriptionJson(const QByteArray &data) -{ - const QJsonDocument document = QJsonDocument::fromJson(data); - const QJsonObject target = document.object(); - - const QString qulVersion = target.value("qulVersion").toString(); - - switch (QVersionNumber::fromString(qulVersion).majorVersion()) { - case 1: - return parseDescriptionJsonV1x(qulVersion, target); - case 2: - return parseDescriptionJsonV2x(qulVersion, target); - default: - return {qulVersion}; - } -} - // https://doc.qt.io/qtcreator/creator-developing-mcu.html#supported-qt-for-mcus-sdks -const QHash oldSdkQtcRequiredVersion = { - {{"1.0"}, {"4.11.x"}}, - {{"1.1"}, {"4.12.0 or 4.12.1"}}, - {{"1.2"}, {"4.12.2 or 4.12.3"}}, -}; +static const QString legacySupportVersionFor(const QString &sdkVersion) +{ + static const QHash oldSdkQtcRequiredVersion = { + {{"1.0"}, {"4.11.x"}}, + {{"1.1"}, {"4.12.0 or 4.12.1"}}, + {{"1.2"}, {"4.12.2 or 4.12.3"}} + }; + if (oldSdkQtcRequiredVersion.contains(sdkVersion)) + return oldSdkQtcRequiredVersion.value(sdkVersion); + + if (QVersionNumber::fromString(sdkVersion).majorVersion() == 1) + return "4.12.4 up to 6.0"; + + return QString(); +} + bool checkDeprecatedSdkError(const Utils::FilePath &qulDir, QString &message) { const McuPackagePathVersionDetector versionDetector("(?<=\\bQtMCUs.)(\\d+\\.\\d+)"); const QString sdkDetectedVersion = versionDetector.parseVersion(qulDir.toString()); + const QString legacyVersion = legacySupportVersionFor(sdkDetectedVersion); - if (oldSdkQtcRequiredVersion.contains(sdkDetectedVersion)) { + if (!legacyVersion.isEmpty()) { message = McuTarget::tr("Qt for MCUs SDK version %1 detected, " "only supported by Qt Creator version %2. " "This version of Qt Creator requires Qt for MCUs %3 or greater.") .arg(sdkDetectedVersion, - oldSdkQtcRequiredVersion.value(sdkDetectedVersion), + legacyVersion, McuSupportOptions::minimalQulVersion().toString()); return true; } @@ -787,10 +755,11 @@ void targetsAndPackages(const Utils::FilePath &dir, McuSdkRepository *repo) continue; } if (QVersionNumber::fromString(desc.qulVersion) < McuSupportOptions::minimalQulVersion()) { + const QString legacyVersion = legacySupportVersionFor(desc.qulVersion); const QString qtcSupportText - = oldSdkQtcRequiredVersion.contains(desc.qulVersion) + = !legacyVersion.isEmpty() ? McuTarget::tr("Detected version \"%1\", only supported by Qt Creator %2.") - .arg(desc.qulVersion, oldSdkQtcRequiredVersion.value(desc.qulVersion)) + .arg(desc.qulVersion, legacyVersion) : McuTarget::tr("Unsupported version \"%1\".").arg(desc.qulVersion); printMessage(McuTarget::tr("Skipped %1. %2 Qt for MCUs version >= %3 required.") .arg(QDir::toNativeSeparators(pth.fileNameWithPathComponents(1)), @@ -818,68 +787,13 @@ void targetsAndPackages(const Utils::FilePath &dir, McuSdkRepository *repo) } } - // Workaround for missing JSON file for Desktop target. - // Desktop JSON file is shipped starting from Qul 1.5. - // This whole section could be removed when minimalQulVersion will reach 1.5 or above - { - const bool hasDesktopDescription - = contains(descriptions, [](const McuTargetDescription &desc) { - return desc.platform.type == McuTargetDescription::TargetType::Desktop; - }); - - if (!hasDesktopDescription) { - QVector desktopLibs; - if (HostOsInfo::isWindowsHost()) { - desktopLibs << dir / "lib/QulQuickUltralite_QT_32bpp_Windows_Release.lib"; // older versions of QUL (<1.5?) - desktopLibs - << dir / "lib/QulQuickUltralitePlatform_QT_32bpp_Windows_msvc_Release.lib"; // newer versions of QUL - } else { - desktopLibs << dir / "lib/libQulQuickUltralite_QT_32bpp_Linux_Debug.a"; // older versions of QUL (<1.5?) - desktopLibs << dir / "lib/libQulQuickUltralitePlatform_QT_32bpp_Linux_gnu_Debug.a"; // newer versions of QUL - } - - if (anyOf(desktopLibs, [](const FilePath &desktopLib) { return desktopLib.exists(); })) { - McuTargetDescription desktopDescription; - desktopDescription.qulVersion - = descriptions.empty() ? McuSupportOptions::minimalQulVersion().toString() - : descriptions.first().qulVersion; - desktopDescription.platform.id = "Qt"; - desktopDescription.platform.name = "Desktop"; - desktopDescription.platform.vendor = "Qt"; - desktopDescription.platform.colorDepths = {32}; - desktopDescription.toolchain.id = HostOsInfo::isWindowsHost() ? QString("msvc") - : QString("gcc"); - desktopDescription.platform.type = McuTargetDescription::TargetType::Desktop; - descriptions.prepend(desktopDescription); - } else { - // show error only on 1.x SDKs, but skip on 2.x - const FilePath desktopLibV2 - = HostOsInfo::isWindowsHost() - ? dir / "lib/QulPlatform_qt_32bpp_Windows_msvc_Release.lib" - : dir / "lib/libQulPlatform_qt_32bpp_Linux_gnu_Release.a"; - if (dir.exists() && !desktopLibV2.exists()) - printMessage( - McuTarget::tr( - "Skipped creating fallback desktop kit: Could not find any of %1.") - .arg(transform(desktopLibs, - [](const auto &path) { - return QDir::toNativeSeparators( - path.fileNameWithPathComponents(1)); - }) - .toList() - .join(" or ")), - false); - } - } - } - repo->mcuTargets.append(targetsFromDescriptions(descriptions, &(repo->packages))); // Keep targets sorted lexicographically std::sort(repo->mcuTargets.begin(), repo->mcuTargets.end(), [](const McuTarget *lhs, const McuTarget *rhs) { - return McuSupportOptions::kitName(lhs) < McuSupportOptions::kitName(rhs); + return McuKitManager::kitName(lhs) < McuKitManager::kitName(rhs); }); } diff --git a/src/plugins/mcusupport/mcusupportsdk.h b/src/plugins/mcusupport/mcusupportsdk.h index b213164d2d4..d3aad240cae 100644 --- a/src/plugins/mcusupport/mcusupportsdk.h +++ b/src/plugins/mcusupport/mcusupportsdk.h @@ -33,7 +33,7 @@ namespace McuSupport { namespace Internal { -constexpr int MAX_COMPATIBILITY_VERSION {1}; +constexpr int MAX_COMPATIBILITY_VERSION{1}; class McuSdkRepository; class McuAbstractPackage; @@ -51,13 +51,14 @@ bool checkDeprecatedSdkError(const Utils::FilePath &qulDir, QString &message); void targetsAndPackages(const Utils::FilePath &qulDir, McuSdkRepository *repo); McuTargetDescription parseDescriptionJson(const QByteArray &); -QVector targetsFromDescriptions(const QList &, QVector *); +QVector targetsFromDescriptions(const QList &, + QVector *); Utils::FilePath kitsPath(const Utils::FilePath &dir); Utils::FilePath packagePathFromSettings(const QString &settingsKey, - QSettings::Scope scope = QSettings::UserScope, - const Utils::FilePath &defaultPath = {}); + QSettings::Scope scope, + const Utils::FilePath &defaultPath); } // namespace Sdk } // namespace Internal } // namespace McuSupport diff --git a/src/plugins/mcusupport/mcutarget.cpp b/src/plugins/mcusupport/mcutarget.cpp new file mode 100644 index 00000000000..291b861e221 --- /dev/null +++ b/src/plugins/mcusupport/mcutarget.cpp @@ -0,0 +1,290 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "mcutarget.h" +#include "mcupackage.h" +#include "mcukitmanager.h" + +#include "mcusupportplugin.h" + +#include +#include +#include +#include +#include +#include + +using namespace ProjectExplorer; +using namespace Utils; + +namespace McuSupport::Internal { + +static ToolChain *msvcToolChain(Id language) +{ + ToolChain *toolChain = ToolChainManager::toolChain([language](const ToolChain *t) { + const Abi abi = t->targetAbi(); + // TODO: Should Abi::WindowsMsvc2022Flavor be added too? + return (abi.osFlavor() == Abi::WindowsMsvc2017Flavor + || abi.osFlavor() == Abi::WindowsMsvc2019Flavor) + && abi.architecture() == Abi::X86Architecture && abi.wordWidth() == 64 + && t->language() == language; + }); + return toolChain; +} + +static ToolChain *gccToolChain(Id language) +{ + ToolChain *toolChain = ToolChainManager::toolChain([language](const ToolChain *t) { + const Abi abi = t->targetAbi(); + return abi.os() != Abi::WindowsOS && abi.architecture() == Abi::X86Architecture + && abi.wordWidth() == 64 && t->language() == language; + }); + return toolChain; +} + +static ToolChain *armGccToolChain(const FilePath &path, Id language) +{ + ToolChain *toolChain = ToolChainManager::toolChain([&path, language](const ToolChain *t) { + return t->compilerCommand() == path && t->language() == language; + }); + if (!toolChain) { + ToolChainFactory *gccFactory + = Utils::findOrDefault(ToolChainFactory::allToolChainFactories(), + [](ToolChainFactory *f) { + return f->supportedToolChainType() + == ProjectExplorer::Constants::GCC_TOOLCHAIN_TYPEID; + }); + if (gccFactory) { + const QList detected = gccFactory->detectForImport({path, language}); + if (!detected.isEmpty()) { + toolChain = detected.first(); + toolChain->setDetection(ToolChain::ManualDetection); + toolChain->setDisplayName("Arm GCC"); + ToolChainManager::registerToolChain(toolChain); + } + } + } + + return toolChain; +} + +static ToolChain *iarToolChain(const FilePath &path, Id language) +{ + ToolChain *toolChain = ToolChainManager::toolChain([language](const ToolChain *t) { + return t->typeId() == BareMetal::Constants::IAREW_TOOLCHAIN_TYPEID + && t->language() == language; + }); + if (!toolChain) { + ToolChainFactory *iarFactory + = Utils::findOrDefault(ToolChainFactory::allToolChainFactories(), + [](ToolChainFactory *f) { + return f->supportedToolChainType() + == BareMetal::Constants::IAREW_TOOLCHAIN_TYPEID; + }); + if (iarFactory) { + Toolchains detected = iarFactory->autoDetect(ToolchainDetector({}, {})); + if (detected.isEmpty()) + detected = iarFactory->detectForImport({path, language}); + for (auto tc : detected) { + if (tc->language() == language) { + toolChain = tc; + toolChain->setDetection(ToolChain::ManualDetection); + toolChain->setDisplayName("IAREW"); + ToolChainManager::registerToolChain(toolChain); + } + } + } + } + + return toolChain; +} + +ToolChain *McuToolChainPackage::toolChain(Id language) const +{ + switch (m_type) { + case Type::MSVC: + return msvcToolChain(language); + case Type::GCC: + return gccToolChain(language); + case Type::IAR: { + const FilePath compiler = path().pathAppended("/bin/iccarm").withExecutableSuffix(); + return iarToolChain(compiler, language); + } + case Type::ArmGcc: + case Type::KEIL: + case Type::GHS: + case Type::GHSArm: + case Type::Unsupported: { + const QLatin1String compilerName( + language == ProjectExplorer::Constants::C_LANGUAGE_ID ? "gcc" : "g++"); + const QString comp = QLatin1String(m_type == Type::ArmGcc ? "/bin/arm-none-eabi-%1" + : "/bar/foo-keil-%1") + .arg(compilerName); + const FilePath compiler = path().pathAppended(comp).withExecutableSuffix(); + + return armGccToolChain(compiler, language); + } + default: + Q_UNREACHABLE(); + } +} + +QString McuToolChainPackage::toolChainName() const +{ + switch (m_type) { + case Type::ArmGcc: + return QLatin1String("armgcc"); + case Type::IAR: + return QLatin1String("iar"); + case Type::KEIL: + return QLatin1String("keil"); + case Type::GHS: + return QLatin1String("ghs"); + case Type::GHSArm: + return QLatin1String("ghs-arm"); + default: + return QLatin1String("unsupported"); + } +} + +QString McuToolChainPackage::cmakeToolChainFileName() const +{ + return toolChainName() + QLatin1String(".cmake"); +} + +QVariant McuToolChainPackage::debuggerId() const +{ + using namespace Debugger; + + QString sub, displayName; + DebuggerEngineType engineType; + + switch (m_type) { + case Type::ArmGcc: { + sub = QString::fromLatin1("bin/arm-none-eabi-gdb-py"); + displayName = McuPackage::tr("Arm GDB at %1"); + engineType = Debugger::GdbEngineType; + break; + } + case Type::IAR: { + sub = QString::fromLatin1("../common/bin/CSpyBat"); + displayName = QLatin1String("CSpy"); + engineType = Debugger::NoEngineType; // support for IAR missing + break; + } + case Type::KEIL: { + sub = QString::fromLatin1("UV4/UV4"); + displayName = QLatin1String("KEIL uVision Debugger"); + engineType = Debugger::UvscEngineType; + break; + } + default: + return QVariant(); + } + + const FilePath command = path().pathAppended(sub).withExecutableSuffix(); + if (const DebuggerItem *debugger = DebuggerItemManager::findByCommand(command)) { + return debugger->id(); + } + + DebuggerItem newDebugger; + newDebugger.setCommand(command); + newDebugger.setUnexpandedDisplayName(displayName.arg(command.toUserOutput())); + newDebugger.setEngineType(engineType); + return DebuggerItemManager::registerDebugger(newDebugger); +} + +McuTarget::McuTarget(const QVersionNumber &qulVersion, + const Platform &platform, + OS os, + const QVector &packages, + const McuToolChainPackage *toolChainPackage, + int colorDepth) + : m_qulVersion(qulVersion) + , m_platform(platform) + , m_os(os) + , m_packages(packages) + , m_toolChainPackage(toolChainPackage) + , m_colorDepth(colorDepth) +{} + +const QVector &McuTarget::packages() const +{ + return m_packages; +} + +const McuToolChainPackage *McuTarget::toolChainPackage() const +{ + return m_toolChainPackage; +} + +McuTarget::OS McuTarget::os() const +{ + return m_os; +} + +const McuTarget::Platform &McuTarget::platform() const +{ + return m_platform; +} + +bool McuTarget::isValid() const +{ + return Utils::allOf(packages(), [](McuAbstractPackage *package) { + package->updateStatus(); + return package->validStatus(); + }); +} + +void McuTarget::printPackageProblems() const +{ + for (auto package : packages()) { + package->updateStatus(); + if (!package->validStatus()) + printMessage(tr("Error creating kit for target %1, package %2: %3") + .arg(McuKitManager::kitName(this), + package->label(), + package->statusText()), + true); + if (package->status() == McuAbstractPackage::Status::ValidPackageMismatchedVersion) + printMessage(tr("Warning creating kit for target %1, package %2: %3") + .arg(McuKitManager::kitName(this), + package->label(), + package->statusText()), + false); + } +} + +const QVersionNumber &McuTarget::qulVersion() const +{ + return m_qulVersion; +} + +int McuTarget::colorDepth() const +{ + return m_colorDepth; +} + +} // namespace McuSupport::Internal diff --git a/src/plugins/mcusupport/mcutarget.h b/src/plugins/mcusupport/mcutarget.h new file mode 100644 index 00000000000..7426f81e152 --- /dev/null +++ b/src/plugins/mcusupport/mcutarget.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include +#include + +namespace ProjectExplorer { +class ToolChain; +} + +namespace Utils { +class PathChooser; +class InfoLabel; +} // namespace Utils + +namespace McuSupport { +namespace Internal { + +class McuAbstractPackage; +class McuToolChainPackage; + +class McuTarget : public QObject +{ + Q_OBJECT + +public: + enum class OS { Desktop, BareMetal, FreeRTOS }; + + struct Platform + { + QString name; + QString displayName; + QString vendor; + }; + + enum { UnspecifiedColorDepth = -1 }; + + McuTarget(const QVersionNumber &qulVersion, + const Platform &platform, + OS os, + const QVector &packages, + const McuToolChainPackage *toolChainPackage, + int colorDepth = UnspecifiedColorDepth); + + const QVersionNumber &qulVersion() const; + const QVector &packages() const; + const McuToolChainPackage *toolChainPackage() const; + const Platform &platform() const; + OS os() const; + int colorDepth() const; + bool isValid() const; + void printPackageProblems() const; + +private: + const QVersionNumber m_qulVersion; + const Platform m_platform; + const OS m_os; + const QVector m_packages; + const McuToolChainPackage *m_toolChainPackage; + const int m_colorDepth; +}; // class McuTarget + + +} // namespace Internal +} // namespace McuSupport diff --git a/src/plugins/mcusupport/test/packagemock.h b/src/plugins/mcusupport/test/packagemock.h index 45a23947bee..7f8e98d40f5 100644 --- a/src/plugins/mcusupport/test/packagemock.h +++ b/src/plugins/mcusupport/test/packagemock.h @@ -27,8 +27,8 @@ #include "mcuabstractpackage.h" -#include #include +#include namespace McuSupport::Internal { @@ -45,19 +45,13 @@ public: MOCK_METHOD(Status, status, (), (const)); MOCK_METHOD(bool, validStatus, (), (const)); - MOCK_METHOD(void, setDownloadUrl, (const QString &) ); - MOCK_METHOD(void, setEnvironmentVariableName, (const QString &) ); - MOCK_METHOD(const QString&, environmentVariableName, (), (const)); + MOCK_METHOD(const QString &, environmentVariableName, (), (const)); MOCK_METHOD(void, setAddToPath, (bool) ); MOCK_METHOD(bool, addToPath, (), (const)); - MOCK_METHOD(void, writeGeneralSettings, (), (const)); MOCK_METHOD(bool, writeToSettings, (), (const)); MOCK_METHOD(void, setRelativePathModifier, (const QString &) ); MOCK_METHOD(void, setVersions, (const QStringList &) ); - MOCK_METHOD(bool, automaticKitCreationEnabled, (), (const)); - MOCK_METHOD(void, setAutomaticKitCreationEnabled, (const bool enabled)); - MOCK_METHOD(QWidget *, widget, ()); }; // class PackageMock } // namespace McuSupport::Internal diff --git a/src/plugins/mcusupport/test/unittest.cpp b/src/plugins/mcusupport/test/unittest.cpp index 45b5cdc76d3..30a6fa8a532 100644 --- a/src/plugins/mcusupport/test/unittest.cpp +++ b/src/plugins/mcusupport/test/unittest.cpp @@ -23,9 +23,10 @@ ** ****************************************************************************/ -#include "mcutargetdescription.h" -#include "nxp_1064_json.h" #include "unittest.h" +#include "mcutargetdescription.h" +#include "mcukitmanager.h" +#include "nxp_1064_json.h" #include "utils/filepath.h" #include #include @@ -55,7 +56,7 @@ void McuSupportTest::test_parseBasicInfoFromJson() { const auto description = Sdk::parseDescriptionJson(nxp_1064_json); - QVERIFY(not description.freeRTOS.envVar.isEmpty()); + QVERIFY(!description.freeRTOS.envVar.isEmpty()); QVERIFY(description.freeRTOS.boardSdkSubDir.isEmpty()); } @@ -65,7 +66,7 @@ void McuSupportTest::test_addNewKit() QSignalSpy kitAddedSpy(&kitManager, &KitManager::kitAdded); - auto *newKit{McuSupportOptions::newKit(&mcuTarget, &freeRtosPackage)}; + auto *newKit{McuKitManager::newKit(&mcuTarget, &freeRtosPackage)}; QVERIFY(newKit != nullptr); QCOMPARE(kitAddedSpy.count(), 1); @@ -85,13 +86,13 @@ void McuSupportTest::test_addFreeRtosCmakeVarToKit() QVERIFY(kit.hasValue(EnvironmentKitAspect::id())); QVERIFY(kit.isValid()); - QVERIFY(not kit.allKeys().empty()); + QVERIFY(!kit.allKeys().empty()); const auto &cmakeConfig{CMakeConfigurationKitAspect::configuration(&kit)}; QCOMPARE(cmakeConfig.size(), 1); CMakeConfigItem expectedCmakeVar{freeRtosCmakeVar.toLocal8Bit(), - defaultfreeRtosPath.toLocal8Bit()}; + FilePath::fromString(defaultfreeRtosPath).toUserOutput().toLocal8Bit()}; QVERIFY(cmakeConfig.contains(expectedCmakeVar)); } diff --git a/src/plugins/mcusupport/test/unittest.h b/src/plugins/mcusupport/test/unittest.h index 738e4d45cea..dcdb5370b2d 100644 --- a/src/plugins/mcusupport/test/unittest.h +++ b/src/plugins/mcusupport/test/unittest.h @@ -25,10 +25,11 @@ #pragma once +#include "mcupackage.h" +#include "mcutarget.h" #include "mcusupportoptions.h" #include "mcusupportplugin.h" #include "mcusupportsdk.h" -#include "mcupackage.h" #include "packagemock.h" #include diff --git a/src/plugins/mcusupport/wizards/application/CMakeLists.txt b/src/plugins/mcusupport/wizards/application/CMakeLists.txt index 74c1c8357ba..4b384653be1 100644 --- a/src/plugins/mcusupport/wizards/application/CMakeLists.txt +++ b/src/plugins/mcusupport/wizards/application/CMakeLists.txt @@ -4,20 +4,7 @@ project(%{ProjectName} VERSION 0.0.1 LANGUAGES C CXX ASM ASM_MASM) find_package(Qul) -if(Qul_VERSION VERSION_GREATER_EQUAL "1.7") - qul_add_target(%{ProjectName}) -else() - add_executable(%{ProjectName}) - target_link_libraries(%{ProjectName} - Qul::QuickUltralite - Qul::QuickUltralitePlatform) -endif() - +qul_add_target(%{ProjectName}) qul_target_qml_sources(%{ProjectName} %{MainQmlFile}) app_target_setup_os(%{ProjectName}) - -if(Qul_VERSION VERSION_GREATER_EQUAL "2.0") - app_target_default_entrypoint(%{ProjectName} %{RootItemName}) -else() - app_target_default_main(%{ProjectName} %{RootItemName}) -endif() +app_target_default_entrypoint(%{ProjectName} %{RootItemName}) diff --git a/src/plugins/mercurial/mercurialsettings.cpp b/src/plugins/mercurial/mercurialsettings.cpp index 94734464828..018f6594e16 100644 --- a/src/plugins/mercurial/mercurialsettings.cpp +++ b/src/plugins/mercurial/mercurialsettings.cpp @@ -73,7 +73,7 @@ MercurialSettingsPage::MercurialSettingsPage(MercurialSettings *settings) setId(VcsBase::Constants::VCS_ID_MERCURIAL); setDisplayName(MercurialSettings::tr("Mercurial")); setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY); - setSettings(m_settings); + setSettings(settings); setLayouter([settings](QWidget *widget) { MercurialSettings &s = *settings; diff --git a/src/plugins/projectexplorer/appoutputpane.cpp b/src/plugins/projectexplorer/appoutputpane.cpp index 076fb1c8953..9fc9562355e 100644 --- a/src/plugins/projectexplorer/appoutputpane.cpp +++ b/src/plugins/projectexplorer/appoutputpane.cpp @@ -174,7 +174,7 @@ AppOutputPane::AppOutputPane() : m_formatterWidget(new QWidget), m_handler(new ShowOutputTaskHandler(this, tr("Show &App Output"), - tr("Show the output that generated this issue in the application output window"), + tr("Show the output that generated this issue in the Application Output pane."), tr("A"))) { ExtensionSystem::PluginManager::addObject(m_handler); diff --git a/src/plugins/projectexplorer/buildconfiguration.cpp b/src/plugins/projectexplorer/buildconfiguration.cpp index 7a68c276c3d..f5aec089ced 100644 --- a/src/plugins/projectexplorer/buildconfiguration.cpp +++ b/src/plugins/projectexplorer/buildconfiguration.cpp @@ -121,8 +121,8 @@ public: layout->setContentsMargins(0, 0, 0, 0); const auto pasteStdOutCB = new QCheckBox(tr("Parse standard output during build"), this); - pasteStdOutCB->setToolTip(tr("Check this to make output parsers look for diagnostics " - "on stdout rather than stderr")); + pasteStdOutCB->setToolTip(tr("Makes output parsers look for diagnostics " + "on stdout rather than stderr.")); pasteStdOutCB->setChecked(bc->parseStdOut()); layout->addWidget(pasteStdOutCB); diff --git a/src/plugins/projectexplorer/compileoutputwindow.cpp b/src/plugins/projectexplorer/compileoutputwindow.cpp index a51397bd256..abb6736a955 100644 --- a/src/plugins/projectexplorer/compileoutputwindow.cpp +++ b/src/plugins/projectexplorer/compileoutputwindow.cpp @@ -118,7 +118,7 @@ CompileOutputWindow::CompileOutputWindow(QAction *cancelBuildAction) : m_handler = new ShowOutputTaskHandler(this, tr("Show Compile &Output"), - tr("Show the output that generated this issue in the compile output window"), + tr("Show the output that generated this issue in the Compile Output pane."), tr("O")); ExtensionSystem::PluginManager::addObject(m_handler); setupContext(C_COMPILE_OUTPUT, m_outputWindow); diff --git a/src/plugins/projectexplorer/kit.cpp b/src/plugins/projectexplorer/kit.cpp index b509c5bc62f..114e6e25512 100644 --- a/src/plugins/projectexplorer/kit.cpp +++ b/src/plugins/projectexplorer/kit.cpp @@ -113,11 +113,11 @@ public: [kit] { return kit->fileSystemFriendlyName(); }); m_macroExpander.registerVariable("CurrentKit:Id", - tr("The id of the currently active kit."), + tr("The ID of the currently active kit."), [kit] { return kit->id().toString(); }, false); m_macroExpander.registerVariable("Kit:Id", - tr("The id of the kit."), + tr("The ID of the kit."), [kit] { return kit->id().toString(); }); } diff --git a/src/plugins/projectexplorer/msvctoolchain.cpp b/src/plugins/projectexplorer/msvctoolchain.cpp index 8f734f78a80..13a945a2807 100644 --- a/src/plugins/projectexplorer/msvctoolchain.cpp +++ b/src/plugins/projectexplorer/msvctoolchain.cpp @@ -1546,6 +1546,12 @@ static const MsvcToolChain *findMsvcToolChain(const QString &displayedVarsBat) static QVersionNumber clangClVersion(const FilePath &clangClPath) { + QString error; + QString dllversion = winGetDLLVersion(Utils::WinDLLFileVersion, clangClPath.toString(), &error); + + if (!dllversion.isEmpty()) + return QVersionNumber::fromString(dllversion); + QtcProcess clangClProcess; clangClProcess.setCommand({clangClPath, {"--version"}}); clangClProcess.runBlocking(); diff --git a/src/plugins/projectexplorer/runconfiguration.cpp b/src/plugins/projectexplorer/runconfiguration.cpp index 76ac46ec224..430eadb8dae 100644 --- a/src/plugins/projectexplorer/runconfiguration.cpp +++ b/src/plugins/projectexplorer/runconfiguration.cpp @@ -188,13 +188,13 @@ RunConfiguration::RunConfiguration(Target *target, Utils::Id id) BuildConfiguration *bc = target->activeBuildConfiguration(); return bc ? bc->macroExpander() : target->macroExpander(); }); - m_expander.registerPrefix("RunConfig:Env", tr("Variables in the run environment"), + m_expander.registerPrefix("RunConfig:Env", tr("Variables in the run environment."), [this](const QString &var) { const auto envAspect = aspect(); return envAspect ? envAspect->environment().expandedValueForKey(var) : QString(); }); m_expander.registerVariable("RunConfig:WorkingDir", - tr("The run configuration's working directory"), + tr("The run configuration's working directory."), [this] { const auto wdAspect = aspect(); return wdAspect ? wdAspect->workingDirectory().toString() : QString(); diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index b501a98bdd1..742b6f055c0 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -30,6 +30,8 @@ add_qtc_plugin(QmlDesigner checkablefiletreeitem.cpp checkablefiletreeitem.h cmakegeneratordialog.cpp cmakegeneratordialog.h cmakegeneratordialogtreemodel.cpp cmakegeneratordialogtreemodel.h + cmakeprojectconverter.cpp cmakeprojectconverter.h + cmakeprojectconverterdialog.cpp cmakeprojectconverterdialog.h generateresource.cpp generateresource.h generatecmakelists.cpp generatecmakelists.h generatecmakelistsconstants.h diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp index 5ac9a4ae8a8..d56c593a83c 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp @@ -129,7 +129,7 @@ void AssetExporter::exportQml(const Utils::FilePaths &qmlFiles, const Utils::Fil .arg(exportAssets? tr("Yes") : tr("No"))); if (m_perComponentExport) - ExportNotification::addInfo(tr("Each component is exported separately")); + ExportNotification::addInfo(tr("Each component is exported separately.")); notifyProgress(0.0); m_exportFiles = qmlFiles; @@ -394,7 +394,7 @@ void AssetExporter::writeMetadata() const { if (m_cancelled) { notifyProgress(1.0); - ExportNotification::addInfo(tr("Export cancelled.")); + ExportNotification::addInfo(tr("Export canceled.")); m_currentState.change(ParsingState::ExportingDone); return; } diff --git a/src/plugins/qmldesigner/boilerplate.qrc b/src/plugins/qmldesigner/boilerplate.qrc index 790f605c08e..a89643d6fff 100644 --- a/src/plugins/qmldesigner/boilerplate.qrc +++ b/src/plugins/qmldesigner/boilerplate.qrc @@ -7,5 +7,6 @@ qmlprojectmaincmakelists.tpl qmlprojectmodulecmakelists.tpl qmlprojectmainqml.tpl + qmlprojectappmainqml.tpl diff --git a/src/plugins/qmldesigner/cmakegeneratordialog.cpp b/src/plugins/qmldesigner/cmakegeneratordialog.cpp index eb7a35f170c..e8cf8e872f4 100644 --- a/src/plugins/qmldesigner/cmakegeneratordialog.cpp +++ b/src/plugins/qmldesigner/cmakegeneratordialog.cpp @@ -151,5 +151,5 @@ void CmakeGeneratorDialog::refreshNotificationText() } } -} -} +} //GenerateCmake +} //QmlDesigner diff --git a/src/plugins/qmldesigner/cmakeprojectconverter.cpp b/src/plugins/qmldesigner/cmakeprojectconverter.cpp new file mode 100644 index 00000000000..52448835771 --- /dev/null +++ b/src/plugins/qmldesigner/cmakeprojectconverter.cpp @@ -0,0 +1,391 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Design Tooling +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ +#include "cmakeprojectconverter.h" +#include "cmakeprojectconverterdialog.h" +#include "generatecmakelists.h" +#include "generatecmakelistsconstants.h" + +#include +#include + +#include +#include + +#include +#include +#include + +using namespace Utils; +using namespace QmlDesigner::GenerateCmake::Constants; + +namespace QmlDesigner { +namespace GenerateCmake { + +const QString MENU_ITEM_CONVERT = QCoreApplication::translate("QmlDesigner::CmakeProjectConverter", + "Export as Latest Project Format"); +const QString ERROR_TITLE = QCoreApplication::translate("QmlDesigner::CmakeProjectConverter", + "Creating Project"); +const QString SUCCESS_TITLE = QCoreApplication::translate("QmlDesigner::CmakeProjectConverter", + "Creating Project"); +const QString ERROR_TEXT = QCoreApplication::translate("QmlDesigner::CmakeProjectConverter", + "Creating project failed.\n%1"); +const QString SUCCESS_TEXT = QCoreApplication::translate("QmlDesigner::CmakeProjectConverter", + "Creating project succeeded."); + +void CmakeProjectConverter::generateMenuEntry() +{ + Core::ActionContainer *menu = + Core::ActionManager::actionContainer(Core::Constants::M_FILE); + auto action = new QAction(MENU_ITEM_CONVERT); + QObject::connect(action, &QAction::triggered, CmakeProjectConverter::onConvertProject); + Core::Command *cmd = Core::ActionManager::registerAction(action, "QmlProject.ConvertToCmakeProject"); + menu->addAction(cmd, Core::Constants::G_FILE_EXPORT); + + action->setEnabled(ProjectExplorer::SessionManager::startupProject() != nullptr); + QObject::connect(ProjectExplorer::SessionManager::instance(), + &ProjectExplorer::SessionManager::startupProjectChanged, [action]() { + action->setEnabled(ProjectExplorer::SessionManager::startupProject() != nullptr); + }); +} + +void CmakeProjectConverter::onConvertProject() +{ + ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject(); + const QmlProjectManager::QmlProject *qmlProject = + qobject_cast(project); + if (qmlProject) { + CmakeProjectConverterDialog dialog(qmlProject); + if (dialog.exec()) { + FilePath newProjectPath = dialog.newPath(); + CmakeProjectConverter converter; + converter.convertProject(qmlProject, newProjectPath); + } + } +} + +bool CmakeProjectConverter::convertProject(const QmlProjectManager::QmlProject *project, + const FilePath &targetDir) +{ + m_converterObjects.clear(); + m_projectDir = project->projectDirectory(); + m_newProjectDir = targetDir; + m_project = project; + + m_rootDirFiles = QStringList(FILENAME_FILTER_QMLPROJECT); + const QString confFile = GenerateCmake::projectEnvironmentVariable(ENV_VARIABLE_CONTROLCONF); + if (!confFile.isEmpty()) + m_rootDirFiles.append(confFile); + + bool retVal = prepareAndExecute(); + + if (retVal) + QMessageBox::information(nullptr, SUCCESS_TITLE, SUCCESS_TEXT); + else + QMessageBox::critical(nullptr, ERROR_TITLE, ERROR_TEXT.arg(m_errorText)); + + return retVal; +} + +bool CmakeProjectConverter::prepareAndExecute() +{ + GenerateCmake::CmakeFileGenerator cmakeGenerator; + + if (!performSanityCheck()) + return false; + if (!prepareBaseDirectoryStructure()) + return false; + if (!prepareCopy()) + return false; + if (!createPreparedProject()) + return false; + if (!cmakeGenerator.prepare(m_newProjectDir, false)) + return false; + if (!cmakeGenerator.execute()) + return false; + if (!modifyNewFiles()) + return false; + + return true; +} + +bool CmakeProjectConverter::isFileBlacklisted(const Utils::FilePath &file) const +{ + if (!file.fileName().compare(FILENAME_CMAKELISTS)) + return true; + if (!file.suffix().compare(FILENAME_SUFFIX_QMLPROJECT)) + return true; + if (!file.suffix().compare(FILENAME_SUFFIX_USER)) + return true; + if (m_rootDirFiles.contains(file.fileName())) + return true; + + return false; +} + +bool CmakeProjectConverter::isDirBlacklisted(const Utils::FilePath &dir) const +{ + if (!dir.isDir()) + return true; + + return false; +} + +const QString ERROR_CANNOT_WRITE_DIR = QCoreApplication::translate("QmlDesigner::CmakeProjectConverter", + "Unable to write to directory\n%1."); + +bool CmakeProjectConverter::performSanityCheck() +{ + if (!m_newProjectDir.parentDir().isWritableDir()) { + m_errorText = ERROR_CANNOT_WRITE_DIR.arg(m_newProjectDir.parentDir().toString()); + return false; + } + + return true; +} + +bool CmakeProjectConverter::prepareBaseDirectoryStructure() +{ + addDirectory(m_newProjectDir); + addDirectory(contentDir()); + addDirectory(sourceDir()); + addDirectory(importDir()); + addDirectory(assetDir()); + addDirectory(assetImportDir()); + addFile(contentDir().pathAppended(FILENAME_APPMAINQML)); + + return true; +} + +bool CmakeProjectConverter::prepareCopy() +{ + FilePaths rootFiles = m_projectDir.dirEntries({m_rootDirFiles, QDir::Files}); + for (const FilePath &file : rootFiles) { + addFile(file, m_newProjectDir.pathAppended(file.fileName())); + } + + prepareCopyDirFiles(m_projectDir, contentDir()); + + FilePaths subDirs = m_projectDir.dirEntries(QDir::Dirs|QDir::NoDotAndDotDot); + for (FilePath &subDir : subDirs) { + if (subDir.fileName() == DIRNAME_IMPORT) { + prepareCopyDirTree(subDir, importDir()); + } + else if (subDir.fileName() == DIRNAME_CPP) { + prepareCopyDirTree(subDir, sourceDir()); + } + else if (subDir.fileName() == DIRNAME_ASSET) { + prepareCopyDirTree(subDir, assetDir()); + } + else if (subDir.fileName() == DIRNAME_ASSETIMPORT) { + prepareCopyDirTree(subDir, assetImportDir()); + } + else { + prepareCopyDirTree(subDir, contentDir().pathAppended(subDir.fileName())); + } + } + + return true; +} + +bool CmakeProjectConverter::prepareCopyDirFiles(const FilePath &dir, const FilePath &targetDir) +{ + FilePaths dirFiles = dir.dirEntries(QDir::Files); + for (FilePath file : dirFiles) { + if (!isFileBlacklisted(file)) + addFile(file, targetDir.pathAppended(file.fileName())); + } + + return true; +} + +bool CmakeProjectConverter::prepareCopyDirTree(const FilePath &dir, const FilePath &targetDir) +{ + prepareCopyDirFiles(dir, targetDir); + FilePaths subDirs = dir.dirEntries(QDir::Dirs|QDir::NoDotAndDotDot); + for (FilePath &subDir : subDirs) { + if (isDirBlacklisted(subDir)) + continue; + addDirectory(targetDir.pathAppended(subDir.fileName())); + prepareCopyDirFiles(subDir, targetDir.pathAppended(subDir.fileName())); + prepareCopyDirTree(subDir, targetDir.pathAppended(subDir.fileName())); + } + + return true; +} + +bool CmakeProjectConverter::addDirectory(const Utils::FilePath &target) +{ + return addObject(ProjectConverterObjectType::Directory, FilePath(), target); +} + +bool CmakeProjectConverter::addFile(const Utils::FilePath &target) +{ + return addFile(FilePath(), target); +} + +bool CmakeProjectConverter::addFile(const Utils::FilePath &original, const Utils::FilePath &target) +{ + addDirectory(target.parentDir()); + return addObject(ProjectConverterObjectType::File, original, target); +} + +bool CmakeProjectConverter::addObject(ProjectConverterObjectType type, + const Utils::FilePath &original, const Utils::FilePath &target) +{ + if (target.isChildOf(m_projectDir)) + return false; + + if (!target.isChildOf(m_newProjectDir) && + ((type == ProjectConverterObjectType::Directory) && (target != m_newProjectDir))) { + return false; + } + + for (ProjectConverterObject &o : m_converterObjects) { + if (o.target == target) + return false; + } + + ProjectConverterObject object; + object.type = type; + object.target = target; + object.original = original; + + m_converterObjects.append(object); + + return true; +} + +bool CmakeProjectConverter::createPreparedProject() +{ + for (ProjectConverterObject &pco : m_converterObjects) { + if (pco.type == ProjectConverterObjectType::Directory) { + pco.target.createDir(); + } + else if (pco.type == ProjectConverterObjectType::File) { + if (pco.original.isEmpty()) { + QFile newFile(pco.target.toString()); + newFile.open(QIODevice::WriteOnly); + newFile.close(); + } + else { + pco.original.copyFile(pco.target); + } + } + } + + return true; +} + +const FilePath CmakeProjectConverter::contentDir() +{ + return m_newProjectDir.pathAppended(DIRNAME_CONTENT); +} + +const FilePath CmakeProjectConverter::sourceDir() +{ + return m_newProjectDir.pathAppended(DIRNAME_CPP); +} + +const FilePath CmakeProjectConverter::importDir() +{ + return m_newProjectDir.pathAppended(DIRNAME_IMPORT); +} + +const FilePath CmakeProjectConverter::assetDir() +{ + return contentDir().pathAppended(DIRNAME_ASSET); +} + +const FilePath CmakeProjectConverter::assetImportDir() +{ + return m_newProjectDir.pathAppended(DIRNAME_ASSETIMPORT); +} + +const FilePath CmakeProjectConverter::projectMainFile() const +{ + auto *target = m_project->activeTarget(); + if (target && target->buildSystem()) { + auto buildSystem = qobject_cast(target->buildSystem()); + if (buildSystem) { + return buildSystem->mainFilePath(); + } + } + return {}; +} + +const QString CmakeProjectConverter::projectMainClass() const +{ + return projectMainFile().baseName(); +} + +bool CmakeProjectConverter::modifyNewFiles() +{ + return modifyAppMainQml() && modifyProjectFile(); +} + +const char APPMAIN_QMLFILE_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectappmainqml.tpl"; + +bool CmakeProjectConverter::modifyAppMainQml() +{ + QString appMainQmlPath = contentDir().pathAppended(FILENAME_APPMAINQML).toString(); + QFile appMainQml(appMainQmlPath); + appMainQml.open(QIODevice::ReadWrite); + if (!appMainQml.isOpen()) + return false; + + QString templateContent = GenerateCmake::readTemplate(APPMAIN_QMLFILE_TEMPLATE_PATH); + QString appMainQmlContent = templateContent.arg(projectMainClass()); + + appMainQml.reset(); + appMainQml.write(appMainQmlContent.toUtf8()); + appMainQml.close(); + + return true; +} + +bool CmakeProjectConverter::modifyProjectFile() +{ + QString projectFileName = m_project->projectFilePath().fileName(); + FilePath projectFilePath = m_newProjectDir.pathAppended(projectFileName); + QFile projectFile(projectFilePath.toString()); + projectFile.open(QIODevice::ReadWrite); + if (!projectFile.isOpen()) + return false; + + QString projectFileContent = QString::fromUtf8(projectFile.readAll()); + const QRegularExpression mainFilePattern("^\\s*mainFile:\\s*\".*\"", QRegularExpression::MultilineOption); + const QString mainFileString(" mainFile: \"content/App.qml\""); + + projectFileContent.replace(mainFilePattern, mainFileString); + + projectFile.reset(); + projectFile.write(projectFileContent.toUtf8()); + projectFile.close(); + + return true; +} + +} //GenerateCmake +} //QmlDesigner diff --git a/src/plugins/qmldesigner/cmakeprojectconverter.h b/src/plugins/qmldesigner/cmakeprojectconverter.h new file mode 100644 index 00000000000..4b21d65ac8b --- /dev/null +++ b/src/plugins/qmldesigner/cmakeprojectconverter.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Design Tooling +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#ifndef CMAKEPROJECTCONVERTER_H +#define CMAKEPROJECTCONVERTER_H + +#include +#include + +namespace QmlDesigner { + +namespace GenerateCmake { + +enum ProjectConverterObjectType { + File, + Directory +}; + +struct ProjectConverterObject { + ProjectConverterObjectType type; + Utils::FilePath target; + Utils::FilePath original; +}; + +class CmakeProjectConverter { + +public: + bool convertProject(const QmlProjectManager::QmlProject *project, + const Utils::FilePath &targetDir); + static void generateMenuEntry(); + static void onConvertProject(); + +private: + bool prepareAndExecute(); + bool isFileBlacklisted(const Utils::FilePath &file) const; + bool isDirBlacklisted(const Utils::FilePath &dir) const; + bool performSanityCheck(); + bool prepareBaseDirectoryStructure(); + bool prepareCopyDirFiles(const Utils::FilePath &dir, const Utils::FilePath &targetDir); + bool prepareCopyDirTree(const Utils::FilePath &dir, const Utils::FilePath &targetDir); + bool prepareCopy(); + bool addDirectory(const Utils::FilePath &target); + bool addFile(const Utils::FilePath &target); + bool addFile(const Utils::FilePath &original, const Utils::FilePath &target); + bool addObject(ProjectConverterObjectType type, + const Utils::FilePath &original, const Utils::FilePath &target); + bool createPreparedProject(); + + const Utils::FilePath contentDir(); + const Utils::FilePath sourceDir(); + const Utils::FilePath importDir(); + const Utils::FilePath assetDir(); + const Utils::FilePath assetImportDir(); + + const QString environmentVariable(const QString &key) const; + const Utils::FilePath projectMainFile() const; + const QString projectMainClass() const; + bool modifyNewFiles(); + bool modifyAppMainQml(); + bool modifyProjectFile(); + +private: + QList m_converterObjects; + QStringList m_rootDirFiles; + Utils::FilePath m_projectDir; + Utils::FilePath m_newProjectDir; + const QmlProjectManager::QmlProject *m_project; + QString m_errorText; +}; + +} //GenerateCmake +} //QmlDesigner + +#endif // CMAKEPROJECTCONVERTER_H diff --git a/src/plugins/qmldesigner/cmakeprojectconverterdialog.cpp b/src/plugins/qmldesigner/cmakeprojectconverterdialog.cpp new file mode 100644 index 00000000000..1c15043779c --- /dev/null +++ b/src/plugins/qmldesigner/cmakeprojectconverterdialog.cpp @@ -0,0 +1,227 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Design Tooling +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "cmakeprojectconverterdialog.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace Utils; + +namespace QmlDesigner { +namespace GenerateCmake { + +const QRegularExpression projectNameRegexp("^(?!(import))(?!(QtQml))(?!(QtQuick))(?:[A-Z][a-zA-Z0-9-_]*)$"); + +static bool projectNameValidationFunction(FancyLineEdit *editor, QString *) +{ + return editor->text().count(projectNameRegexp); +} + +static bool dirValidationFunction(FancyLineEdit *editor, QString *) +{ + return FilePath::fromString(editor->text()).isWritableDir(); +} + +const QString EXPLANATION_TEXT = QCoreApplication::translate( + "QmlDesigner::CmakeProjectConverterDialog", + "This process creates a copy of the existing project. The new project's folder structure is adjusted for CMake build process and necessary related new files are generated.\n\nThe new project can be opened in Qt Creator using the main CMakeLists.txt file."); + +const QString PROJECT_NAME_LABEL = QCoreApplication::translate( + "QmlDesigner::CmakeProjectConverterDialog", + "Name:"); + +const QString PARENT_DIR_LABEL = QCoreApplication::translate( + "QmlDesigner::CmakeProjectConverterDialog", + "Create in:"); + +CmakeProjectConverterDialog::CmakeProjectConverterDialog(const QmlProjectManager::QmlProject *oldProject) + : QDialog() +{ + const FilePath defaultDir = Core::DocumentManager::projectsDirectory(); + const QString defaultName = uniqueProjectName(defaultDir, oldProject->displayName()); + + QLabel *mainLabel = new QLabel(EXPLANATION_TEXT, this); + mainLabel->setWordWrap(true); + + mainLabel->setMargin(20); + mainLabel->setMinimumWidth(600); + + m_errorLabel = new InfoLabel(); + m_errorLabel->setType(InfoLabel::InfoType::None); + + m_nameEditor = new FancyLineEdit(); + m_nameEditor->setValidationFunction(projectNameValidationFunction); + m_nameEditor->setText(defaultName); + + m_dirSelector = new PathChooser(); + m_dirSelector->setExpectedKind(PathChooser::Directory); + m_dirSelector->setValidationFunction(dirValidationFunction); + m_dirSelector->setPath(defaultDir.toString()); + + QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); + m_okButton = buttons->button(QDialogButtonBox::Ok); + m_okButton->setDefault(true); + + connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); + connect(m_nameEditor, &FancyLineEdit::textChanged, this, &CmakeProjectConverterDialog::pathValidChanged); + connect(m_dirSelector->lineEdit(), &FancyLineEdit::textChanged, this, &CmakeProjectConverterDialog::pathValidChanged); + + QGroupBox *form = new QGroupBox(); + QFormLayout *formLayout = new QFormLayout(form); + formLayout->addRow(PROJECT_NAME_LABEL, m_nameEditor); + formLayout->addRow(PARENT_DIR_LABEL, m_dirSelector); + + QVBoxLayout *dialogLayout = new QVBoxLayout(this); + dialogLayout->addWidget(mainLabel); + dialogLayout->addWidget(form); + dialogLayout->addWidget(m_errorLabel); + dialogLayout->addWidget(buttons); + + pathValidChanged(); +} + +void CmakeProjectConverterDialog::pathValidChanged() +{ + bool valid = isValid(); + + if (valid) { + m_newProjectDir = FilePath::fromString(m_dirSelector->path()).pathAppended(m_nameEditor->text()); + } + else { + m_newProjectDir = FilePath(); + } + + const QString error = errorText(); + m_errorLabel->setType(error.isEmpty() ? InfoLabel::None : InfoLabel::Warning); + m_errorLabel->setText(error); + m_okButton->setEnabled(valid); +} + +const FilePath CmakeProjectConverterDialog::newPath() const +{ + return m_newProjectDir; +} + +const QStringList blackListedStarts = {"import","QtQml","QtQuick"}; + +const QString CmakeProjectConverterDialog::startsWithBlacklisted(const QString &text) const +{ + for (const QString &badWord : blackListedStarts) { + if (text.startsWith(badWord)) + return badWord; + } + + return {}; +} + +const QString ERROR_TEXT_NAME_EMPTY = QCoreApplication::translate( + "QmlDesigner::CmakeProjectConverterDialog", + "Name is empty."); +const QString ERROR_TEXT_NAME_BAD_START = QCoreApplication::translate( + "QmlDesigner::CmakeProjectConverterDialog", + "Name must not start with \"%1\"."); +const QString ERROR_TEXT_NAME_LOWERCASE_START = QCoreApplication::translate( + "QmlDesigner::CmakeProjectConverterDialog", + "Name must begin with a capital letter"); +const QString ERROR_TEXT_NAME_BAD_CHARACTERS = QCoreApplication::translate( + "QmlDesigner::CmakeProjectConverterDialog", + "Name must contain only letters, numbers or characters - _."); + +const QString ERROR_DIR_NOT_DIR = QCoreApplication::translate( + "QmlDesigner::CmakeProjectConverterDialog", + "Target is not a directory."); +const QString ERROR_DIR_NOT_WRITABLE = QCoreApplication::translate( + "QmlDesigner::CmakeProjectConverterDialog", + "Cannot write to target directory."); +const QString ERROR_DIR_EXISTS = QCoreApplication::translate( + "QmlDesigner::CmakeProjectConverterDialog", + "Project directory already exists."); + +const QString CmakeProjectConverterDialog::errorText() const +{ + QString text; + + if (!m_nameEditor->isValid()) { + QString name = m_nameEditor->text(); + + if (name.isEmpty()) + return ERROR_TEXT_NAME_EMPTY; + + const QString badStart = startsWithBlacklisted(text); + if (!badStart.isEmpty()) + return ERROR_TEXT_NAME_BAD_START.arg(badStart); + + if (name[0].isLower()) + return ERROR_TEXT_NAME_LOWERCASE_START; + + return ERROR_TEXT_NAME_BAD_CHARACTERS; + + } + + if (!m_dirSelector->isValid()) { + FilePath path = m_dirSelector->filePath(); + if (!path.isDir()) + return ERROR_DIR_NOT_DIR; + if (!path.isWritableDir()) + return ERROR_DIR_NOT_WRITABLE; + } + + if (FilePath::fromString(m_dirSelector->path()).pathAppended(m_nameEditor->text()).exists()) + return ERROR_DIR_EXISTS; + + + return text; +} + +const QString CmakeProjectConverterDialog::uniqueProjectName(const FilePath &dir, const QString &oldName) const +{ + for (unsigned i = 0; ; ++i) { + QString name = oldName; + if (i) + name += QString::number(i); + if (!dir.pathAppended(name).exists()) + return name; + } + return oldName; +} + +bool CmakeProjectConverterDialog::isValid() +{ + FilePath newPath = FilePath::fromString(m_dirSelector->path()).pathAppended(m_nameEditor->text()); + return m_dirSelector->isValid() && m_nameEditor->isValid() && !newPath.exists(); +} + +} //GenerateCmake +} //QmlDesigner diff --git a/src/plugins/qmldesigner/cmakeprojectconverterdialog.h b/src/plugins/qmldesigner/cmakeprojectconverterdialog.h new file mode 100644 index 00000000000..a3143de7cd8 --- /dev/null +++ b/src/plugins/qmldesigner/cmakeprojectconverterdialog.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Design Tooling +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + + +#ifndef CMAKEPROJECTCONVERTERDIALOG_H +#define CMAKEPROJECTCONVERTERDIALOG_H + +#include +#include +#include +#include +#include + +#include + +namespace QmlDesigner { +namespace GenerateCmake { + +class CmakeProjectConverterDialog : public QDialog +{ + Q_OBJECT + +public: + CmakeProjectConverterDialog(const QmlProjectManager::QmlProject *oldProject); + const Utils::FilePath newPath() const; + +public slots: + void pathValidChanged(); + +private: + const QString startsWithBlacklisted(const QString &text) const; + const QString errorText() const; + const QString uniqueProjectName(const Utils::FilePath &dir, const QString &oldName) const; + bool isValid(); + +private: + Utils::FilePath m_newProjectDir; + Utils::FancyLineEdit *m_nameEditor; + Utils::PathChooser *m_dirSelector; + Utils::InfoLabel *m_errorLabel; + QPushButton *m_okButton; +}; + +} //GenerateCmake +} //Qmldesigner + +#endif // CMAKEPROJECTCONVERTERDIALOG_H diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index 5327098cf3a..79242905905 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -989,15 +989,6 @@ void addTabBarToStackedContainer(const SelectionContext &selectionContext) } -Utils::FilePath projectFilePath() -{ - if (auto *doc = QmlDesignerPlugin::instance()->documentManager().currentDesignDocument()) { - if (auto *proj = ProjectExplorer::SessionManager::projectForFile(doc->fileName())) - return proj->projectDirectory(); - } - return Utils::FilePath(); -} - static AddFilesResult addFilesToProject(const QStringList &fileNames, const QString &defaultDirectory) { QString directory = AddImagesDialog::getDirectory(fileNames, defaultDirectory); @@ -1050,10 +1041,10 @@ static QString getAssetDefaultDirectory(const QString &assetDir, const QString & { QString adjustedDefaultDirectory = defaultDirectory; - Utils::FilePath contentPath = projectFilePath(); + Utils::FilePath contentPath = QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath(); if (contentPath.pathAppended("content").exists()) - contentPath = contentPath.pathAppended("content"); + contentPath = contentPath.pathAppended("content"); Utils::FilePath assetPath = contentPath.pathAppended(assetDir); diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index d88cf612c15..945820d9134 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -150,7 +150,7 @@ Edit3DWidget::Edit3DWidget(Edit3DView *view) : tr("Your file does not import Qt Quick 3D.

" "To create a 3D view, add the QtQuick3D module in the Library view. Or click" " here " - "here to add it immediately.

" + "to add it immediately.

" "To import 3D assets from another tool, click the \"Add New Assets...\" button in the Assets tab of the Library view."); m_onboardingLabel->setText(labelText.arg(Utils::creatorTheme()->color(Utils::Theme::TextColorLink).name())); m_onboardingLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); diff --git a/src/plugins/qmldesigner/components/eventlist/eventlistactions.cpp b/src/plugins/qmldesigner/components/eventlist/eventlistactions.cpp index 823ce30da3b..a23b8f83a20 100644 --- a/src/plugins/qmldesigner/components/eventlist/eventlistactions.cpp +++ b/src/plugins/qmldesigner/components/eventlist/eventlistactions.cpp @@ -33,9 +33,18 @@ namespace QmlDesigner { -inline bool eventListEnabled(const SelectionContext &) +inline bool eventListEnabled(const SelectionContext &context) { - return EventList::hasEventListModel(); + static ModelNode lastRootNode; + static bool lastValue = false; + + if (lastRootNode == context.rootNode()) + return lastValue; + + lastRootNode = context.rootNode(); + lastValue = EventList::hasEventListModel(); + + return lastValue; } QIcon eventListIconFromIconFont(Theme::Icon iconType) diff --git a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp index 4f000e12953..95d92e68fe9 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp @@ -2226,4 +2226,39 @@ void FormEditorFlowWildcardItem::paint(QPainter *painter, const QStyleOptionGrap FormEditorFlowDecisionItem::paint(painter, option, widget); } +void FormEditor3dPreview::updateGeometry() +{ + prepareGeometryChange(); + + m_selectionBoundingRect = qmlItemNode().instanceBoundingRect().adjusted(0, 0, 1., 1.); + m_boundingRect = qmlItemNode().instanceBoundingRect(); + m_paintedBoundingRect = m_boundingRect; + setTransform(QTransform()); + +} + +QPointF FormEditor3dPreview::instancePosition() const +{ + return QPointF(0, 0); +} + +void FormEditor3dPreview::paint(QPainter *painter, + const QStyleOptionGraphicsItem *option, + QWidget *widget) +{ + if (!painter->isActive()) + return; + + painter->save(); + + bool showPlaceHolder = qmlItemNode().instanceIsRenderPixmapNull(); + + if (showPlaceHolder) + paintPlaceHolderForInvisbleItem(painter); + else + painter->drawPixmap(m_boundingRect.topLeft(), qmlItemNode().instanceRenderPixmap()); + + painter->restore(); +} + } //QmlDesigner diff --git a/src/plugins/qmldesigner/components/formeditor/formeditoritem.h b/src/plugins/qmldesigner/components/formeditor/formeditoritem.h index 2213f42a664..a9e70145e2e 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditoritem.h +++ b/src/plugins/qmldesigner/components/formeditor/formeditoritem.h @@ -171,6 +171,23 @@ private: QPointF m_oldPos; }; +class FormEditor3dPreview : public FormEditorItem +{ + friend FormEditorScene; + +public: + void updateGeometry() override; + QPointF instancePosition() const override; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; + +protected: + FormEditor3dPreview(const QmlItemNode &qmlItemNode, FormEditorScene *scene) + : FormEditorItem(qmlItemNode, scene) + { + setHighlightBoundingRect(true); + } +}; + class FormEditorFlowActionItem : public FormEditorItem { friend FormEditorScene; diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorscene.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorscene.cpp index bbc1f283e1f..dea52631d85 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorscene.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditorscene.cpp @@ -173,7 +173,9 @@ FormEditorItem *FormEditorScene::addFormEditorItem(const QmlItemNode &qmlItemNod { FormEditorItem *formEditorItem = nullptr; - if (type == Flow) + if (type == Preview3d) + formEditorItem = new FormEditor3dPreview(qmlItemNode, this); + else if (type == Flow) formEditorItem = new FormEditorFlowItem(qmlItemNode, this); else if (type == FlowAction) formEditorItem = new FormEditorFlowActionItem(qmlItemNode, this); diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorscene.h b/src/plugins/qmldesigner/components/formeditor/formeditorscene.h index 070d5877762..2d15af77a23 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorscene.h +++ b/src/plugins/qmldesigner/components/formeditor/formeditorscene.h @@ -58,7 +58,8 @@ public: FlowAction, FlowTransition, FlowDecision, - FlowWildcard + FlowWildcard, + Preview3d }; FormEditorScene(FormEditorWidget *widget, FormEditorView *editorView); diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp index bfe6f373838..93a072cf17c 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include #include @@ -655,6 +656,14 @@ static void updateTransitions(FormEditorScene *scene, const QmlItemNode &qmlItem void FormEditorView::instancesCompleted(const QVector &completedNodeList) { + if (Qml3DNode::isValidQml3DNode(rootModelNode())) { + if (completedNodeList.contains(rootModelNode())) { + FormEditorItem *item = scene()->itemForQmlItemNode(rootModelNode()); + if (item) + scene()->synchronizeTransformation(item); + } + } + const bool isFlow = rootModelNode().isValid() && QmlItemNode(rootModelNode()).isFlowView(); QList itemNodeList; for (const ModelNode &node : completedNodeList) { @@ -724,6 +733,10 @@ void FormEditorView::instancesRenderImageChanged(const QVector &nodeL if (QmlItemNode::isValidQmlItemNode(node)) if (FormEditorItem *item = scene()->itemForQmlItemNode(QmlItemNode(node))) item->update(); + if (Qml3DNode::isValidQml3DNode(node)) { + if (FormEditorItem *item = scene()->itemForQmlItemNode(node)) + item->update(); + } } } @@ -795,6 +808,9 @@ void FormEditorView::setupFormEditorWidget() if (QmlItemNode::isValidQmlItemNode(rootModelNode())) setupFormEditorItemTree(rootModelNode()); + if (Qml3DNode::isValidQml3DNode(rootModelNode())) + setupFormEditor3DView(); + m_formEditorWidget->initialize(); if (!rewriterView()->errors().isEmpty()) @@ -903,7 +919,8 @@ void FormEditorView::checkRootModelNode() QTC_ASSERT(rootModelNode().isValid(), return); - if (!rootModelNode().metaInfo().isGraphicalItem()) + if (!rootModelNode().metaInfo().isGraphicalItem() + && !Qml3DNode::isValidQml3DNode(rootModelNode())) m_formEditorWidget->showErrorMessageBox( {DocumentMessage(tr("%1 is not supported as the root element by Form Editor.") .arg(rootModelNode().simplifiedTypeName()))}); @@ -911,6 +928,13 @@ void FormEditorView::checkRootModelNode() m_formEditorWidget->hideErrorMessageBox(); } +void FormEditorView::setupFormEditor3DView() +{ + m_scene->addFormEditorItem(rootModelNode(), FormEditorScene::Preview3d); + FormEditorItem *item = m_scene->itemForQmlItemNode(rootModelNode()); + item->updateGeometry(); +} + void FormEditorView::reset() { QTimer::singleShot(200, this, &FormEditorView::delayedReset); diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorview.h b/src/plugins/qmldesigner/components/formeditor/formeditorview.h index 1db4ae1b4d1..c15f73498b5 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorview.h +++ b/src/plugins/qmldesigner/components/formeditor/formeditorview.h @@ -151,6 +151,7 @@ private: void resetNodeInstanceView(); void addOrRemoveFormEditorItem(const ModelNode &node); void checkRootModelNode(); + void setupFormEditor3DView(); QPointer m_formEditorWidget; QPointer m_scene; diff --git a/src/plugins/qmldesigner/components/integration/designdocument.cpp b/src/plugins/qmldesigner/components/integration/designdocument.cpp index f32a66b832f..e9e76c29cba 100644 --- a/src/plugins/qmldesigner/components/integration/designdocument.cpp +++ b/src/plugins/qmldesigner/components/integration/designdocument.cpp @@ -344,7 +344,7 @@ Utils::FilePath DesignDocument::projectFolder() const bool DesignDocument::hasProject() const { - return ProjectExplorer::SessionManager::projectForFile(fileName()); + return !DocumentManager::currentProjectDirPath().isEmpty(); } void DesignDocument::changeToInFileComponentModel(ComponentTextModifier *textModifer) diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp index 79e66976a97..654a18dfc9e 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp @@ -768,7 +768,7 @@ void NavigatorTreeModel::handleItemLibraryItemDrop(const QMimeData *mimeData, in "NavigatorTreeModel", "Inserting materials under imported 3D component nodes is not supported. " "Materials used in imported 3D components have to be modified inside the component itself.\n\n" - "Would you like to go into component '%1'?") + "Would you like to go into component \"%1\"?") .arg(targetProperty.parentModelNode().id()), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp index 30fcb8981c2..e5c9fadc530 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp +++ b/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp @@ -81,7 +81,7 @@ TextEditorView::TextEditorView(QObject *parent) Core::Command *command = Core::ActionManager::registerAction(completionAction, TextEditor::Constants::COMPLETE_THIS, context); command->setDefaultKeySequence(QKeySequence(Core::useMacShortcuts ? tr("Meta+Space") : tr("Ctrl+Space"))); - connect(completionAction, &QAction::triggered, [this]() { + connect(completionAction, &QAction::triggered, this, [this] { if (m_widget->textEditor()) m_widget->textEditor()->editorWidget()->invokeAssist(TextEditor::Completion); }); @@ -99,16 +99,12 @@ void TextEditorView::modelAttached(Model *model) AbstractView::modelAttached(model); - auto textEditor = qobject_cast( + auto textEditor = qobject_cast( QmlDesignerPlugin::instance()->currentDesignDocument()->textEditor()->duplicate()); + // Set the context of the text editor, but we add another special context to override shortcuts. Core::Context context = textEditor->context(); context.prepend(TEXTEDITOR_CONTEXT_ID); - - /* - * Set the context of the text editor, but we add another special context to override shortcuts. - */ - m_textEditorContext->setContext(context); m_widget->setTextEditor(textEditor); @@ -272,10 +268,9 @@ void TextEditorView::reformatFile() auto document = qobject_cast(Core::EditorManager::currentDocument()); - /* Reformat document if we have a .ui.qml file */ - if (document - && document->filePath().toString().endsWith(".ui.qml") - && DesignerSettings::getValue(DesignerSettingsKey::REFORMAT_UI_QML_FILES).toBool()) { + // Reformat document if we have a .ui.qml file + if (document && document->filePath().toString().endsWith(".ui.qml") + && DesignerSettings::getValue(DesignerSettingsKey::REFORMAT_UI_QML_FILES).toBool()) { QmlJS::Document::Ptr currentDocument(document->semanticInfo().document); QmlJS::Snapshot snapshot = QmlJS::ModelManagerInterface::instance()->snapshot(); @@ -315,4 +310,3 @@ void TextEditorView::instancePropertyChanged(const QList #include - +#include #include #include @@ -52,13 +52,17 @@ TextEditorWidget::TextEditorWidget(TextEditorView *textEditorView) : QWidget() , m_textEditorView(textEditorView) , m_statusBar(new TextEditorStatusBar(this)) + , m_findToolBar(new Core::FindToolBarPlaceHolder(this)) + , m_layout(new QVBoxLayout(this)) { setAcceptDrops(true); - QBoxLayout *layout = new QVBoxLayout(this); - layout->setContentsMargins(0, 0, 0, 0); - layout->setSpacing(0); - layout->addWidget(m_statusBar); + m_statusBar->hide(); + + m_layout->setContentsMargins(0, 0, 0, 0); + m_layout->setSpacing(0); + m_layout->addWidget(m_statusBar); + m_layout->addWidget(m_findToolBar); m_updateSelectionTimer.setSingleShot(true); m_updateSelectionTimer.setInterval(200); @@ -72,24 +76,22 @@ void TextEditorWidget::setTextEditor(TextEditor::BaseTextEditor *textEditor) m_textEditor.reset(textEditor); if (textEditor) { - layout()->removeWidget(m_statusBar); - layout()->addWidget(textEditor->editorWidget()); - layout()->addWidget(m_statusBar); + m_layout->insertWidget(0, textEditor->editorWidget()); + setFocusProxy(textEditor->editorWidget()); QmlDesignerPlugin::instance()->emitCurrentTextEditorChanged(textEditor); - connect(textEditor->editorWidget(), &QPlainTextEdit::cursorPositionChanged, - this, [this]() { - /* Cursor position is changed by rewriter */ + connect(textEditor->editorWidget(), &QPlainTextEdit::cursorPositionChanged, this, [this] { + // Cursor position is changed by rewriter if (!m_blockCursorSelectionSynchronisation) m_updateSelectionTimer.start(); }); textEditor->editorWidget()->installEventFilter(this); + static QString styleSheet = Theme::replaceCssColors( - QString::fromUtf8(Utils::FileReader::fetchQrc( - ":/qmldesigner/scrollbar.css"))); + QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/scrollbar.css"))); textEditor->editorWidget()->verticalScrollBar()->setStyleSheet(styleSheet); textEditor->editorWidget()->horizontalScrollBar()->setStyleSheet(styleSheet); } @@ -105,7 +107,6 @@ void TextEditorWidget::contextHelp(const Core::IContext::HelpCallback &callback) void TextEditorWidget::updateSelectionByCursorPosition() { - /* Because of the timer we have to be careful. */ if (!m_textEditorView->model()) return; @@ -162,11 +163,13 @@ void TextEditorWidget::gotoCursorPosition(int line, int column) void TextEditorWidget::setStatusText(const QString &text) { m_statusBar->setText(text); + m_statusBar->setVisible(!text.isEmpty()); } void TextEditorWidget::clearStatusBar() { m_statusBar->clearText(); + m_statusBar->hide(); } int TextEditorWidget::currentLine() const @@ -199,21 +202,25 @@ bool TextEditorWidget::eventFilter(QObject *, QEvent *event) auto keyEvent = static_cast(event); if (std::find(overrideKeys.begin(), overrideKeys.end(), keyEvent->key()) != overrideKeys.end()) { + if (keyEvent->key() == Qt::Key_Escape) + m_findToolBar->hide(); + keyEvent->accept(); return true; } static const Qt::KeyboardModifiers relevantModifiers = Qt::ShiftModifier - | Qt::ControlModifier - | Qt::AltModifier - | Qt::MetaModifier; + | Qt::ControlModifier + | Qt::AltModifier + | Qt::MetaModifier; - QKeySequence keySqeuence(keyEvent->key() | (keyEvent->modifiers() & relevantModifiers)); - for (const QKeySequence &overrideSequence : overrideSequences) + const QKeySequence keySqeuence(keyEvent->key() | (keyEvent->modifiers() & relevantModifiers)); + for (const QKeySequence &overrideSequence : overrideSequences) { if (keySqeuence.matches(overrideSequence)) { keyEvent->accept(); return true; } + } } return false; } diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.h b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.h index 96a8361069f..efd51d8afc2 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.h +++ b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.h @@ -26,11 +26,15 @@ #include -#include #include +#include +#include #include +namespace Core { +class FindToolBarPlaceHolder; +} namespace QmlDesigner { class TextEditorView; @@ -72,7 +76,9 @@ private: std::unique_ptr m_textEditor; QPointer m_textEditorView; QTimer m_updateSelectionTimer; - TextEditorStatusBar *m_statusBar; + TextEditorStatusBar *m_statusBar = nullptr; + Core::FindToolBarPlaceHolder *m_findToolBar = nullptr; + QVBoxLayout *m_layout = nullptr; bool m_blockCursorSelectionSynchronisation = false; bool m_blockRoundTrip = false; }; diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp index 09eb2620943..06b83b44b35 100644 --- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp @@ -301,7 +301,7 @@ ModelNode TransitionEditorView::addNewTransition() if (!properties.isEmpty()) properties.chop(2); Core::AsynchronousMessageBox::warning( - tr("No properties to animate found."), + tr("No Property Changes to Animate"), tr("To add transitions, first change the properties that you want to animate in states (%1).") .arg(properties)); } diff --git a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp index a001a8b0c29..9ff3289e459 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp @@ -69,10 +69,38 @@ SubComponentManager::SubComponentManager(Model *model, QObject *parent) this, [this](const QString &path) { parseDirectory(path); }); } +QString findFolderForImport(const QStringList &importPaths, const QString &url) +{ + if (url.isEmpty()) + return {}; + + QString folderUrl = url; + folderUrl.replace('.', '/'); + + for (const QString &path : importPaths) { + QFileInfo dirInfo = QFileInfo(path + QLatin1Char('/') + folderUrl); + + if (dirInfo.exists() && dirInfo.isDir()) + return dirInfo.canonicalFilePath(); + + const QDir parentDir = dirInfo.dir(); + if (parentDir.exists()) { + const QStringList parts = url.split('.'); + const QString lastFolder = parts.last(); + + const QStringList candidates = parentDir.entryList({lastFolder + ".*"}, QDir::Dirs); + if (!candidates.isEmpty()) + return parentDir.canonicalPath() + "/" + candidates.first(); + } + } + return {}; +} bool SubComponentManager::addImport(const Import &import, int index) { - if (debug) + if (debug) { qDebug() << Q_FUNC_INFO << index << import.file().toUtf8(); + qDebug() << Q_FUNC_INFO << index << import.url(); + } bool importExists = false; if (import.isFileImport()) { @@ -85,19 +113,11 @@ bool SubComponentManager::addImport(const Import &import, int index) } } else { QString url = import.url(); - - url.replace(QLatin1Char('.'), QLatin1Char('/')); - - foreach (const QString &path, importPaths()) { - QFileInfo dirInfo = QFileInfo(path + QLatin1Char('/') + url); - if (dirInfo.exists() && dirInfo.isDir()) { - const QString canonicalDirPath = dirInfo.canonicalFilePath(); - m_watcher.addPath(canonicalDirPath); - importExists = true; - //m_dirToQualifier.insertMulti(canonicalDirPath, import.qualifier()); ### todo: proper support for import as - } + const QString result = findFolderForImport(importPaths(), url); + if (!result.isEmpty()) { + m_watcher.addPath(result); + importExists = true; } - // TODO: QDeclarativeDomImport::Library } if (importExists) { diff --git a/src/plugins/qmldesigner/documentmanager.cpp b/src/plugins/qmldesigner/documentmanager.cpp index d75d570fc92..453dd96232a 100644 --- a/src/plugins/qmldesigner/documentmanager.cpp +++ b/src/plugins/qmldesigner/documentmanager.cpp @@ -358,11 +358,18 @@ Utils::FilePath DocumentManager::currentProjectDirPath() return {}; Utils::FilePath qmlFileName = QmlDesignerPlugin::instance()->currentDesignDocument()->fileName(); - ProjectExplorer::Project *project = ProjectExplorer::SessionManager::projectForFile(qmlFileName); - if (!project) - return {}; - return project->projectDirectory(); + ProjectExplorer::Project *project = ProjectExplorer::SessionManager::projectForFile(qmlFileName); + if (project) + return project->projectDirectory(); + + const QList projects = ProjectExplorer::SessionManager::projects(); + for (auto p : projects) { + if (qmlFileName.startsWith(p->projectDirectory().toString())) + return p->projectDirectory(); + } + + return {}; } QStringList DocumentManager::isoIconsQmakeVariableValue(const QString &proPath) diff --git a/src/plugins/qmldesigner/generatecmakelists.cpp b/src/plugins/qmldesigner/generatecmakelists.cpp index 1dc5cd1bc81..a585f2d2d40 100644 --- a/src/plugins/qmldesigner/generatecmakelists.cpp +++ b/src/plugins/qmldesigner/generatecmakelists.cpp @@ -66,24 +66,26 @@ enum ProjectDirectoryError { NoError = 0, MissingContentDir = 1<<1, MissingImportDir = 1<<2, - MissingAssetImportDir = 1<<3, - MissingCppDir = 1<<4, - MissingMainCMake = 1<<5, - MissingMainQml = 1<<6, - MissingAppMainQml = 1<<7, - MissingQmlModules = 1<<8, - MissingMainCpp = 1<<9, - MissingMainCppHeader = 1<<10, - MissingEnvHeader = 1<<11 + MissingAssetDir = 1<<3, + MissingAssetImportDir = 1<<4, + MissingCppDir = 1<<5, + MissingMainCMake = 1<<6, + MissingMainQml = 1<<7, + MissingAppMainQml = 1<<8, + MissingQmlModules = 1<<9, + MissingMainCpp = 1<<10, + MissingMainCppHeader = 1<<11, + MissingEnvHeader = 1<<12 }; -QVector queuedFiles; +const QString MENU_ITEM_GENERATE = QCoreApplication::translate("QmlDesigner::GenerateCmake", + "Generate CMake Build Files"); void generateMenuEntry() { Core::ActionContainer *menu = Core::ActionManager::actionContainer(Core::Constants::M_FILE); - auto action = new QAction(QCoreApplication::translate("QmlDesigner::GenerateCmake", "Export to Qt Creator (CMake)")); + auto action = new QAction(MENU_ITEM_GENERATE); QObject::connect(action, &QAction::triggered, GenerateCmake::onGenerateCmakeLists); Core::Command *cmd = Core::ActionManager::registerAction(action, "QmlProject.CreateCMakeLists"); menu->addAction(cmd, Core::Constants::G_FILE_EXPORT); @@ -106,11 +108,19 @@ void onGenerateCmakeLists() return; } - queuedFiles.clear(); - GenerateCmakeLists::generateCmakes(rootDir); - GenerateEntryPoints::generateEntryPointFiles(rootDir); - if (showConfirmationDialog(rootDir)) - writeQueuedFiles(); + CmakeFileGenerator cmakeGen; + cmakeGen.prepare(rootDir); + + FilePaths allFiles; + for (const GeneratableFile &file: cmakeGen.fileQueue().queuedFiles()) + allFiles.append(file.filePath); + + CmakeGeneratorDialog dialog(rootDir, allFiles); + if (dialog.exec()) { + FilePaths confirmedFiles = dialog.getFilePaths(); + cmakeGen.filterFileQueue(confirmedFiles); + cmakeGen.execute(); + } } bool isErrorFatal(int error) @@ -135,7 +145,7 @@ int isProjectCorrectlyFormed(const FilePath &rootDir) if (!rootDir.pathAppended(DIRNAME_IMPORT).exists()) errors |= MissingImportDir; - if (!rootDir.pathAppended(DIRNAME_ASSET).exists()) + if (!rootDir.pathAppended(DIRNAME_ASSETIMPORT).exists()) errors |= MissingAssetImportDir; if (!rootDir.pathAppended(DIRNAME_CPP).exists()) @@ -157,13 +167,6 @@ int isProjectCorrectlyFormed(const FilePath &rootDir) return errors; } -void removeUnconfirmedQueuedFiles(const Utils::FilePaths confirmedFiles) -{ - QtConcurrent::blockingFilter(queuedFiles, [confirmedFiles](const GeneratableFile &qf) { - return confirmedFiles.contains(qf.filePath); - }); -} - const QString WARNING_MISSING_STRUCTURE_FATAL = QCoreApplication::translate("QmlDesigner::GenerateCmake", "The project is not properly structured for automatically generating CMake files.\n\nAborting process.\n\nThe following files or directories are missing:\n\n%1"); //const QString WARNING_MISSING_STRUCTURE_NONFATAL = QCoreApplication::translate("QmlDesigner::GenerateCmake", @@ -198,44 +201,32 @@ void showProjectDirErrorDialog(int error) } } -bool showConfirmationDialog(const Utils::FilePath &rootDir) -{ - Utils::FilePaths files; - for (GeneratableFile &file: queuedFiles) - files.append(file.filePath); - - CmakeGeneratorDialog dialog(rootDir, files); - if (dialog.exec()) { - Utils::FilePaths confirmedFiles = dialog.getFilePaths(); - removeUnconfirmedQueuedFiles(confirmedFiles); - - return true; - } - - return false; -} - -bool queueFile(const FilePath &filePath, const QString &fileContent) +bool FileQueue::queueFile(const FilePath &filePath, const QString &fileContent) { GeneratableFile file; file.filePath = filePath; file.content = fileContent; file.fileExists = filePath.exists(); - queuedFiles.append(file); + m_queuedFiles.append(file); return true; } -bool writeQueuedFiles() +const QVector FileQueue::queuedFiles() const { - for (GeneratableFile &file: queuedFiles) + return m_queuedFiles; +} + +bool FileQueue::writeQueuedFiles() +{ + for (GeneratableFile &file: m_queuedFiles) if (!writeFile(file)) return false; return true; } -bool writeFile(const GeneratableFile &file) +bool FileQueue::writeFile(const GeneratableFile &file) { QFile fileHandle(file.filePath.toString()); fileHandle.open(QIODevice::WriteOnly); @@ -246,6 +237,13 @@ bool writeFile(const GeneratableFile &file) return true; } +void FileQueue::filterFiles(const Utils::FilePaths keepFiles) +{ + QtConcurrent::blockingFilter(m_queuedFiles, [keepFiles](const GeneratableFile &qf) { + return keepFiles.contains(qf.filePath); + }); +} + QString readTemplate(const QString &templatePath) { QFile templatefile(templatePath); @@ -257,54 +255,90 @@ QString readTemplate(const QString &templatePath) return content; } +const QString projectEnvironmentVariable(const QString &key) +{ + QString value = {}; + + auto *target = ProjectExplorer::SessionManager::startupProject()->activeTarget(); + if (target && target->buildSystem()) { + auto buildSystem = qobject_cast(target->buildSystem()); + if (buildSystem) { + auto envItems = buildSystem->environment(); + auto confEnv = std::find_if(envItems.begin(), envItems.end(), + [key](NameValueItem &item){return item.name == key;}); + if (confEnv != envItems.end()) + value = confEnv->value; + } + } + return value; } -namespace GenerateCmakeLists { - -QStringList moduleNames; - const QDir::Filters FILES_ONLY = QDir::Files; const QDir::Filters DIRS_ONLY = QDir::Dirs|QDir::Readable|QDir::NoDotAndDotDot; const char MAIN_CMAKEFILE_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectmaincmakelists.tpl"; const char QMLMODULES_FILE_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectmodules.tpl"; -bool generateCmakes(const FilePath &rootDir) +bool CmakeFileGenerator::prepare(const FilePath &rootDir, bool checkFileBelongs) { - moduleNames.clear(); + m_checkFileIsInProject = checkFileBelongs; FilePath contentDir = rootDir.pathAppended(DIRNAME_CONTENT); FilePath importDir = rootDir.pathAppended(DIRNAME_IMPORT); - FilePath assetDir = rootDir.pathAppended(DIRNAME_ASSET); + FilePath assetImportDir = rootDir.pathAppended(DIRNAME_ASSETIMPORT); generateModuleCmake(contentDir); generateImportCmake(importDir); - generateImportCmake(assetDir); + generateImportCmake(assetImportDir); generateMainCmake(rootDir); + generateEntryPointFiles(rootDir); return true; } +const FileQueue CmakeFileGenerator::fileQueue() const +{ + return m_fileQueue; +} + +void CmakeFileGenerator::filterFileQueue(const Utils::FilePaths &keepFiles) +{ + m_fileQueue.filterFiles(keepFiles); +} + +bool CmakeFileGenerator::execute() +{ + return m_fileQueue.writeQueuedFiles(); +} + const char DO_NOT_EDIT_FILE_COMMENT[] = "### This file is automatically generated by Qt Design Studio.\n### Do not change\n\n"; const char ADD_SUBDIR[] = "add_subdirectory(%1)\n"; -void generateMainCmake(const FilePath &rootDir) +void CmakeFileGenerator::generateMainCmake(const FilePath &rootDir) { //TODO startupProject() may be a terrible way to try to get "current project". It's not necessarily the same thing at all. QString projectName = ProjectExplorer::SessionManager::startupProject()->displayName(); QString appName = projectName + "App"; - QString cmakeFileContent = GenerateCmake::readTemplate(MAIN_CMAKEFILE_TEMPLATE_PATH).arg(appName); + QString fileSection = ""; + const QString qtcontrolsConfFile = GenerateCmake::projectEnvironmentVariable(ENV_VARIABLE_CONTROLCONF); + if (!qtcontrolsConfFile.isEmpty()) + fileSection = QString(" FILES\n %1").arg(qtcontrolsConfFile); + + QString cmakeFileContent = GenerateCmake::readTemplate(MAIN_CMAKEFILE_TEMPLATE_PATH) + .arg(appName) + .arg(fileSection); + queueCmakeFile(rootDir, cmakeFileContent); QString subdirIncludes; subdirIncludes.append(QString(ADD_SUBDIR).arg(DIRNAME_CONTENT)); subdirIncludes.append(QString(ADD_SUBDIR).arg(DIRNAME_IMPORT)); - if (rootDir.pathAppended(DIRNAME_ASSET).exists()) - subdirIncludes.append(QString(ADD_SUBDIR).arg(DIRNAME_ASSET)); + if (rootDir.pathAppended(DIRNAME_ASSETIMPORT).exists()) + subdirIncludes.append(QString(ADD_SUBDIR).arg(DIRNAME_ASSETIMPORT)); QString modulesAsPlugins; - for (const QString &moduleName : moduleNames) + for (const QString &moduleName : m_moduleNames) modulesAsPlugins.append(" " + moduleName + "plugin\n"); QString moduleFileContent = GenerateCmake::readTemplate(QMLMODULES_FILE_TEMPLATE_PATH) @@ -312,10 +346,10 @@ void generateMainCmake(const FilePath &rootDir) .arg(subdirIncludes) .arg(modulesAsPlugins); - GenerateCmake::queueFile(rootDir.pathAppended(FILENAME_MODULES), moduleFileContent); + m_fileQueue.queueFile(rootDir.pathAppended(FILENAME_MODULES), moduleFileContent); } -void generateImportCmake(const FilePath &dir, const QString &modulePrefix) +void CmakeFileGenerator::generateImportCmake(const FilePath &dir, const QString &modulePrefix) { if (!dir.exists()) return; @@ -347,7 +381,7 @@ const char MODULEFILE_PROPERTY_SINGLETON[] = "QT_QML_SINGLETON_TYPE"; const char MODULEFILE_PROPERTY_SET[] = "set_source_files_properties(%1\n PROPERTIES\n %2 %3\n)\n\n"; const char MODULEFILE_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectmodulecmakelists.tpl"; -void generateModuleCmake(const FilePath &dir, const QString &uri) +void CmakeFileGenerator::generateModuleCmake(const FilePath &dir, const QString &uri) { QString fileTemplate = GenerateCmake::readTemplate(MODULEFILE_TEMPLATE_PATH); @@ -381,15 +415,16 @@ void generateModuleCmake(const FilePath &dir, const QString &uri) QString moduleUri = uri.isEmpty() ? dir.fileName() : uri; - QString moduleName = QString(moduleUri).remove('.'); - moduleNames.append(moduleName); + + QString moduleName = QString(moduleUri).replace('.', '_'); + m_moduleNames.append(moduleName); QString fileContent; fileContent.append(fileTemplate.arg(singletonContent, moduleName, moduleUri, moduleContent)); queueCmakeFile(dir, fileContent); } -QStringList getSingletonsFromQmldirFile(const FilePath &filePath) +QStringList CmakeFileGenerator::getSingletonsFromQmldirFile(const FilePath &filePath) { QStringList singletons; QFile f(filePath.toString()); @@ -412,36 +447,42 @@ QStringList getSingletonsFromQmldirFile(const FilePath &filePath) return singletons; } -FilePaths getDirectoryQmls(const FilePath &dir) +QStringList CmakeFileGenerator::getDirectoryQmls(const FilePath &dir) { - const QStringList qmlFilesOnly("*.qml"); - ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject(); + QStringList moduleFiles; + + const QStringList qmlFilesOnly(FILENAME_FILTER_QML); FilePaths allFiles = dir.dirEntries({qmlFilesOnly, FILES_ONLY}); - FilePaths moduleFiles; for (FilePath &file : allFiles) { - if (!isFileBlacklisted(file.fileName()) && - project->isKnownFile(file)) { - moduleFiles.append(file); + if (includeFile(file)) { + moduleFiles.append(file.fileName()); } } return moduleFiles; } -QStringList getDirectoryTreeQmls(const FilePath &dir) +QStringList CmakeFileGenerator::getDirectoryResources(const FilePath &dir) { - const QStringList qmlFilesOnly("*.qml"); - ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject(); - QStringList qmlFileList; + QStringList moduleFiles; - FilePaths thisDirFiles = dir.dirEntries({qmlFilesOnly, FILES_ONLY}); - for (FilePath &file : thisDirFiles) { - if (!isFileBlacklisted(file.fileName()) && - project->isKnownFile(file)) { - qmlFileList.append(file.fileName()); + FilePaths allFiles = dir.dirEntries(FILES_ONLY); + for (FilePath &file : allFiles) { + if (!file.fileName().endsWith(".qml", Qt::CaseInsensitive) && + includeFile(file)) { + moduleFiles.append(file.fileName()); } } + return moduleFiles; +} + +QStringList CmakeFileGenerator::getDirectoryTreeQmls(const FilePath &dir) +{ + QStringList qmlFileList; + + qmlFileList.append(getDirectoryQmls(dir)); + FilePaths subDirsList = dir.dirEntries(DIRS_ONLY); for (FilePath &subDir : subDirsList) { if (isDirBlacklisted(subDir)) @@ -455,19 +496,11 @@ QStringList getDirectoryTreeQmls(const FilePath &dir) return qmlFileList; } -QStringList getDirectoryTreeResources(const FilePath &dir) +QStringList CmakeFileGenerator::getDirectoryTreeResources(const FilePath &dir) { - ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject(); QStringList resourceFileList; - FilePaths thisDirFiles = dir.dirEntries(FILES_ONLY); - for (FilePath &file : thisDirFiles) { - if (!isFileBlacklisted(file.fileName()) && - !file.fileName().endsWith(".qml", Qt::CaseInsensitive) && - project->isKnownFile(file)) { - resourceFileList.append(file.fileName()); - } - } + resourceFileList.append(getDirectoryResources(dir)); FilePaths subDirsList = dir.dirEntries(DIRS_ONLY); for (FilePath &subDir : subDirsList) { @@ -483,28 +516,41 @@ QStringList getDirectoryTreeResources(const FilePath &dir) return resourceFileList; } -void queueCmakeFile(const FilePath &dir, const QString &content) +void CmakeFileGenerator::queueCmakeFile(const FilePath &dir, const QString &content) { FilePath filePath = dir.pathAppended(FILENAME_CMAKELISTS); - GenerateCmake::queueFile(filePath, content); + m_fileQueue.queueFile(filePath, content); } -bool isFileBlacklisted(const QString &fileName) +bool CmakeFileGenerator::isFileBlacklisted(const QString &fileName) { return (!fileName.compare(FILENAME_QMLDIR) || !fileName.compare(FILENAME_CMAKELISTS)); } -bool isDirBlacklisted(const FilePath &dir) +bool CmakeFileGenerator::isDirBlacklisted(const FilePath &dir) { return (!dir.fileName().compare(DIRNAME_DESIGNER)); } +bool CmakeFileGenerator::includeFile(const FilePath &filePath) +{ + if (m_checkFileIsInProject) { + ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject(); + if (!project->isKnownFile(filePath)) + return false; + } + + return !isFileBlacklisted(filePath.fileName()); } -namespace GenerateEntryPoints { -bool generateEntryPointFiles(const FilePath &dir) + +bool CmakeFileGenerator::generateEntryPointFiles(const FilePath &dir) { + const QString qtcontrolsConf = GenerateCmake::projectEnvironmentVariable(ENV_VARIABLE_CONTROLCONF); + if (!qtcontrolsConf.isEmpty()) + m_resourceFileLocations.append(qtcontrolsConf); + bool cppOk = generateMainCpp(dir); bool qmlOk = generateMainQml(dir); @@ -517,23 +563,23 @@ const char MAIN_CPPFILE_HEADER_PLUGIN_LINE[] = "Q_IMPORT_QML_PLUGIN(%1)\n"; const char ENV_HEADER_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectenvheader.tpl"; const char ENV_HEADER_VARIABLE_LINE[] = " qputenv(\"%1\", \"%2\");\n"; -bool generateMainCpp(const FilePath &dir) +bool CmakeFileGenerator::generateMainCpp(const FilePath &dir) { FilePath srcDir = dir.pathAppended(DIRNAME_CPP); QString cppContent = GenerateCmake::readTemplate(MAIN_CPPFILE_TEMPLATE_PATH); FilePath cppFilePath = srcDir.pathAppended(FILENAME_MAINCPP); - bool cppOk = GenerateCmake::queueFile(cppFilePath, cppContent); + bool cppOk = m_fileQueue.queueFile(cppFilePath, cppContent); QString modulesAsPlugins; - for (const QString &moduleName : GenerateCmakeLists::moduleNames) + for (const QString &moduleName : m_moduleNames) modulesAsPlugins.append( QString(MAIN_CPPFILE_HEADER_PLUGIN_LINE).arg(moduleName + "Plugin")); QString headerContent = GenerateCmake::readTemplate(MAIN_CPPFILE_HEADER_TEMPLATE_PATH) .arg(modulesAsPlugins); FilePath headerFilePath = srcDir.pathAppended(FILENAME_MAINCPP_HEADER); - bool pluginHeaderOk = GenerateCmake::queueFile(headerFilePath, headerContent); + bool pluginHeaderOk = m_fileQueue.queueFile(headerFilePath, headerContent); bool envHeaderOk = true; QString environment; @@ -551,7 +597,7 @@ bool generateMainCpp(const FilePath &dir) QString envHeaderContent = GenerateCmake::readTemplate(ENV_HEADER_TEMPLATE_PATH) .arg(environment); FilePath envHeaderPath = srcDir.pathAppended(FILENAME_ENV_HEADER); - envHeaderOk = GenerateCmake::queueFile(envHeaderPath, envHeaderContent); + envHeaderOk = m_fileQueue.queueFile(envHeaderPath, envHeaderContent); } } @@ -560,24 +606,21 @@ bool generateMainCpp(const FilePath &dir) const char MAIN_QMLFILE_TEMPLATE_PATH[] = ":/boilerplatetemplates/qmlprojectmainqml.tpl"; -bool generateMainQml(const FilePath &dir) +bool CmakeFileGenerator::generateMainQml(const FilePath &dir) { QString content = GenerateCmake::readTemplate(MAIN_QMLFILE_TEMPLATE_PATH); FilePath filePath = dir.pathAppended(FILENAME_MAINQML); - return GenerateCmake::queueFile(filePath, content); + return m_fileQueue.queueFile(filePath, content); } -const QStringList resourceFileLocations = {"qtquickcontrols2.conf"}; - -bool isFileResource(const QString &relativeFilePath) +bool CmakeFileGenerator::isFileResource(const QString &relativeFilePath) { - if (resourceFileLocations.contains(relativeFilePath)) + if (m_resourceFileLocations.contains(relativeFilePath)) return true; return false; } -} //GenerateEntryPoints - +} //GenerateCmake } //QmlDesigner diff --git a/src/plugins/qmldesigner/generatecmakelists.h b/src/plugins/qmldesigner/generatecmakelists.h index eafe72defcb..7045ce17c8a 100644 --- a/src/plugins/qmldesigner/generatecmakelists.h +++ b/src/plugins/qmldesigner/generatecmakelists.h @@ -43,31 +43,56 @@ void generateMenuEntry(); void onGenerateCmakeLists(); bool isErrorFatal(int error); int isProjectCorrectlyFormed(const Utils::FilePath &rootDir); -void removeUnconfirmedQueuedFiles(const Utils::FilePaths confirmedFiles); void showProjectDirErrorDialog(int error); -bool showConfirmationDialog(const Utils::FilePath &rootDir); -bool queueFile(const Utils::FilePath &filePath, const QString &fileContent); -bool writeFile(const GeneratableFile &file); -bool writeQueuedFiles(); QString readTemplate(const QString &templatePath); -} -namespace GenerateCmakeLists { -bool generateCmakes(const Utils::FilePath &rootDir); -void generateMainCmake(const Utils::FilePath &rootDir); -void generateImportCmake(const Utils::FilePath &dir, const QString &modulePrefix = QString()); -void generateModuleCmake(const Utils::FilePath &dir, const QString &moduleUri = QString()); -Utils::FilePaths getDirectoryQmls(const Utils::FilePath &dir); -QStringList getSingletonsFromQmldirFile(const Utils::FilePath &filePath); -QStringList getDirectoryTreeQmls(const Utils::FilePath &dir); -QStringList getDirectoryTreeResources(const Utils::FilePath &dir); -void queueCmakeFile(const Utils::FilePath &filePath, const QString &content); -bool isFileBlacklisted(const QString &fileName); -bool isDirBlacklisted(const Utils::FilePath &dir); -} -namespace GenerateEntryPoints { -bool generateEntryPointFiles(const Utils::FilePath &dir); -bool generateMainCpp(const Utils::FilePath &dir); -bool generateMainQml(const Utils::FilePath &dir); -bool isFileResource(const QString &relativeFilePath); -} -} +const QString projectEnvironmentVariable(const QString &key); + +class FileQueue { +public: + bool queueFile(const Utils::FilePath &filePath, const QString &fileContent); + const QVector queuedFiles() const; + bool writeQueuedFiles(); + void filterFiles(const Utils::FilePaths keepFiles); + +private: + bool writeFile(const GeneratableFile &file); + +private: + QVector m_queuedFiles; +}; + +class CmakeFileGenerator { +public: + bool prepare(const Utils::FilePath &rootDir, bool check = true); + const FileQueue fileQueue() const; + void filterFileQueue(const Utils::FilePaths &keepFiles); + bool execute(); + +private: + void generateMainCmake(const Utils::FilePath &rootDir); + void generateImportCmake(const Utils::FilePath &dir, const QString &modulePrefix = QString()); + void generateModuleCmake(const Utils::FilePath &dir, const QString &moduleUri = QString()); + bool generateEntryPointFiles(const Utils::FilePath &dir); + bool generateMainCpp(const Utils::FilePath &dir); + bool generateMainQml(const Utils::FilePath &dir); + QStringList getDirectoryQmls(const Utils::FilePath &dir); + QStringList getDirectoryResources(const Utils::FilePath &dir); + QStringList getSingletonsFromQmldirFile(const Utils::FilePath &filePath); + QStringList getDirectoryTreeQmls(const Utils::FilePath &dir); + QStringList getDirectoryTreeResources(const Utils::FilePath &dir); + void queueCmakeFile(const Utils::FilePath &filePath, const QString &content); + bool isFileResource(const QString &relativeFilePath); + bool isFileBlacklisted(const QString &fileName); + bool isDirBlacklisted(const Utils::FilePath &dir); + bool includeFile(const Utils::FilePath &filePath); + +private: + FileQueue m_fileQueue; + QStringList m_resourceFileLocations; + QStringList m_moduleNames; + bool m_checkFileIsInProject; +}; + +} //GenerateCmake + +} //QmlDesigner diff --git a/src/plugins/qmldesigner/generatecmakelistsconstants.h b/src/plugins/qmldesigner/generatecmakelistsconstants.h index 0374ac2ad65..e4358dc5999 100644 --- a/src/plugins/qmldesigner/generatecmakelistsconstants.h +++ b/src/plugins/qmldesigner/generatecmakelistsconstants.h @@ -34,7 +34,8 @@ namespace Constants { const char DIRNAME_CONTENT[] = "content"; const char DIRNAME_IMPORT[] = "imports"; -const char DIRNAME_ASSET[] = "asset_imports"; +const char DIRNAME_ASSET[] = "assets"; +const char DIRNAME_ASSETIMPORT[] = "asset_imports"; const char DIRNAME_CPP[] = "src"; const char DIRNAME_DESIGNER[] = "designer"; @@ -47,6 +48,15 @@ const char FILENAME_MODULES[] = "qmlmodules"; const char FILENAME_QMLDIR[] = "qmldir"; const char FILENAME_ENV_HEADER[] = "app_environment.h"; +const char FILENAME_SUFFIX_QMLPROJECT[] = "qmlproject"; +const char FILENAME_SUFFIX_QML[] = "qml"; +const char FILENAME_SUFFIX_USER[] = "user"; + +const char FILENAME_FILTER_QMLPROJECT[] = "*.qmlproject"; +const char FILENAME_FILTER_QML[] = "*.qml"; + +const char ENV_VARIABLE_CONTROLCONF[] = "QT_QUICK_CONTROLS_CONF"; + } //Constants } //GenerateCmake } //QmlDesigner diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index 2114a56d3d7..3748a2164e0 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -24,6 +24,7 @@ ****************************************************************************/ #include "qmldesignerplugin.h" +#include "cmakeprojectconverter.h" #include "designmodecontext.h" #include "designmodewidget.h" #include "exception.h" @@ -230,6 +231,7 @@ bool QmlDesignerPlugin::initialize(const QStringList & /*arguments*/, QString *e GenerateResource::generateMenuEntry(); GenerateCmake::generateMenuEntry(); + GenerateCmake::CmakeProjectConverter::generateMenuEntry(); const QString fontPath = Core::ICore::resourcePath( @@ -257,9 +259,9 @@ bool QmlDesignerPlugin::initialize(const QStringList & /*arguments*/, QString *e designerActionManager().addDesignerAction(startNanotraceAction); auto shutDownNanotraceAction = new ModelNodeAction("ShutDown Nanotrace", - QObject::tr("ShutDown Nanotrace"), + QObject::tr("Shut Down Nanotrace"), shutdownNanotraceIcon(), - QObject::tr("ShutDown Nanotrace"), + QObject::tr("Shut Down Nanotrace"), ComponentCoreConstants::eventListCategory, QKeySequence(), 220, diff --git a/src/plugins/qmldesigner/qmldesignerplugin.qbs b/src/plugins/qmldesigner/qmldesignerplugin.qbs index a3f5649680c..09690f9d2f4 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.qbs +++ b/src/plugins/qmldesigner/qmldesignerplugin.qbs @@ -1035,6 +1035,10 @@ Project { "cmakegeneratordialogtreemodel.h", "cmakegeneratordialog.cpp", "cmakegeneratordialog.h", + "cmakeprojectconverter.cpp", + "cmakeprojectconverter.h", + "cmakeprojectconverterdialog.cpp", + "cmakeprojectconverterdialog.h", "designersettings.cpp", "designersettings.h", "designmodecontext.cpp", diff --git a/src/plugins/qmldesigner/qmlprojectappmainqml.tpl b/src/plugins/qmldesigner/qmlprojectappmainqml.tpl new file mode 100644 index 00000000000..35f8218dc65 --- /dev/null +++ b/src/plugins/qmldesigner/qmlprojectappmainqml.tpl @@ -0,0 +1,14 @@ +import QtQuick +import QtQuick.Window + +Window { + visible: true + title: "%1" + width: mainScreen.width + height: mainScreen.height + + %1 { + id: mainScreen + } + +} diff --git a/src/plugins/qmldesigner/qmlprojectmaincmakelists.tpl b/src/plugins/qmldesigner/qmlprojectmaincmakelists.tpl index 14a91d20eaf..423ed1a921a 100644 --- a/src/plugins/qmldesigner/qmlprojectmaincmakelists.tpl +++ b/src/plugins/qmldesigner/qmlprojectmaincmakelists.tpl @@ -10,8 +10,7 @@ qt_add_executable(%1 src/main.cpp) qt_add_resources(%1 "configuration" PREFIX "/" - FILES - qtquickcontrols2.conf +%2 ) target_link_libraries(%1 PRIVATE diff --git a/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp b/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp index cfb2bfaf474..dc57689cad9 100644 --- a/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp +++ b/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp @@ -76,8 +76,7 @@ QmlMultiLanguageAspect::QmlMultiLanguageAspect(ProjectExplorer::Target *target) setVisible(isMultilanguagePresent()); setSettingsKey(Constants::USE_MULTILANGUAGE_KEY); setLabel(tr("Use MultiLanguage in Form Editor."), BoolAspect::LabelPlacement::AtCheckBox); - setToolTip(tr("By enabling this Form Editor can read translations\n" - "from MultiLanguage plugin.")); + setToolTip(tr("Reads translations from MultiLanguage plugin.")); setDefaultValue(!databaseFilePath().isEmpty()); QVariantMap getDefaultValues; diff --git a/src/plugins/remotelinux/remotelinuxcheckforfreediskspaceservice.cpp b/src/plugins/remotelinux/remotelinuxcheckforfreediskspaceservice.cpp index bb2cdf86adc..bb1b67a746b 100644 --- a/src/plugins/remotelinux/remotelinuxcheckforfreediskspaceservice.cpp +++ b/src/plugins/remotelinux/remotelinuxcheckforfreediskspaceservice.cpp @@ -68,7 +68,7 @@ void RemoteLinuxCheckForFreeDiskSpaceService::deployAndFinish() = deviceConfiguration()->mapToGlobalPath(Utils::FilePath::fromString(d->pathToCheck)); const qint64 freeSpace = path.bytesAvailable(); if (freeSpace < 0) { - emit errorMessage(tr("Can't get the info about the free disk space for \"%1\"") + emit errorMessage(tr("Cannot get info about free disk space for \"%1\"") .arg(path.toUserOutput())); return; } diff --git a/src/plugins/studiowelcome/CMakeLists.txt b/src/plugins/studiowelcome/CMakeLists.txt index 48c07d15b97..a8b92ab1f8b 100644 --- a/src/plugins/studiowelcome/CMakeLists.txt +++ b/src/plugins/studiowelcome/CMakeLists.txt @@ -1,6 +1,6 @@ add_qtc_plugin(StudioWelcome CONDITION TARGET Qt5::QuickWidgets AND TARGET QmlDesigner - DEPENDS Qt5::QuickWidgets + DEPENDS Qt5::QuickWidgets Qt5::QmlPrivate PLUGIN_DEPENDS Core ProjectExplorer QtSupport QmlDesigner DEFINES STUDIO_QML_PATH="${CMAKE_CURRENT_SOURCE_DIR}/qml/" SOURCES diff --git a/src/plugins/studiowelcome/examplecheckout.cpp b/src/plugins/studiowelcome/examplecheckout.cpp index 45d7dd440fb..06d37a39317 100644 --- a/src/plugins/studiowelcome/examplecheckout.cpp +++ b/src/plugins/studiowelcome/examplecheckout.cpp @@ -24,13 +24,23 @@ ****************************************************************************/ #include "examplecheckout.h" +#include "studiowelcomeplugin.h" + #include #include #include +#include #include #include +#include + +#include +#include + +#include + #include #include #include @@ -45,11 +55,8 @@ using namespace Utils; -ExampleCheckout::ExampleCheckout(QObject *) {} - void ExampleCheckout::registerTypes() { - FileDownloader::registerQmlType(); static bool once = []() { FileDownloader::registerQmlType(); FileExtractor::registerQmlType(); @@ -59,69 +66,6 @@ void ExampleCheckout::registerTypes() QTC_ASSERT(once, ;); } -void ExampleCheckout::checkoutExample(const QUrl &url, const QString &tempFile, const QString &completeBaseFileName) -{ - registerTypes(); - - m_dialog.reset(new QDialog(Core::ICore::dialogParent())); - m_dialog->setModal(true); - m_dialog->setFixedSize(620, 300); - QHBoxLayout *layout = new QHBoxLayout(m_dialog.get()); - layout->setContentsMargins(2, 2, 2, 2); - - auto widget = new QQuickWidget(m_dialog.get()); - - layout->addWidget(widget); - widget->engine()->addImportPath("qrc:/studiofonts"); - - widget->engine()->addImportPath( - Core::ICore::resourcePath("/qmldesigner/propertyEditorQmlSources/imports").toString()); - - widget->setSource(QUrl("qrc:/qml/downloaddialog/main.qml")); - - m_dialog->setWindowFlag(Qt::Tool, true); - widget->setResizeMode(QQuickWidget::SizeRootObjectToView); - - rootObject = widget->rootObject(); - - QTC_ASSERT(rootObject, qWarning() << "QML error"; return ); - - rootObject->setProperty("url", url); - rootObject->setProperty("tempFile", tempFile); - rootObject->setProperty("completeBaseName", completeBaseFileName); - - m_dialog->show(); - - rootObject = widget->rootObject(); - - connect(rootObject, SIGNAL(canceled()), this, SLOT(handleCancel())); - connect(rootObject, SIGNAL(accepted()), this, SLOT(handleAccepted())); -} - -QString ExampleCheckout::extractionFolder() const -{ - return m_extrationFolder; -} - -ExampleCheckout::~ExampleCheckout() {} - -void ExampleCheckout::handleCancel() -{ - m_dialog->close(); - m_dialog.release()->deleteLater(); - deleteLater(); -} - -void ExampleCheckout::handleAccepted() -{ - QQmlProperty property(rootObject, "path"); - m_extrationFolder = property.read().toString(); - m_dialog->close(); - emit finishedSucessfully(); - m_dialog.release()->deleteLater(); - deleteLater(); -} - void FileDownloader::registerQmlType() { qmlRegisterType("ExampleCheckout", 1, 0, "FileDownloader"); @@ -133,7 +77,8 @@ FileDownloader::FileDownloader(QObject *parent) FileDownloader::~FileDownloader() { - m_tempFile.remove(); + if (m_tempFile.exists()) + m_tempFile.remove(); } void FileDownloader::start() @@ -167,7 +112,8 @@ void FileDownloader::start() QNetworkReply::connect(reply, &QNetworkReply::finished, this, [this, reply]() { if (reply->error()) { - m_tempFile.remove(); + if (m_tempFile.exists()) + m_tempFile.remove(); qDebug() << Q_FUNC_INFO << m_url << reply->errorString(); emit downloadFailed(); } else { @@ -247,6 +193,17 @@ void FileDownloader::probeUrl() }); QNetworkReply::connect(reply, &QNetworkReply::finished, this, [this, reply]() { + QQmlData *data = QQmlData::get(this, false); + if (!data) { + qDebug() << Q_FUNC_INFO << "FileDownloader is nullptr."; + return; + } + + if (QQmlData::wasDeleted(this)) { + qDebug() << Q_FUNC_INFO << "FileDownloader was deleted."; + return; + } + if (reply->error()) return; @@ -261,8 +218,17 @@ void FileDownloader::probeUrl() &QNetworkReply::errorOccurred, this, [this, reply](QNetworkReply::NetworkError code) { - // QNetworkReply::HostNotFoundError - // QNetworkReply::ContentNotFoundError + QQmlData *data = QQmlData::get(this, false); + if (!data) { + qDebug() << Q_FUNC_INFO << "FileDownloader is nullptr."; + return; + } + + if (QQmlData::wasDeleted(this)) { + qDebug() << Q_FUNC_INFO << "FileDownloader was deleted."; + return; + } + m_available = false; emit availableChanged(); }); @@ -273,12 +239,7 @@ FileExtractor::FileExtractor(QObject *parent) : QObject(parent) { m_targetPath = Utils::FilePath::fromString( - QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)); - - if (!m_targetPath.isEmpty()) - m_targetPath = m_targetPath.pathAppended("QtDesignStudio"); - else - m_targetPath = "/temp/"; + StudioWelcome::Internal::StudioWelcomePlugin::examplesPathSetting()); m_timer.setInterval(100); m_timer.setSingleShot(false); @@ -291,6 +252,32 @@ FileExtractor::FileExtractor(QObject *parent) emit birthTimeChanged(); }); + + const ExtensionSystem::PluginSpec *pluginSpec + = Utils::findOrDefault(ExtensionSystem::PluginManager::plugins(), + Utils::equal(&ExtensionSystem::PluginSpec::name, + QString("StudioWelcome"))); + + if (!pluginSpec) + return; + + ExtensionSystem::IPlugin *plugin = pluginSpec->plugin(); + + if (!plugin) + return; + + auto studioWelcomePlugin = qobject_cast(plugin); + + if (studioWelcomePlugin) { + QObject::connect(studioWelcomePlugin, + &StudioWelcome::Internal::StudioWelcomePlugin::examplesDownloadPathChanged, + this, + [this](const QString &path) { + m_targetPath = Utils::FilePath::fromString(path); + emit targetPathChanged(); + emit targetFolderExistsChanged(); + }); + } } FileExtractor::~FileExtractor() {} @@ -381,12 +368,21 @@ QString FileExtractor::sourceFile() const void FileExtractor::extract() { + const QString targetFolder = m_targetPath.toString() + "/" + m_archiveName; + + // If the target directory already exists, remove it and its content + QDir targetDir(targetFolder); + if (targetDir.exists()) + targetDir.removeRecursively(); + + // Create a new directory to generate a proper creation date + targetDir.mkdir(targetFolder); + Utils::Archive *archive = Utils::Archive::unarchive(m_sourceFile, m_targetPath); archive->setParent(this); QTC_ASSERT(archive, return ); m_timer.start(); - const QString targetFolder = m_targetPath.toString() + "/" + m_archiveName; qint64 bytesBefore = QStorageInfo(m_targetPath.toFileInfo().dir()).bytesAvailable(); qint64 compressedSize = QFileInfo(m_sourceFile.toString()).size(); @@ -424,7 +420,7 @@ void FileExtractor::extract() emit detailedTextChanged(); }); - QObject::connect(archive, &Utils::Archive::finished, [this](bool ret) { + QObject::connect(archive, &Utils::Archive::finished, this, [this](bool ret) { m_finished = ret; m_timer.stop(); diff --git a/src/plugins/studiowelcome/examplecheckout.h b/src/plugins/studiowelcome/examplecheckout.h index 4e256994377..af2be4893ec 100644 --- a/src/plugins/studiowelcome/examplecheckout.h +++ b/src/plugins/studiowelcome/examplecheckout.h @@ -33,36 +33,9 @@ #include -QT_BEGIN_NAMESPACE -class QDialog; -QT_END_NAMESPACE - -class ExampleCheckout : public QObject +struct ExampleCheckout { - Q_OBJECT -public: - explicit ExampleCheckout(QObject *parent = nullptr); - - Q_INVOKABLE void checkoutExample(const QUrl &url, const QString &tempFile, const QString &completeBaseFileName); - - QString extractionFolder() const; - - ~ExampleCheckout(); - static void registerTypes(); - -public slots: - void handleCancel(); - void handleAccepted(); - -signals: - void finishedSucessfully(); - void progressChanged(int); - -private: - std::unique_ptr m_dialog; - QObject *rootObject = nullptr; - QString m_extrationFolder; }; class FileExtractor : public QObject diff --git a/src/plugins/studiowelcome/studiowelcome.qbs b/src/plugins/studiowelcome/studiowelcome.qbs index afc9e627a52..cc655ea5a23 100644 --- a/src/plugins/studiowelcome/studiowelcome.qbs +++ b/src/plugins/studiowelcome/studiowelcome.qbs @@ -3,7 +3,7 @@ import qbs.FileInfo QtcPlugin { name: "StudioWelcome" - Depends { name: "Qt"; submodules: ["qml", "quick", "quickwidgets"] } + Depends { name: "Qt"; submodules: ["qml", "qml-private", "quick", "quickwidgets"] } Depends { name: "Utils" } Depends { name: "Core" } diff --git a/src/plugins/studiowelcome/studiowelcomeplugin.cpp b/src/plugins/studiowelcome/studiowelcomeplugin.cpp index c004b146691..72f6afcdb57 100644 --- a/src/plugins/studiowelcome/studiowelcomeplugin.cpp +++ b/src/plugins/studiowelcome/studiowelcomeplugin.cpp @@ -33,14 +33,12 @@ #include #include #include +#include #include #include #include #include -#include -#include - #include #include #include @@ -52,11 +50,13 @@ #include #include +#include #include #include +#include #include #include -#include +#include #include #include @@ -66,6 +66,7 @@ #include #include #include +#include #include #include #include @@ -97,6 +98,8 @@ const char STATISTICS_COLLECTION_MODE[] = "StatisticsCollectionMode"; const char NO_TELEMETRY[] = "NoTelemetry"; const char CRASH_REPORTER_SETTING[] = "CrashReportingEnabled"; +const char EXAMPLES_DOWNLOAD_PATH[] = "StudioWelcome/ExamplesDownloadPath"; + QPointer s_view = nullptr; static StudioWelcomePlugin *s_pluginInstance = nullptr; @@ -224,9 +227,15 @@ public: Q_INVOKABLE void openProjectAt(int row) { + if (m_blockOpenRecent) + return; + + m_blockOpenRecent = true; const QString projectFile = data(index(row, 0), ProjectModel::FilePathRole).toString(); if (QFileInfo::exists(projectFile)) ProjectExplorer::ProjectExplorerPlugin::openProjectWelcomePage(projectFile); + + resetProjects(); } Q_INVOKABLE int get(int) { return -1; } @@ -265,31 +274,6 @@ public: const QString &tempFile, const QString &completeBaseName) { - if (!url.isEmpty()) { - ExampleCheckout *checkout = new ExampleCheckout; - checkout->checkoutExample(QUrl::fromUserInput(url), tempFile, completeBaseName); - connect(checkout, - &ExampleCheckout::finishedSucessfully, - this, - [checkout, formFile, example, explicitQmlproject]() { - const QString exampleFolder = checkout->extractionFolder() + "/" + example - + "/"; - - QString projectFile = exampleFolder + example + ".qmlproject"; - - if (!explicitQmlproject.isEmpty()) - projectFile = exampleFolder + explicitQmlproject; - - ProjectExplorer::ProjectExplorerPlugin::openProjectWelcomePage(projectFile); - - const QString qmlFile = QFileInfo(projectFile).dir().absolutePath() + "/" - + formFile; - - Core::EditorManager::openEditor(Utils::FilePath::fromString(qmlFile)); - }); - return; - } - const Utils::FilePath projectFile = Core::ICore::resourcePath("examples") / example / example + ".qmlproject"; ProjectExplorer::ProjectExplorerPlugin::openProjectWelcomePage(projectFile.toString()); @@ -311,39 +295,14 @@ private: bool m_communityVersion = true; bool m_enterpriseVersion = false; + bool m_blockOpenRecent = false; }; void ProjectModel::setupVersion() { - const ExtensionSystem::PluginSpec *pluginSpec = Utils::findOrDefault( - ExtensionSystem::PluginManager::plugins(), - Utils::equal(&ExtensionSystem::PluginSpec::name, QString("LicenseChecker"))); - - if (!pluginSpec) - return; - - ExtensionSystem::IPlugin *plugin = pluginSpec->plugin(); - - if (!plugin) - return; - - m_communityVersion = false; - - bool retVal = false; - bool success = QMetaObject::invokeMethod(plugin, - "qdsEnterpriseLicense", - Qt::DirectConnection, - Q_RETURN_ARG(bool, retVal)); - - if (!success) { - qWarning("Check for Qt Design Studio Enterprise License failed."); - return; - } - if (!retVal) { - qWarning("No Qt Design Studio Enterprise License. Disabling asset importer."); - return; - } - m_enterpriseVersion = true; + Utils::FoundLicense license = Utils::checkLicense(); + m_communityVersion = license == Utils::FoundLicense::community; + m_enterpriseVersion = license == Utils::FoundLicense::enterprise; } ProjectModel::ProjectModel(QObject *parent) @@ -520,8 +479,11 @@ QHash ProjectModel::roleNames() const void ProjectModel::resetProjects() { - beginResetModel(); - endResetModel(); + QTimer::singleShot(2000, this, [this]() { + beginResetModel(); + endResetModel(); + m_blockOpenRecent = false; + }); } class WelcomeMode : public Core::IMode @@ -617,6 +579,12 @@ void StudioWelcomePlugin::extensionsInitialized() Core::ICore::resourcePath("qmldesigner/studio_templates")); Core::ICore::setNewDialogFactory([](QWidget *parent) { return new QdsNewDialog(parent); }); + + const QString filters = QString("Project (*.qmlproject);;UI file (*.ui.qml);;QML file " + "(*.qml);;JavaScript file (*.js);;%1") + .arg(Utils::allFilesFilterString()); + + Core::DocumentManager::setFileDialogFilter(filters); } if (showSplashScreen()) { @@ -690,6 +658,20 @@ void StudioWelcomePlugin::resumeRemoveSplashTimer() m_removeSplashTimer.start(m_removeSplashRemainingTime); } +Utils::FilePath StudioWelcomePlugin::defaultExamplesPath() +{ + return Utils::FilePath::fromString( + QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)) + .pathAppended("QtDesignStudio"); +} + +QString StudioWelcomePlugin::examplesPathSetting() +{ + return Core::ICore::settings() + ->value(EXAMPLES_DOWNLOAD_PATH, defaultExamplesPath().toString()) + .toString(); +} + WelcomeMode::WelcomeMode() { setDisplayName(tr("Studio")); @@ -709,7 +691,7 @@ WelcomeMode::WelcomeMode() ExampleCheckout::registerTypes(); m_modeWidget = new QQuickWidget; - m_modeWidget->setMinimumSize(1024, 768); + m_modeWidget->setMinimumSize(640, 480); m_modeWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); QmlDesigner::Theme::setupTheme(m_modeWidget->engine()); m_modeWidget->engine()->addImportPath("qrc:/studiofonts"); @@ -801,19 +783,18 @@ StudioSettingsPage::StudioSettingsPage() : m_buildCheckBox(new QCheckBox(tr("Build"))) , m_debugCheckBox(new QCheckBox(tr("Debug"))) , m_analyzeCheckBox(new QCheckBox(tr("Analyze"))) + , m_pathChooser(new Utils::PathChooser()) { const QString toolTip = tr( "Hide top-level menus with advanced functionality to simplify the UI. Build is " "generally not required in the context of Qt Design Studio.Debug and Analyze" "are only required for debugging and profiling."); - QVBoxLayout *boxLayout = new QVBoxLayout(this); + QVBoxLayout *boxLayout = new QVBoxLayout(); setLayout(boxLayout); auto groupBox = new QGroupBox(tr("Hide Menu")); groupBox->setToolTip(toolTip); boxLayout->addWidget(groupBox); - boxLayout->addSpacerItem( - new QSpacerItem(10, 10, QSizePolicy::Expanding, QSizePolicy::Expanding)); auto verticalLayout = new QVBoxLayout(); groupBox->setLayout(verticalLayout); @@ -831,6 +812,29 @@ StudioSettingsPage::StudioSettingsPage() m_buildCheckBox->setChecked(hideBuildMenuSetting()); m_debugCheckBox->setChecked(hideDebugMenuSetting()); m_analyzeCheckBox->setChecked(hideAnalyzeMenuSetting()); + + auto examplesGroupBox = new QGroupBox(tr("Examples")); + boxLayout->addWidget(examplesGroupBox); + + auto horizontalLayout = new QHBoxLayout(); + examplesGroupBox->setLayout(horizontalLayout); + + auto label = new QLabel(tr("Examples path:")); + m_pathChooser->setFilePath( + Utils::FilePath::fromString(StudioWelcomePlugin::examplesPathSetting())); + auto resetButton = new QPushButton(tr("Reset Path")); + + connect(resetButton, &QPushButton::clicked, this, [this]() { + m_pathChooser->setFilePath(StudioWelcomePlugin::defaultExamplesPath()); + }); + + horizontalLayout->addWidget(label); + horizontalLayout->addWidget(m_pathChooser); + horizontalLayout->addWidget(resetButton); + + + boxLayout->addSpacerItem( + new QSpacerItem(10, 10, QSizePolicy::Expanding, QSizePolicy::Expanding)); } void StudioSettingsPage::apply() @@ -855,6 +859,14 @@ void StudioSettingsPage::apply() Core::RestartDialog restartDialog(Core::ICore::dialogParent(), restartText); restartDialog.exec(); } + + QSettings *s = Core::ICore::settings(); + const QString value = m_pathChooser->filePath().toString(); + + if (s->value(EXAMPLES_DOWNLOAD_PATH, false).toString() != value) { + s->setValue(EXAMPLES_DOWNLOAD_PATH, value); + emit s_pluginInstance->examplesDownloadPathChanged(value); + } } StudioWelcomeSettingsPage::StudioWelcomeSettingsPage() diff --git a/src/plugins/studiowelcome/studiowelcomeplugin.h b/src/plugins/studiowelcome/studiowelcomeplugin.h index 519f86dd863..cd6b8ea87f2 100644 --- a/src/plugins/studiowelcome/studiowelcomeplugin.h +++ b/src/plugins/studiowelcome/studiowelcomeplugin.h @@ -27,6 +27,7 @@ #include #include +#include #include @@ -46,6 +47,7 @@ private: QCheckBox *m_buildCheckBox; QCheckBox *m_debugCheckBox; QCheckBox *m_analyzeCheckBox; + Utils::PathChooser *m_pathChooser; }; class StudioWelcomeSettingsPage : public Core::IOptionsPage @@ -76,6 +78,12 @@ public: void pauseRemoveSplashTimer(); void resumeRemoveSplashTimer(); + static Utils::FilePath defaultExamplesPath(); + static QString examplesPathSetting(); + +signals: + void examplesDownloadPathChanged(const QString &path); + private: class WelcomeMode *m_welcomeMode = nullptr; QTimer m_removeSplashTimer; diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index fdf244cd9b6..2560f58e93b 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -836,6 +836,8 @@ public: setMultiTextCursorProvider([editor]() { return editor->multiTextCursor(); }); } + ~TextEditorWidgetFind() override { cancelCurrentSelectAll(); } + bool supportsSelectAll() const override { return true; } void selectAll(const QString &txt, FindFlags findFlags) override; @@ -858,7 +860,12 @@ void TextEditorWidgetFind::selectAll(const QString &txt, FindFlags findFlags) m_selectWatcher = new QFutureWatcher(); connect(m_selectWatcher, &QFutureWatcher::finished, this, [this]() { - const FileSearchResultList &results = m_selectWatcher->result(); + const QFuture future = m_selectWatcher->future(); + m_selectWatcher->deleteLater(); + m_selectWatcher = nullptr; + if (future.resultCount() <= 0) + return; + const FileSearchResultList &results = future.result(); const QTextCursor c(m_editor->document()); auto cursorForResult = [c](const FileSearchResult &r) { return Utils::Text::selectAt(c, r.lineNumber, r.matchStart + 1, r.matchLength); diff --git a/src/shared/qbs b/src/shared/qbs index 768b6894a8d..29b15e594f5 160000 --- a/src/shared/qbs +++ b/src/shared/qbs @@ -1 +1 @@ -Subproject commit 768b6894a8d189158fed83cbc79d92d5484cb2e4 +Subproject commit 29b15e594f50314959ce9b63c4a39cc86e831475 diff --git a/tests/manual/qml/testprojects/plugins/imports/MyPlugin2.1/MyComponent.qml b/tests/manual/qml/testprojects/plugins/imports/MyPlugin2.1/MyComponent.qml new file mode 100644 index 00000000000..e52e52d7d10 --- /dev/null +++ b/tests/manual/qml/testprojects/plugins/imports/MyPlugin2.1/MyComponent.qml @@ -0,0 +1,38 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +import QtQuick 2.0 + +Rectangle { + width: 60 + height: 60 + color: "green" + property alias text: textItem.text + + Text { + id: textItem + anchors.centerIn: parent + } +} diff --git a/tests/manual/qml/testprojects/plugins/imports/MyPlugin2.1/MyComponent2.qml b/tests/manual/qml/testprojects/plugins/imports/MyPlugin2.1/MyComponent2.qml new file mode 100644 index 00000000000..5e2306a716f --- /dev/null +++ b/tests/manual/qml/testprojects/plugins/imports/MyPlugin2.1/MyComponent2.qml @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +import QtQuick 2.0 + +Item { + property alias text: textItem.text + property alias color: rect.color + property alias radius: rect.radius + + Rectangle { + id: rect + anchors.fill: parent + color: "green" + + Text { + id: textItem + anchors.centerIn: parent + } + } +} diff --git a/tests/manual/qml/testprojects/plugins/imports/MyPlugin2.1/designer/MyComponentSpecifics.qml b/tests/manual/qml/testprojects/plugins/imports/MyPlugin2.1/designer/MyComponentSpecifics.qml new file mode 100644 index 00000000000..c6c888e369c --- /dev/null +++ b/tests/manual/qml/testprojects/plugins/imports/MyPlugin2.1/designer/MyComponentSpecifics.qml @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +import QtQuick 2.0 +import HelperWidgets 2.0 +import QtQuick.Layouts 1.0 + +Column { + anchors.left: parent.left + anchors.right: parent.right + + Section { + anchors.left: parent.left + anchors.right: parent.right + caption: qsTr("Color") + + ColorEditor { + caption: qsTr("Color") + backendValue: backendValues.color + supportGradient: true + } + + + } + + Section { + anchors.left: parent.left + anchors.right: parent.right + caption: "Rectangle" + + SectionLayout { + rows: 2 + + Label { + text: qsTr("Text") + } + + SecondColumnLayout { + LineEdit { + backendValue: backendValues.text + Layout.fillWidth: true + } + } + + Label { + text: qsTr("Radius") + } + SecondColumnLayout { + SpinBox { + backendValue: backendValues.radius + hasSlider: true + Layout.preferredWidth: 80 + minimumValue: 0 + maximumValue: Math.min(backendValues.height.value, backendValues.width.value) / 2 + } + ExpandingSpacer { + + } + } + } + } +} diff --git a/tests/manual/qml/testprojects/plugins/imports/MyPlugin2.1/designer/MyPlugin.metainfo b/tests/manual/qml/testprojects/plugins/imports/MyPlugin2.1/designer/MyPlugin.metainfo new file mode 100644 index 00000000000..5eef250228c --- /dev/null +++ b/tests/manual/qml/testprojects/plugins/imports/MyPlugin2.1/designer/MyPlugin.metainfo @@ -0,0 +1,44 @@ +MetaInfo { + Type { + name: "MyPlugin2.MyComponent" + icon: ":/qtquickplugin/images/item-icon16.png" + + ItemLibraryEntry { + name: "My Component" + category: "My Test Plugin 2" + libraryIcon: ":/qtquickplugin/images/item-icon.png" + version: "1.0" + requiredImport: "MyPlugin2" + + Property { + name: "text" + type: "binding" + value: "qsTr(\"This is text\")" + } + } + } + + Type { + name: "MyPlugin2.MyComponent2" + icon: ":/qtquickplugin/images/item-icon16.png" + + ItemLibraryEntry { + name: "My Component 2" + category: "My Test Plugin 2" + libraryIcon: ":/qtquickplugin/images/item-icon.png" + version: "1.0" + requiredImport: "MyPlugin2" + + Property { + name: "text" + type: "binding" + value: "qsTr(\"This is text\")" + } + + Property { name: "width"; type: "int"; value: 200; } + Property { name: "height"; type: "int"; value: 200; } + Property { name: "color"; type: "QColor"; value: "red"; } + } + } + +} diff --git a/tests/manual/qml/testprojects/plugins/imports/MyPlugin2.1/qmldir b/tests/manual/qml/testprojects/plugins/imports/MyPlugin2.1/qmldir new file mode 100644 index 00000000000..660f6d416b0 --- /dev/null +++ b/tests/manual/qml/testprojects/plugins/imports/MyPlugin2.1/qmldir @@ -0,0 +1,2 @@ +MyComponent 1.0 MyComponent.qml +MyComponent2 1.0 MyComponent2.qml diff --git a/tests/manual/qml/testprojects/plugins/plugins.qml b/tests/manual/qml/testprojects/plugins/plugins.qml index dc3d729077e..daf85ad3892 100644 --- a/tests/manual/qml/testprojects/plugins/plugins.qml +++ b/tests/manual/qml/testprojects/plugins/plugins.qml @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2022 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt Creator. @@ -23,13 +23,10 @@ ** ****************************************************************************/ -import QtQuick 2.3 -import QtQuick.Controls 1.2 -import QtQuick.Window 2.2 +import QtQuick import MyPlugin 1.0 -ApplicationWindow { - title: qsTr("Hello World") +Rectangle { width: 640 height: 480 @@ -38,3 +35,9 @@ ApplicationWindow { } } + +/*##^## +Designer { + D{i:0;autoSize:true;height:480;width:640} +} +##^##*/ diff --git a/tests/manual/qml/testprojects/plugins/plugins2.qml b/tests/manual/qml/testprojects/plugins/plugins2.qml new file mode 100644 index 00000000000..9e586d56f85 --- /dev/null +++ b/tests/manual/qml/testprojects/plugins/plugins2.qml @@ -0,0 +1,38 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +import QtQuick 2.3 +import MyPlugin2 1.0 + +Rectangle { + + width: 640 + height: 480 + + MyComponent { + text: qsTr("Some Text") + + } +} diff --git a/tests/system/README b/tests/system/README index 61847485fbf..2d71245cb34 100644 --- a/tests/system/README +++ b/tests/system/README @@ -59,7 +59,7 @@ This folder must contain the following: * a speedcrunch 0.11 repository (or source copy) inside 'creator-test-data' named 'speedcrunch' Fifth - you'll have to make sure that some needed tools are available (no matter on which OS you're on). - * cmake + * cmake 3.14 or newer * wget or curl, capable of HTTPS Normally it should be okay to just install them as usual and add their executables' path(s) to the PATH variable. diff --git a/tests/system/suite_general/tst_save_before_build/test.py b/tests/system/suite_general/tst_save_before_build/test.py index 728f849975a..3befbbd8eac 100644 --- a/tests/system/suite_general/tst_save_before_build/test.py +++ b/tests/system/suite_general/tst_save_before_build/test.py @@ -25,43 +25,63 @@ source("../../shared/qtcreator.py") -def ensureSaveBeforeBuildChecked(shouldBeChecked): + +def __openBuildAndRunSettings__(): invokeMenuItem("Tools", "Options...") mouseClick(waitForObjectItem(":Options_QListView", "Build & Run")) clickOnTab(":Options.qt_tabwidget_tabbar_QTabBar", "General") - if test.compare(waitForObject(":Build and Run.Save all files before build_QCheckBox").checked, - shouldBeChecked, "'Save all files before build' should be %s" % str(shouldBeChecked)): - clickButton(waitForObject(":Options.Cancel_QPushButton")) - else: - ensureChecked(":Build and Run.Save all files before build_QCheckBox", shouldBeChecked) - clickButton(waitForObject(":Options.OK_QPushButton")) + + +def verifySaveBeforeBuildChecked(shouldBeChecked): + __openBuildAndRunSettings__() + test.compare(waitForObject(":Build and Run.Save all files before build_QCheckBox").checked, + shouldBeChecked, + "'Save all files before build' should be %s" % str(shouldBeChecked)) + clickButton(waitForObject(":Options.Cancel_QPushButton")) + + +def ensureSaveBeforeBuildChecked(shouldBeChecked): + __openBuildAndRunSettings__() + ensureChecked(":Build and Run.Save all files before build_QCheckBox", shouldBeChecked) + clickButton(waitForObject(":Options.OK_QPushButton")) + def main(): startQC() if not startedWithoutPluginError(): return - ensureSaveBeforeBuildChecked(False) - # create qt quick application - createNewQtQuickApplication(tempDir(), "SampleApp") - for expectDialog in [True, False]: - files = ["SampleApp.SampleApp\\.pro", - "SampleApp.Sources.main\\.cpp", - "SampleApp.Resources.qml\.qrc./.main\\.qml"] - for i, file in enumerate(files): - if not openDocument(file): - test.fatal("Could not open file '%s'" % simpleFileName(file)) - continue - test.log("Changing file '%s'" % simpleFileName(file)) - typeLines(getEditorForFileSuffix(file, True), "") - # try to compile - clickButton(waitForObject(":*Qt Creator.Build Project_Core::Internal::FancyToolButton")) - try: - ensureChecked(":Save Changes.Always save files before build_QCheckBox", - i == len(files) - 1, 5000) # At the last iteration, check the box - clickButton(waitForObject(":Save Changes.Save All_QPushButton")) - test.verify(expectDialog, "The 'Save Changes' dialog was shown.") - except: - test.verify(not expectDialog, "The 'Save Changes' dialog was not shown.") - waitForCompile() - ensureSaveBeforeBuildChecked(True) + verifySaveBeforeBuildChecked(False) + for buildSystem in ["CMake", "qmake"]: + projectName = "SampleApp-" + buildSystem + ensureSaveBeforeBuildChecked(False) + # create qt quick application + createNewQtQuickApplication(tempDir(), projectName, buildSystem=buildSystem) + for expectDialog in [True, False]: + verifySaveBeforeBuildChecked(not expectDialog) + if buildSystem == "CMake": + files = ["%s.CMakeLists\\.txt" % projectName, + "%s.%s.Source Files.main\\.cpp" % (projectName, projectName), + "%s.%s.qml\.qrc./.main\\.qml" % (projectName, projectName)] + elif buildSystem == "qmake": + files = ["%s.%s\\.pro" % (projectName, projectName), + "%s.Sources.main\\.cpp" % projectName, + "%s.Resources.qml\.qrc./.main\\.qml" % projectName] + for i, file in enumerate(files): + if not openDocument(file): + test.fatal("Could not open file '%s'" % simpleFileName(file)) + continue + test.log("Changing file '%s'" % simpleFileName(file)) + typeLines(getEditorForFileSuffix(file, True), "") + # try to compile + clickButton(waitForObject(":*Qt Creator.Build Project_Core::Internal::FancyToolButton")) + try: + ensureChecked(":Save Changes.Always save files before build_QCheckBox", + i == len(files) - 1, 5000) # At the last iteration, check the box + clickButton(waitForObject(":Save Changes.Save All_QPushButton")) + test.verify(expectDialog, "The 'Save Changes' dialog was shown.") + except: + test.verify(not expectDialog, "The 'Save Changes' dialog was not shown.") + waitForCompile() + verifySaveBeforeBuildChecked(True) + invokeMenuItem("File", "Close All Projects and Editors") invokeMenuItem("File", "Exit") diff --git a/tests/unit/unittest/unittest.qbs b/tests/unit/unittest/unittest.qbs index 83c6260c5a9..5401aa09e1b 100644 --- a/tests/unit/unittest/unittest.qbs +++ b/tests/unit/unittest/unittest.qbs @@ -4,52 +4,10 @@ import qbs.FileInfo Project { name: "Unit test & helper products" - Product { - name: "qtc_gtest_gmock" - - Export { - property bool useExternalLibs: preferExternalLibs && externalLibsPresent - property bool useRepo: !useExternalLibs && hasRepo - property bool preferExternalLibs: true - property bool externalLibsPresent: preferExternalLibs && gtest.present && gmock.present - property string repoDir: FileInfo.joinPaths(path, "3rdparty", "googletest") - property string gtestDir: FileInfo.joinPaths(repoDir, "googletest") - property string gmockDir: FileInfo.joinPaths(repoDir, "googlemock") - property bool hasRepo: File.exists(gtestDir) - - Depends { name: "pkgconfig"; condition: preferExternalLibs; required: false } - Depends { name: "gtest"; condition: preferExternalLibs; required: false } - Depends { name: "gmock"; condition: preferExternalLibs; required: false } - - Depends { name: "cpp" } - Group { - name: "Files from repository" - condition: qtc_gtest_gmock.useRepo - cpp.includePaths: [ - qtc_gtest_gmock.gtestDir, - qtc_gtest_gmock.gmockDir, - FileInfo.joinPaths(qtc_gtest_gmock.gtestDir, "include"), - FileInfo.joinPaths(qtc_gtest_gmock.gmockDir, "include"), - ] - files: [ - FileInfo.joinPaths(qtc_gtest_gmock.gtestDir, "src", "gtest-all.cc"), - FileInfo.joinPaths(qtc_gtest_gmock.gmockDir, "src", "gmock-all.cc"), - ] - } - - Properties { - condition: useRepo - cpp.includePaths: [ - FileInfo.joinPaths(gtestDir, "include"), - FileInfo.joinPaths(gmockDir, "include"), - ] - } - } - } - QtcProduct { name: "Unit test" condition: qtc_gtest_gmock.hasRepo || qtc_gtest_gmock.externalLibsPresent + type: ["application", "autotest"] consoleApplication: true destinationDirectory: FileInfo.joinPaths(project.buildDirectory, @@ -72,7 +30,7 @@ Project { Depends { name: "pkgconfig"; required: false } Depends { name: "benchmark"; required: false } - Depends { name: "qtc_gtest_gmock" } + Depends { name: "qtc_gtest_gmock"; required: false } sqlite_sources.buildSharedLib: false