diff --git a/coin/instructions/common_environment.yaml b/coin/instructions/common_environment.yaml index 66119627c55..dedcbaf2fc5 100644 --- a/coin/instructions/common_environment.yaml +++ b/coin/instructions/common_environment.yaml @@ -2,27 +2,45 @@ type: Group instructions: - type: Group instructions: + # Currently used Qt version for packaging ... - type: Group instructions: - - type: SetBuildDirectory - directory: "{{.AgentWorkingDir}}/build" - - type: MakeDirectory - directory: "{{.BuildDir}}" + - type: EnvironmentVariable + variableName: QTC_QT_BASE_URL + variableValue: "https://ci-files02-hki.ci.qt.io/packages/jenkins/qt/6.8.0/release_content/" + - type: EnvironmentVariable + variableName: MACOSX_DEPLOYMENT_TARGET + variableValue: 12.0 + enable_if: + condition: property + property: features + not_contains_value: "OldestQt" + # ... or oldest supported Qt version + - type: Group + instructions: + - type: EnvironmentVariable + variableName: QTC_QT_BASE_URL + variableValue: "https://ci-files02-hki.ci.qt.io/packages/jenkins/qt/6.4.3/release_content/" + - type: EnvironmentVariable + variableName: MACOSX_DEPLOYMENT_TARGET + variableValue: 11.0 + enable_if: + condition: property + property: features + contains_value: "OldestQt" + - type: SetBuildDirectory + directory: "{{.AgentWorkingDir}}/build" + - type: MakeDirectory + directory: "{{.BuildDir}}" - type: EnvironmentVariable variableName: QTC_BUILD_TYPE variableValue: "RelWithDebInfo" - type: EnvironmentVariable variableName: LLVM_BASE_URL variableValue: https://ci-files02-hki.ci.qt.io/packages/jenkins/qtcreator_libclang/libclang-release_19.1.0-based - - type: EnvironmentVariable - variableName: QTC_QT_BASE_URL - variableValue: "https://ci-files02-hki.ci.qt.io/packages/jenkins/qt/6.8.0/release_content/" - type: EnvironmentVariable variableName: QTC_QT_MODULES variableValue: "qt5compat qtbase qtdeclarative qtimageformats qtquick3d qtquicktimeline qtserialport qtshadertools qtsvg qttools qttranslations qtwebengine" - - type: EnvironmentVariable - variableName: MACOSX_DEPLOYMENT_TARGET - variableValue: 11.0 - type: EnvironmentVariable variableName: SDKTOOL_MACOSX_DEPLOYMENT_TARGET variableValue: 11.0 @@ -37,6 +55,17 @@ instructions: - type: EnvironmentVariable variableName: QTC_QT_POSTFIX variableValue: "-Windows-Windows_11_23H2-MSVC2022-Windows-Windows_11_23H2-X86_64.7z" + enable_if: + condition: property + property: features + not_contains_value: "OldestQt" + - type: EnvironmentVariable + variableName: QTC_QT_POSTFIX + variableValue: "-Windows-Windows_11_22H2-MSVC2019-Windows-Windows_11_22H2-X86_64.7z" + enable_if: + condition: property + property: features + contains_value: "OldestQt" - type: EnvironmentVariable variableName: QTC_SDKTOOL_QT_EXT variableValue: ".zip" @@ -55,9 +84,30 @@ instructions: equals_value: Windows - type: Group instructions: - - type: EnvironmentVariable - variableName: QTC_QT_POSTFIX - variableValue: "-Linux-RHEL_8_8-GCC-Linux-RHEL_8_8-X86_64.7z" + - type: Group + instructions: + - type: EnvironmentVariable + variableName: QTC_QT_POSTFIX + variableValue: "-Linux-RHEL_8_8-GCC-Linux-RHEL_8_8-X86_64.7z" + - type: EnvironmentVariable + variableName: QTC_ICU_URL + variableValue: "https://ci-files02-hki.ci.qt.io/packages/jenkins/development_releases/prebuilt/icu/prebuilt/73.2/icu-linux-g++-Rhel8.6-x64.7z" + enable_if: + condition: property + property: features + not_contains_value: "OldestQt" + - type: Group + instructions: + - type: EnvironmentVariable + variableName: QTC_QT_POSTFIX + variableValue: "-Linux-RHEL_8_4-GCC-Linux-RHEL_8_4-X86_64.7z" + - type: EnvironmentVariable + variableName: QTC_ICU_URL + variableValue: "https://ci-files02-hki.ci.qt.io/packages/jenkins/development_releases/prebuilt/icu/prebuilt/56.1/icu-linux-g++-Rhel7.2-x64.7z" + enable_if: + condition: property + property: features + contains_value: "OldestQt" - type: EnvironmentVariable variableName: QTC_SDKTOOL_QT_EXT variableValue: ".tar.xz" @@ -67,9 +117,6 @@ instructions: - type: EnvironmentVariable variableName: QTC_LLVM_POSTFIX variableValue: "-linux-Rhel8.8-gcc10.3-x86_64.7z" - - type: EnvironmentVariable - variableName: QTC_ICU_URL - variableValue: "https://ci-files02-hki.ci.qt.io/packages/jenkins/development_releases/prebuilt/icu/prebuilt/56.1/icu-linux-g++-Rhel7.2-x64.7z" - type: EnvironmentVariable variableName: PYTHON_EXECUTABLE variableValue: "python3" @@ -124,6 +171,17 @@ instructions: - type: EnvironmentVariable variableName: QTC_QT_POSTFIX variableValue: "-MacOS-MacOS_14-Clang-MacOS-MacOS_14-X86_64-ARM64.7z" + enable_if: + condition: property + property: features + not_contains_value: "OldestQt" + - type: EnvironmentVariable + variableName: QTC_QT_POSTFIX + variableValue: "-MacOS-MacOS_12-Clang-MacOS-MacOS_12-X86_64-ARM64.7z" + enable_if: + condition: property + property: features + contains_value: "OldestQt" - type: EnvironmentVariable variableName: QTC_SDKTOOL_QT_EXT variableValue: ".tar.xz" diff --git a/coin/product_dependencies.yaml b/coin/product_dependencies.yaml index 9f43d3da95f..959465886ef 100644 --- a/coin/product_dependencies.yaml +++ b/coin/product_dependencies.yaml @@ -1,4 +1,3 @@ dependencies: ../../qt/qt5.git: ref: "6.8" - diff --git a/dist/changelog/changes-15.0.0.md b/dist/changelog/changes-15.0.0.md index 6ff5d51b73a..558ef3cc63f 100644 --- a/dist/changelog/changes-15.0.0.md +++ b/dist/changelog/changes-15.0.0.md @@ -47,6 +47,8 @@ Editing * Added `Fold Recursively` and `Unfold Recursively` * Fixed the display of multi-line annotations ([QTCREATORBUG-29951](https://bugreports.qt.io/browse/QTCREATORBUG-29951)) +* Fixed the tab order in `Advanced Search` + ([QTCREATORBUG-31771](https://bugreports.qt.io/browse/QTCREATORBUG-31771)) ### C++ @@ -67,6 +69,8 @@ Editing ([QTCREATORBUG-31256](https://bugreports.qt.io/browse/QTCREATORBUG-31256)) * Fixed the display of annotations with array operators ([QTCREATORBUG-31670](https://bugreports.qt.io/browse/QTCREATORBUG-31670)) +* Fixed code formatting after `Apply Changes to Declaration / Definition` + ([QTCREATORBUG-31293](https://bugreports.qt.io/browse/QTCREATORBUG-31293)) * ClangFormat * Implemented `Export` and `Import` for the settings @@ -182,6 +186,8 @@ Projects * Fixed the option `Build Only the Application to Be Run` for the `Build before deploying` preferences ([QTCREATORBUG-31416](https://bugreports.qt.io/browse/QTCREATORBUG-31416)) +* Fixed the `vcpkg` support for Android + ([QTCREATORBUG-31883](https://bugreports.qt.io/browse/QTCREATORBUG-31883)) ### Workspace @@ -274,6 +280,11 @@ Extension Manager Platforms --------- +### Windows + +* Fixed that it wasn't possible to select a remote `qmake` executable + ([QTCREATORBUG-31939](https://bugreports.qt.io/browse/QTCREATORBUG-31939)) + ### macOS * Added support for back and forward gestures @@ -284,6 +295,8 @@ Platforms * Improved the responsiveness of Qt Creator during Android related operations * Fixed that the setup wizard could use the wrong NDK and build tools version ([QTCREATORBUG-31311](https://bugreports.qt.io/browse/QTCREATORBUG-31311)) +* Fixed a freeze in the preferences while an emulator is running + ([QTCREATORBUG-31912](https://bugreports.qt.io/browse/QTCREATORBUG-31912)) ### iOS @@ -314,11 +327,13 @@ Alexandru Croitor Ali Kianian Andre Hartmann André Pönitz +Andrii Batyiev Andrii Semkiv Artem Sokolovskii Artur Twardy Assam Boudjelthia Audun Sutterud +BogDan Vatra Burak Hancerli Christian Kandeler Christian Stenger @@ -332,6 +347,7 @@ Henning Gruendl Jaroslaw Kobus Jussi Witick Justyna Hudziak +Kai Köhne Karim Pinter Knud Dollereder Kwangsub Kim @@ -346,6 +362,7 @@ Mariusz Szczepanik Mathias Hasselmann Mats Honkamaa Mehdi Salem +Michael Weghorn Miikka Heikkinen Orgad Shaneh Pino Toscano @@ -366,5 +383,6 @@ Tim Jenßen Toni Saario Ulf Hermann Vikas Pachdha +Ville Lavonius Xavier Besson Zoltan Gera diff --git a/doc/qtcreator/images/qtcreator-debugger-general-options.png b/doc/qtcreator/images/qtcreator-debugger-general-options.png deleted file mode 100644 index 6ab957dd76a..00000000000 Binary files a/doc/qtcreator/images/qtcreator-debugger-general-options.png and /dev/null differ diff --git a/doc/qtcreator/images/qtcreator-preferences-debugger-general.webp b/doc/qtcreator/images/qtcreator-preferences-debugger-general.webp new file mode 100644 index 00000000000..9eb9c77ef8c Binary files /dev/null and b/doc/qtcreator/images/qtcreator-preferences-debugger-general.webp differ diff --git a/doc/qtcreator/images/qtcreator-preferences-telemetry-usage-statistics.webp b/doc/qtcreator/images/qtcreator-preferences-telemetry-usage-statistics.webp new file mode 100644 index 00000000000..392b518284d Binary files /dev/null and b/doc/qtcreator/images/qtcreator-preferences-telemetry-usage-statistics.webp differ diff --git a/doc/qtcreator/images/qtcreator-telemetry-settings.png b/doc/qtcreator/images/qtcreator-telemetry-settings.png deleted file mode 100644 index aac2db8240a..00000000000 Binary files a/doc/qtcreator/images/qtcreator-telemetry-settings.png and /dev/null differ diff --git a/doc/qtcreator/src/debugger/creator-only/creator-debugger-settings.qdoc b/doc/qtcreator/src/debugger/creator-only/creator-debugger-settings.qdoc index b3fef5c0e32..a26ac4a8f44 100644 --- a/doc/qtcreator/src/debugger/creator-only/creator-debugger-settings.qdoc +++ b/doc/qtcreator/src/debugger/creator-only/creator-debugger-settings.qdoc @@ -15,7 +15,7 @@ \uicontrol Debugger. In the \uicontrol General tab, you can specify settings that are common to all debuggers. - \image qtcreator-debugger-general-options.png "Debugger General preferences" + \image qtcreator-preferences-debugger-general.webp {Debugger General preferences} You can customize the appearance and behavior of the debug views and setting breakpoints, as well as map source paths to target paths. diff --git a/doc/qtcreator/src/debugger/creator-only/creator-debugger.qdoc b/doc/qtcreator/src/debugger/creator-only/creator-debugger.qdoc index 130a4665e0d..31937cc381a 100644 --- a/doc/qtcreator/src/debugger/creator-only/creator-debugger.qdoc +++ b/doc/qtcreator/src/debugger/creator-only/creator-debugger.qdoc @@ -838,7 +838,7 @@ To change the appearance and behavior of the debug views, set preferences in \preferences > \uicontrol Debugger > \uicontrol General. - \image qtcreator-debugger-general-options.png {General tab in Debugger preferences} + \image qtcreator-preferences-debugger-general.webp {General tab in Debugger preferences} For example, you can: @@ -1270,7 +1270,7 @@ at which the libraries were built, you can map source paths to target paths in \preferences > \uicontrol Debugger > \uicontrol General. - \image qtcreator-debugger-general-options.png {General tab in Debugger preferences} + \image qtcreator-preferences-debugger-general.webp {General tab in Debugger preferences} For more information, see \l{Source Paths Mapping}. diff --git a/doc/qtcreator/src/editors/creator-code-indentation.qdoc b/doc/qtcreator/src/editors/creator-code-indentation.qdoc index f088e833879..0fa0a4ce048 100644 --- a/doc/qtcreator/src/editors/creator-code-indentation.qdoc +++ b/doc/qtcreator/src/editors/creator-code-indentation.qdoc @@ -74,8 +74,8 @@ To use a context-specific margin when available, select the \uicontrol {Use context-specific margin} check box. \if defined(qtcreator) - Then, use the ClangFormat \c ColumnLimit option to set the margin, for - example. + Then, use the \l{ClangFormat Style Options}{ClangFormat} \c ColumnLimit + option to set the margin, for example. \sa {C++ Code Style} \endif diff --git a/doc/qtcreator/src/editors/creator-only/creator-beautifier.qdoc b/doc/qtcreator/src/editors/creator-only/creator-beautifier.qdoc index b8b9efda4b5..2f74bd44e33 100644 --- a/doc/qtcreator/src/editors/creator-only/creator-beautifier.qdoc +++ b/doc/qtcreator/src/editors/creator-only/creator-beautifier.qdoc @@ -23,7 +23,7 @@ \li \l{http://astyle.sourceforge.net}{Artistic Style} - \li \l{http://clang.llvm.org/docs/ClangFormat.html}{ClangFormat} + \li \l{ClangFormat: Documentation}{ClangFormat} \li \l{http://uncrustify.sourceforge.net}{Uncrustify} @@ -41,7 +41,7 @@ \list \li \l{http://sourceforge.net/projects/astyle/files/astyle} {Artistic Style} - \li \l{http://llvm.org/releases/download.html}{ClangFormat} + \li \l{ClangFormat: Download}{ClangFormat} \li \l{http://sourceforge.net/projects/uncrustify/files/uncrustify} {Uncrustify} \endlist diff --git a/doc/qtcreator/src/editors/creator-only/creator-preferences-cpp-code-style.qdoc b/doc/qtcreator/src/editors/creator-only/creator-preferences-cpp-code-style.qdoc index 024b731d3ca..a2f6521d85f 100644 --- a/doc/qtcreator/src/editors/creator-only/creator-preferences-cpp-code-style.qdoc +++ b/doc/qtcreator/src/editors/creator-only/creator-preferences-cpp-code-style.qdoc @@ -11,7 +11,7 @@ \brief Set global code style for C++ files. - \QC uses the Clang \l{https://clang.llvm.org/docs/LibFormat.html}{LibFormat} + \QC uses the Clang \l{LibFormat: Documentation}{LibFormat} library to automatically format and indent C++ code. It enforces a coding style for a project or the whole organization. @@ -25,7 +25,8 @@ \li \uicontrol {Indenting Only} to only indent code. \li \uicontrol {Full Formatting} to use the \key {Ctrl+I} keyboard shortcut to format code instead of indenting. - \li \uicontrol {Use Built-In Indenter} to turn off ClangFormat. + \li \uicontrol {Use Built-In Indenter} to turn off + \l{ClangFormat: Documentation}{ClangFormat}. \endlist \li Select \uicontrol {Ignore files greater than} to make parsing faster by ignoring big files. Specify the maximum size of files to parse. @@ -38,10 +39,8 @@ \li In \uicontrol {Custom settings}, select the settings to change, and then select \uicontrol Copy. \li Give a name to the settings, and select \uicontrol OK. - \li In \uicontrol ClangFormat, edit the - \l{https://clang.llvm.org/docs/ClangFormatStyleOptions.html} - {ClangFormat Style Options}. The live preview shows how the - preferences change the indentation. + \li In \uicontrol ClangFormat, edit the \l {ClangFormat Style Options}. + The live preview shows how the preferences change the indentation. If you enter invalid values, you see warning messages. \endlist diff --git a/doc/qtcreator/src/external-resources/external-resources.qdoc b/doc/qtcreator/src/external-resources/external-resources.qdoc index 28b741f841a..c4e4ec852b2 100644 --- a/doc/qtcreator/src/external-resources/external-resources.qdoc +++ b/doc/qtcreator/src/external-resources/external-resources.qdoc @@ -253,3 +253,19 @@ \externalpage https://code.qt.io/cgit/qt-creator/qt-creator.git/tree/share/qtcreator/jsonschemas/project.json \title project.json */ +/*! + \externalpage https://clang.llvm.org/docs/ClangFormat.html + \title ClangFormat: Documentation +*/ +/*! + \externalpage https://llvm.org/releases/download.html + \title ClangFormat: Download +*/ +/*! + \externalpage https://clang.llvm.org/docs/ClangFormatStyleOptions.html + \title ClangFormat Style Options +*/ +/*! + \externalpage https://clang.llvm.org/docs/LibFormat.html + \title LibFormat: Documentation +*/ diff --git a/doc/qtcreator/src/howto/creator-only/creator-telemetry.qdoc b/doc/qtcreator/src/howto/creator-only/creator-telemetry.qdoc new file mode 100644 index 00000000000..b2cfde5c4bc --- /dev/null +++ b/doc/qtcreator/src/howto/creator-only/creator-telemetry.qdoc @@ -0,0 +1,38 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page creator-how-to-collect-usage-statistics.html + \previouspage creator-how-tos.html + + \ingroup creator-how-to-use + + \title Collect usage statistics + + When you install \QC with \QOI, you can allow it to collect pseudonymous + information about your system and \QC use. If you decline, the telemetry + plugin is not installed and no usage statistics are collected. + + \section1 Principles of data collection + + No personal data, such as names, IP addresses, MAC addresses, or project + and path names are collected. However, QUuid objects are used to identify + data records that belong to one user. The objects cannot be converted + back to the actual values from which they were generated. + + For more information about Qt privacy policy, see + \l{https://www.qt.io/terms-conditions/privacy-and-security} + {Qt Appendix for Privacy and Security}. + + \QC respects the same privacy rules. + + \section1 Turn on data collection + + To allow the telemetry plugin to collect data, go to \preferences > + \uicontrol Telemetry > \uicontrol {Usage Statistics}, and then select + \uicontrol {Send pseudonymous usage statistics}. + + \image qtcreator-preferences-telemetry-usage-statistics.webp {Usage Statistics} + + \sa {Installation} +*/ diff --git a/doc/qtcreator/src/howto/creator-telemetry.qdoc b/doc/qtcreator/src/howto/creator-telemetry.qdoc deleted file mode 100644 index 672ef719eb0..00000000000 --- a/doc/qtcreator/src/howto/creator-telemetry.qdoc +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only - -/*! - \page creator-telemetry.html - \if defined(qtdesignstudio) - \previouspage creator-quick-ui-forms.html - \nextpage collecting-usage-statistics.html - \else - \previouspage creator-how-tos.html - \endif - - \ingroup creator-how-to-use - - \title Manage data collection - - \if defined (qtcreator) - When you install \QC as a part of Qt installation, you are asked whether - you allow it to collect pseudonymous information about your system and \QC - use. If you decline, the plugin is not installed and no analytics data is - collected. - - If you accept, all collected and transmitted data is fully transparent to - you. You can change the settings for collecting and transmitting data any - time. By default, no data is collected and you have to select a telemetry - mode for data collection to begin. - - - See \l {Collect usage statistics} for more information about the data - transmitted by the telemetry plugin and \l {Specify telemetry settings} - {specifying telemetry settings}. - \else - To enable the use of the telemetry plugin, you need to select \uicontrol - {Enable Usage Statistics} in the splash screen that appears when you first - launch \QDS. If the splash screen does not appear, you can enable the - telemetry plugin by selecting \uicontrol Help > \uicontrol {About Plugins} > - \uicontrol Utilities > \uicontrol UsageStatistic on Linux and Windows (or - \uicontrol {\QDS} > \uicontrol {About Plugins} > \uicontrol Utilities > - \uicontrol UsageStatistic on \macos). - \image studio-usage-statistics.png "Enabling Usage Statistics" - \endif - - \if defined(qtdesignstudio) - See below for more information about the collected data: - - \list - \li \l {Collect usage statistics} - \li \l {Collecting User Feedback} - \li \l {Reporting Crashes} - \endlist - \endif - - \section1 Principles of data collection - - No personal data, such as names, IP addresses, MAC addresses, or project - and path names are collected. However, QUuid objects are used to identify - data records that belong to one user. The objects cannot be converted - back to the actual values from which they were generated. - - For more information about Qt privacy policy, select - \l{https://www.qt.io/terms-conditions/#privacy} - {Legal Notice and Privacy Policy}. - - \sa {Collect usage statistics} -*/ - -/*! - \page collecting-usage-statistics.html - \if defined(qtdesignstudio) - \previouspage creator-telemetry.html - \nextpage studio-user-feedback.html - \else - \previouspage creator-how-tos.html - \endif - - \ingroup creator-how-to-use - - \title Collect usage statistics - - The telemetry plugin uses the - \l{https://api.kde.org/frameworks/kuserfeedback/html/index.html} - {KUserFeedback} framework to collect the usage data. The library - has been designed from the user data privacy point of view and - \QC respects the same privacy rules. - - The data is transmitted to the backend storage using an encrypted - connection. The storage is located in the same Heroku backend as the - \QOI backend. Physically, data is stored in the Amazon cloud. - - \section1 Specify telemetry settings - - To determine what data is transmitted to the backend storage: - - \list 1 - \li Select \preferences > \uicontrol Telemetry - > \uicontrol {Usage Statistics}. - \image qtcreator-telemetry-settings.png "Telemetry settings" - \li In the \uicontrol {Telemetry mode} list, select the mode that - determines what kind of data is collected. - \li In the \uicontrol {Data sources} list, select entries to view - exactly what data is collected. Deselect check boxes for data - that you do not want to transmit to the backend storage. - \endlist - - \sa {Manage data collection} -*/ diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-creating.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-creating.qdoc index 2d7a3772e17..e100c948f1e 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-creating.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-creating.qdoc @@ -258,17 +258,29 @@ system libraries or your own libraries. Further, your own libraries might link to other libraries. To compile your project and benefit from services such as code completion and syntax highlighting, add the libraries to your - project. The process of adding a library to a project depends on the build - system that you use. + project. - \section1 CMake projects + \section1 Create subprojects - To add CMakeLists.txt files to any project, use the - \l{https://cmake.org/cmake/help/latest/command/add_subdirectory.html} - {add_subdirectory} command. The files can define complete projects that - you include into the top-level project or any other CMake commands. + To create subprojects and add them to a project: - \section1 qmake projects + \list 1 + \li Right-click the project name in the \l Projects view to open the + context menu, and select \uicontrol {New Subproject}. + \li Follow the instructions of the wizard to create a subproject. + \image qtcreator-project-qt-quick.webp {New Project dialog} + \endlist + + \section1 Add existing projects as subprojects + + To add an existing project as a subproject: + + \list 1 + \li Select \uicontrol {Add Existing Projects} in the context menu. + \li In the file browser dialog, locate your subproject. + \endlist + + \section1 Create SUBDIRS projects for qmake When you create a new project and select qmake as the build system, you can add it to another project as a subproject in the @@ -292,28 +304,15 @@ and the subproject that you add as a value of the \l{Variables#subdirs} {SUBDIRS variable}. It also adds all the necessary files for the subproject. - \section2 Add subprojects to the root project + \section2 Specify dependencies - To create more subprojects, right-click the project name in the - \l Projects view to open the context menu, and select - \uicontrol {New Subproject}. Follow the steps in the - \uicontrol {New Subproject} wizard to create a subproject. - - \image qtcreator-project-qt-quick.webp {New Project dialog} - - To add an existing project as a subproject, select - \uicontrol {Add Existing Projects} in the context menu. - In the file browser dialog, locate your subproject. + To specify dependencies, use the \uicontrol{Add Library} wizard. \section2 Remove subprojects To remove subprojects, right-click the project name in the \uicontrol Projects view, and select \uicontrol {Remove Subproject} in the context menu. - \section2 Specify dependencies - - To specify dependencies, use the \uicontrol{Add Library} wizard. - \sa {Creating Projects}, {Use project wizards}, {Add libraries to qmake projects}, {Add libraries to CMake projects} */ diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-code-style.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-code-style.qdoc index 88e0354aaa9..e22fabd370d 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-code-style.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-code-style.qdoc @@ -53,9 +53,9 @@ project. \endlist - In rare cases, ClangFormat can trip over a code construct and - trigger a \QC crash. If that happens for your project, select - \uicontrol {Use Built-In Indenter} in \uicontrol {Formatting mode} to + In rare cases, \l{ClangFormat: Documentation}{ClangFormat} can trip over a + code construct and trigger a \QC crash. If that happens for your project, + select \uicontrol {Use Built-In Indenter} in \uicontrol {Formatting mode} to turn off ClangFormat for the project. If you can reproduce the crash, go to \uicontrol Help > \uicontrol {Report Bug} to report the bug and attach the code that triggers the crash to the bug report. diff --git a/doc/qtdesignstudio/images/studio-preferences-telemetry-usage-statistics.webp b/doc/qtdesignstudio/images/studio-preferences-telemetry-usage-statistics.webp new file mode 100644 index 00000000000..a5c4dbf6826 Binary files /dev/null and b/doc/qtdesignstudio/images/studio-preferences-telemetry-usage-statistics.webp differ diff --git a/doc/qtdesignstudio/src/how-to/qtdesignstudio-usage-statistics.qdoc b/doc/qtdesignstudio/src/how-to/qtdesignstudio-usage-statistics.qdoc new file mode 100644 index 00000000000..750d2d11c6b --- /dev/null +++ b/doc/qtdesignstudio/src/how-to/qtdesignstudio-usage-statistics.qdoc @@ -0,0 +1,59 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page studio-telemetry.html + \previouspage creator-quick-ui-forms.html + \nextpage studio-collecting-usage-statistics.html + + \title Managing Data Collection + + See below for more information about the collected data: + + \list + \li \l {Collecting Usage Statistics} + \li \l {Collecting User Feedback} + \li \l {Reporting Crashes} + \endlist + + \section1 Principles of Data Collection + + No personal data, such as names, IP addresses, MAC addresses, or project + and path names are collected. However, QUuid objects are used to identify + data records that belong to one user. The objects cannot be converted + back to the actual values from which they were generated. + + For more information about Qt privacy policy, see + \l{https://www.qt.io/terms-conditions/privacy-and-security} + {Qt Appendix for Privacy and Security}. + + \sa {Collecting Usage Statistics} +*/ + +/*! + \page studio-collecting-usage-statistics.html + \previouspage studio-telemetry.html + \nextpage studio-user-feedback.html + + \title Collecting Usage Statistics + + The telemetry plugin uses the + \l{https://api.kde.org/frameworks/kuserfeedback/html/index.html} + {KUserFeedback} framework to collect the usage data. The library + has been designed from the user data privacy point of view and + \QC respects the same privacy rules. + + The data is transmitted to the backend storage using an encrypted + connection. The storage is located in the same Heroku backend as the + \QOI backend. Physically, data is stored in the Amazon cloud. + + \section1 Turning on Telemetry + + To determine what data is transmitted to the backend storage, go to + \preferences > \uicontrol Telemetry > \uicontrol {Usage Statistics}, + and then select \uicontrol {Enable telemetry}. + + \image studio-preferences-telemetry-usage-statistics.webp {Usage Statistics} + + \sa {Managing Data Collection} +*/ diff --git a/doc/qtdesignstudio/src/qtdesignstudio-advanced.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-advanced.qdoc index 7c11a4a7800..fbc362f6c8c 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-advanced.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-advanced.qdoc @@ -23,7 +23,7 @@ Some of the wizard templates create projects that contain UI files. You should always edit UI files in the \l {2D} and \l Properties view, to avoid breaking the code. - \li \l{Manage Data Collection} + \li \l{Managing Data Collection} You can enable \QDS to report crashes automatically. If you enable the telemetry plugin, you can turn on the pseudonymous user diff --git a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc index 8cf7f393684..dfe679ef114 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc @@ -217,9 +217,9 @@ \li Extending Component Functionality \endomit \li \l{UI Files} - \li \l{Manage Data Collection} + \li \l{Managing Data Collection} \list - \li \l {Collect Usage Statistics} + \li \l {Collecting Usage Statistics} \li \l {Collecting User Feedback} \li \l {Reporting Crashes} \endlist diff --git a/scripts/build.py b/scripts/build.py index 1b4306911dc..6f2cd39e668 100755 --- a/scripts/build.py +++ b/scripts/build.py @@ -272,12 +272,7 @@ def package_qtcreator(args, paths): if not args.no_cdb: common.check_print_call(command + [paths.qtcreatorcdbext_install]) - # use -mf=off to avoid usage of the ARM executable compression filter, - # which cannot be extracted by p7zip - # use -snl to preserve symlinks even if their target doesn't exist - # which is important for the _dev package on Linux - # (only works with official/upstream 7zip) - zip = ['7z', 'a', '-mmt' + args.zip_threads, '-mf=off', '-snl'] + zip = common.sevenzip_command(args.zip_threads) if not args.no_zip: if not args.no_qtcreator: common.check_print_call(zip diff --git a/scripts/build_plugin.py b/scripts/build_plugin.py index 0de2a3949d6..81d80bd2561 100755 --- a/scripts/build_plugin.py +++ b/scripts/build_plugin.py @@ -147,12 +147,7 @@ def package(args, paths): if common.is_windows_platform() and args.sign_command: command = shlex.split(args.sign_command) common.check_print_call(command + [paths.install]) - # use -mf=off to avoid usage of the ARM executable compression filter, - # which cannot be extracted by p7zip - # use -snl to preserve symlinks even if their target doesn't exist - # which is important for the _dev package on Linux - # (only works with official/upstream 7zip) - zip = ['7z', 'a', '-mmt' + args.zip_threads, '-mf=off', '-snl'] + zip = common.sevenzip_command(args.zip_threads) common.check_print_call(zip + [os.path.join(paths.result, args.name + '.7z'), '*'], paths.install) if os.path.exists(paths.dev_install): # some plugins might not provide anything in Devel diff --git a/scripts/build_sdktool.py b/scripts/build_sdktool.py index b028079cafa..d831492939a 100755 --- a/scripts/build_sdktool.py +++ b/scripts/build_sdktool.py @@ -4,13 +4,13 @@ from __future__ import annotations import argparse -from itertools import islice import os from pathlib import Path from typing import NamedTuple from common import (is_linux_platform, is_mac_platform, is_windows_platform, - download_and_extract, check_print_call) + download_and_extract, check_print_call, sevenzip_command, + get_single_subdir) class BuildParams(NamedTuple): @@ -105,13 +105,6 @@ def sign_sdktool(params: BuildParams, env=environment) -def get_single_subdir(path: Path): - entries = list(islice(path.iterdir(), 2)) - if len(entries) == 1: - return path / entries[0] - return path - - def build_sdktool( qt_src_url: str, qt_build_base: Path, @@ -150,7 +143,7 @@ def zip_sdktool( ) -> None: glob = "*.exe" if is_windows_platform() else "*" check_print_call( - cmd=["7z", "a", str(out_7zip), glob], + cmd=sevenzip_command() + [str(out_7zip), glob], cwd=sdktool_target_path ) diff --git a/scripts/common.py b/scripts/common.py index 4ee0ddf7036..e343a46dad1 100644 --- a/scripts/common.py +++ b/scripts/common.py @@ -4,6 +4,7 @@ from __future__ import annotations import argparse import asyncio +from itertools import islice import os import locale from pathlib import Path @@ -61,6 +62,26 @@ def get_commit_SHA(path): git_sha = f.read().strip() return git_sha + +def get_single_subdir(path: Path): + entries = list(islice(path.iterdir(), 2)) + if len(entries) == 1: + return path / entries[0] + return path + + +def sevenzip_command(threads=None): + # use -mf=off to avoid usage of the ARM executable compression filter, + # which cannot be extracted by p7zip + # use -snl to preserve symlinks even if their target doesn't exist + # which is important for the _dev package on Linux + # (only works with official/upstream 7zip) + command = ['7z', 'a', '-mf=off', '-snl'] + if threads: + command.extend(['-mmt' + threads]) + return command + + # copy of shutil.copytree that does not bail out if the target directory already exists # and that does not create empty directories def copytree(src, dst, symlinks=False, ignore=None): @@ -142,22 +163,26 @@ async def download(url: str, target: Path) -> None: def download_and_extract(urls: list[str], target: Path, temp: Path) -> None: + download_and_extract_tuples([(url, target) for url in urls], temp) + + +def download_and_extract_tuples(urls_and_targets: list[tuple[str, Path]], temp: Path) -> None: temp.mkdir(parents=True, exist_ok=True) - target_files = [] + target_tuples : list[tuple[Path, Path]] = [] # TODO make this work with file URLs, which then aren't downloaded # but just extracted async def impl(): tasks : list[asyncio.Task] = [] - for url in urls: + for (url, target_path) in urls_and_targets: u = urlparse(url) filename = Path(u.path).name target_file = temp / filename - target_files.append(target_file) + target_tuples.append((target_file, target_path)) tasks.append(asyncio.create_task(download(url, target_file))) for task in tasks: await task asyncio.run(impl()) - for file in target_files: + for (file, target) in target_tuples: extract_file(file, target) diff --git a/scripts/install_qt.py b/scripts/install_qt.py index c2c68f0e2ad..777349c91d0 100755 --- a/scripts/install_qt.py +++ b/scripts/install_qt.py @@ -4,7 +4,7 @@ from __future__ import annotations import argparse -from common import download_and_extract +from common import download_and_extract_tuples from pathlib import Path import subprocess import sys @@ -73,10 +73,10 @@ def install_qt( with TemporaryDirectory() as temporary_dir: if need_to_install_qt: - urls = qt_modules + url_target_tuples = [(url, qt_path) for url in qt_modules] if icu_url: - qt_modules.append(icu_url) - download_and_extract(urls, qt_path, Path(temporary_dir)) + url_target_tuples.append((icu_url, qt_path / 'lib')) + download_and_extract_tuples(url_target_tuples, Path(temporary_dir)) patch_qt(qt_path) diff --git a/share/qtcreator/debugger/creatortypes.py b/share/qtcreator/debugger/creatortypes.py index 968eb26a51c..cd090c59ed8 100644 --- a/share/qtcreator/debugger/creatortypes.py +++ b/share/qtcreator/debugger/creatortypes.py @@ -202,6 +202,46 @@ def qdump__CPlusPlus__Internal__Value(d, value): d.putPlainChildren(value) +def is_windows_drive_letter(ch): + return (ch >= ord('A') and ch <= ord('Z')) or (ch >= ord('a') and ch <= ord('z')) + +def is_relative_filepath_enc(path_enc): + # Note: path is hex-encoded UTF-16 here, i.e. 4 byte per original QChar + """ + This needs to stay in sync with the implementation on the C++ side + in filepath.cpp. + + bool isWindowsDriveLetter(QChar ch) + { + return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'); + } + bool startsWithWindowsDriveLetterAndSlash(QStringView path) + { + return path.size() > 2 && path[1] == ':' && path[2] == '/ + && isWindowsDriveLetter(path[0]); + } + bool FilePath::isRelativePath() const + { + const QStringView p = pathView(); + if (p.startsWith('/')) + return false; + if (startsWithWindowsDriveLetterAndSlash(p)) + return false; + if (p.startsWith(u":/")) // QRC + return false; + return true; + } + """ + colon = "3A00" + slash = "2F00" + if path_enc.startswith(slash): + return False + if path_enc[4:12] == colon + slash and is_windows_drive_letter(int(path_enc[0:2], 16)): + return False + if path_enc.startswith(colon + slash): + return False + return True + def qdump__Utils__FilePath(d, value): data, path_len, scheme_len, host_len = d.split("{@QString}IHH", value) length, enc = d.encodeStringHelper(data, d.displayStringLimit) @@ -216,8 +256,10 @@ def qdump__Utils__FilePath(d, value): dot = "2E00" colon = "3A00" val = scheme_enc + colon + slash + slash + host_enc - if not path_enc.startswith(slash): + if is_relative_filepath_enc(path_enc): val += slash + dot + slash + elif is_windows_drive_letter(int(path_enc[0:2], 16)): + val += slash val += path_enc else: val = enc diff --git a/share/qtcreator/debugger/lldbbridge.py b/share/qtcreator/debugger/lldbbridge.py index 9ec8586116a..4863a0b3f58 100644 --- a/share/qtcreator/debugger/lldbbridge.py +++ b/share/qtcreator/debugger/lldbbridge.py @@ -988,7 +988,7 @@ class Dumper(DumperBase): connect_options = lldb.SBPlatformConnectOptions(self.remoteChannel_) res = self.target.GetPlatform().ConnectRemote(connect_options) - DumperBase.warn("CONNECT: %s %s platform: %s %s" % (res, + DumperBase.warn("CONNECT: %s %s platform: %s connected: %s" % (res, self.remoteChannel_, self.target.GetPlatform().GetName(), self.target.GetPlatform().IsConnected())) diff --git a/share/qtcreator/lua-plugins/luatests/tst_texteditor.lua b/share/qtcreator/lua-plugins/luatests/tst_texteditor.lua index a33c6bfe783..a21c08659d2 100644 --- a/share/qtcreator/lua-plugins/luatests/tst_texteditor.lua +++ b/share/qtcreator/lua-plugins/luatests/tst_texteditor.lua @@ -46,6 +46,9 @@ local function tst_embedWidget() } embed = editor:addEmbeddedWidget(layout, cursor:mainCursor():position()) + embed:onShouldClose(function() + embed:close() + end) end local function setup() diff --git a/src/libs/extensionsystem/pluginmanager.cpp b/src/libs/extensionsystem/pluginmanager.cpp index 1a9de94160e..6f9e0e586ae 100644 --- a/src/libs/extensionsystem/pluginmanager.cpp +++ b/src/libs/extensionsystem/pluginmanager.cpp @@ -1451,10 +1451,10 @@ void PluginManagerPrivate::loadPlugins() void PluginManagerPrivate::loadPluginsAtRuntime(const QSet &plugins) { - const bool allSoftloadable = allOf(plugins, &PluginSpec::isSoftLoadable); + const bool allSoftloadable = allOf(plugins, &PluginSpec::isEffectivelySoftloadable); if (!allSoftloadable) { const QStringList notSoftLoadablePlugins = Utils::transform( - Utils::filtered(plugins, std::not_fn(&PluginSpec::isSoftLoadable)), + Utils::filtered(plugins, std::not_fn(&PluginSpec::isEffectivelySoftloadable)), &PluginSpec::displayName); qWarning().noquote() << "PluginManagerPrivate::loadPluginsAtRuntime(): trying to load non-softloadable" @@ -1464,7 +1464,7 @@ void PluginManagerPrivate::loadPluginsAtRuntime(const QSet &plugin // load the plugins and their dependencies (if possible) ordered by dependency const QList queue = filtered(loadQueue(), [&plugins](PluginSpec *spec) { // Is the current plugin already running, or not soft loadable? - if (spec->state() == PluginSpec::State::Running || !spec->isSoftLoadable()) + if (spec->state() == PluginSpec::State::Running || !spec->isEffectivelySoftloadable()) return false; // Is the current plugin in the list of plugins to load? diff --git a/src/libs/extensionsystem/pluginspec.cpp b/src/libs/extensionsystem/pluginspec.cpp index 302690da8f2..bb58e36a28d 100644 --- a/src/libs/extensionsystem/pluginspec.cpp +++ b/src/libs/extensionsystem/pluginspec.cpp @@ -497,6 +497,22 @@ bool PluginSpec::isForceDisabled() const return d->forceDisabled; } +bool PluginSpec::isEffectivelySoftloadable() const +{ + if (state() == Running) + return true; + + if (!d->softLoadable) + return false; + + if (state() < PluginSpec::Resolved) + return false; // We won't know yet. + + return !Utils::anyOf(dependencySpecs(), [](PluginSpec *dependency) { + return !dependency->isEffectivelySoftloadable(); + }); +} + /*! Returns whether the plugin is allowed to be loaded during runtime without a restart. diff --git a/src/libs/extensionsystem/pluginspec.h b/src/libs/extensionsystem/pluginspec.h index 2f5a5a37209..dfa7274f0c7 100644 --- a/src/libs/extensionsystem/pluginspec.h +++ b/src/libs/extensionsystem/pluginspec.h @@ -131,6 +131,7 @@ public: virtual bool isForceEnabled() const; virtual bool isForceDisabled() const; virtual bool isSoftLoadable() const; + virtual bool isEffectivelySoftloadable() const; virtual QVector dependencies() const; virtual QJsonObject metaData() const; diff --git a/src/libs/utils/devicefileaccess.cpp b/src/libs/utils/devicefileaccess.cpp index f86ccf122e5..08eb2b956cf 100644 --- a/src/libs/utils/devicefileaccess.cpp +++ b/src/libs/utils/devicefileaccess.cpp @@ -239,8 +239,10 @@ Result DeviceFileAccess::copyRecursively(const FilePath &src, const FilePath &ta targetProcess.writeRaw(srcProcess.readAllRawStandardOutput()); }); - srcProcess.setCommand({sourceTar, {"-C", src.path(), "-cf", "-", "."}}); - targetProcess.setCommand({targetTar, {"xf", "-", "-C", target.path()}}); + srcProcess.setCommand({sourceTar, {"-cf", "-", "."}}); + srcProcess.setWorkingDirectory(src); + targetProcess.setCommand({targetTar, {"xf", "-"}}); + targetProcess.setWorkingDirectory(target); targetProcess.start(); targetProcess.waitForStarted(); diff --git a/src/libs/utils/filepath.cpp b/src/libs/utils/filepath.cpp index 89b3ed3685b..e617ddf1eb3 100644 --- a/src/libs/utils/filepath.cpp +++ b/src/libs/utils/filepath.cpp @@ -869,6 +869,13 @@ static bool startsWithWindowsDriveLetterAndSlash(QStringView path) return path.size() > 2 && path[1] == ':' && path[2] == '/' && isWindowsDriveLetter(path[0]); } +static bool startsWithWindowsDriveLetter(QStringView path) +{ + if (path.size() > 2 && startsWithWindowsDriveLetterAndSlash(path)) + return true; + return path.size() == 2 && path[1] == ':' && isWindowsDriveLetter(path[0]); +} + int FilePath::rootLength(const QStringView path) { if (path.size() == 0) @@ -888,6 +895,8 @@ int FilePath::rootLength(const QStringView path) if (startsWithWindowsDriveLetterAndSlash(path)) return 3; // FIXME-ish: same assumption as elsewhere: we assume "x:/" only ever appears as root + if (path.size() == 2 && startsWithWindowsDriveLetter(path)) + return 2; if (path[0] == '/') return 1; @@ -1252,9 +1261,14 @@ void FilePath::setFromString(QStringView fileNameView) if (schemeEnd != -1 && schemeEnd < firstSlash) { // This is a pseudo Url, we can't use QUrl here sadly. const QStringView scheme = fileNameView.left(schemeEnd); - const int hostEnd = fileNameView.indexOf(slash, schemeEnd + 3); + int hostEnd = fileNameView.indexOf(slash, schemeEnd + 3); const QString host = decodeHost( fileNameView.mid(schemeEnd + 3, hostEnd - schemeEnd - 3).toString()); + + QStringView path = fileNameView.mid(hostEnd); + if (!path.isEmpty() && path[0] == '/' && startsWithWindowsDriveLetter(path.mid(1))) + hostEnd++; + setParts(scheme, host, hostEnd != -1 ? fileNameView.mid(hostEnd) : QStringView()); return; } @@ -1553,9 +1567,6 @@ bool FilePath::contains(const QString &s) const bool FilePath::startsWithDriveLetter() const { QStringView p = pathView(); - if (needsDevice() && !p.isEmpty()) - p = p.mid(1); - return p.size() >= 2 && isWindowsDriveLetter(p[0]) && p.at(1) == ':'; } diff --git a/src/libs/utils/layoutbuilder.cpp b/src/libs/utils/layoutbuilder.cpp index 8249fb817cf..99fbf5d9fa6 100644 --- a/src/libs/utils/layoutbuilder.cpp +++ b/src/libs/utils/layoutbuilder.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -785,6 +786,11 @@ void Widget::setContentsMargins(int left, int top, int right, int bottom) access(this)->setContentsMargins(left, top, right, bottom); } +void Widget::setCursor(Qt::CursorShape shape) +{ + access(this)->setCursor(shape); +} + void Widget::activateWindow() { access(this)->activateWindow(); @@ -1149,9 +1155,24 @@ void tight(Layout *layout) layout->setSpacing(0); } +class LineEditImpl : public Utils::FancyLineEdit +{ +public: + using FancyLineEdit::FancyLineEdit; + + void keyPressEvent(QKeyEvent *event) override + { + FancyLineEdit::keyPressEvent(event); + if (acceptReturnKeys && (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return)) + event->accept(); + } + + bool acceptReturnKeys = false; +}; + LineEdit::LineEdit(std::initializer_list ps) { - ptr = new Implementation; + ptr = new LineEditImpl; apply(this, ps); } @@ -1192,6 +1213,7 @@ void LineEdit::setMinimumHeight(int height) void LineEdit::onReturnPressed(const std::function &func, QObject *guard) { + static_cast(access(this))->acceptReturnKeys = true; QObject::connect(access(this), &Utils::FancyLineEdit::returnPressed, guard, func); } diff --git a/src/libs/utils/layoutbuilder.h b/src/libs/utils/layoutbuilder.h index 1220d077cd6..08d71a14618 100644 --- a/src/libs/utils/layoutbuilder.h +++ b/src/libs/utils/layoutbuilder.h @@ -258,6 +258,8 @@ public: void setNoMargins(int = 0); void setNormalMargins(int = 0); void setContentsMargins(int left, int top, int right, int bottom); + void setCursor(Qt::CursorShape shape); + void activateWindow(); void close(); }; diff --git a/src/libs/utils/macroexpander.cpp b/src/libs/utils/macroexpander.cpp index fdaf4c7aebc..082c88f6f4f 100644 --- a/src/libs/utils/macroexpander.cpp +++ b/src/libs/utils/macroexpander.cpp @@ -382,7 +382,13 @@ QString MacroExpander::expand(const QString &stringWithVariables) const FilePath MacroExpander::expand(const FilePath &fileNameWithVariables) const { - // We want single variables to expand to fully qualified strings. + // This is intentionally unsymmetric: We have already sanitized content + // in fileNameWithVariables, so using .toString() is fine. + // The macro expansion may introduce arbitrary new contents, so + // sanitization using fromUserInput() needs to repeated. + // We also cannot just operate on the scheme, host and path component + // individually as we want to allow single variables to expand to fully + // remote-qualified paths. return FilePath::fromUserInput(expand(fileNameWithVariables.toString())); } diff --git a/src/libs/utils/markdownbrowser.cpp b/src/libs/utils/markdownbrowser.cpp index cca2c9c3eed..e15323058cb 100644 --- a/src/libs/utils/markdownbrowser.cpp +++ b/src/libs/utils/markdownbrowser.cpp @@ -394,6 +394,11 @@ QSize MarkdownBrowser::minimumSizeHint() const return boundingRect.size().toSize() + QTextBrowser::minimumSizeHint(); } +void MarkdownBrowser::setMargins(const QMargins &margins) +{ + setViewportMargins(margins); +} + void MarkdownBrowser::setAllowRemoteImages(bool allow) { static_cast(document())->setAllowRemoteImages(allow); diff --git a/src/libs/utils/markdownbrowser.h b/src/libs/utils/markdownbrowser.h index a6417609001..63de9e29f3a 100644 --- a/src/libs/utils/markdownbrowser.h +++ b/src/libs/utils/markdownbrowser.h @@ -25,6 +25,8 @@ public: QSize sizeHint() const override; QSize minimumSizeHint() const override; + void setMargins(const QMargins &margins); + protected: void changeEvent(QEvent *event) override; diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp index 8d80ebb5dfa..07ccde1f263 100644 --- a/src/plugins/android/androidconfigurations.cpp +++ b/src/plugins/android/androidconfigurations.cpp @@ -58,6 +58,22 @@ # include #endif // WITH_TESTS +/* +Suggested NDK and Debugger version per Qt version: + +| Qt5 | Qt6 | NDK | GDB | LLDB | +| ---------------- | --------- | ------------- | ------ | ------ | +| 5.12.0 - 5.13.1 | | 19.2.5345600 | 7.11.0 | | +| 5.13.2 - 5.15.8 | 6.0 - 6.1 | 21.3.6528147 | 8.3.0 | | +| 5.15.9 - 5.15.16 | 6.2 - 6.3 | 22.1.7171670 | 8.3.0 | 11.0.5 | +| | 6.4 | 23.1.7779620 | 8.3.0 | 12.0.8 | +| | 6.5 - 6.6 | 25.1.8937393 | | 14.0.6 | +| | 6.7 - 6.8 | 26.1.10909125 | | 17.0.2 | + +< Qt 6.5: Mapping read from sdk_definitions.json +>= Qt 6.5: Mapping read from /modules/Core.json +*/ + using namespace ProjectExplorer; using namespace QtSupport; using namespace Tasking; diff --git a/src/plugins/autotest/qtest/qttesttreeitem.cpp b/src/plugins/autotest/qtest/qttesttreeitem.cpp index 00f8a0665ef..196b7e89151 100644 --- a/src/plugins/autotest/qtest/qttesttreeitem.cpp +++ b/src/plugins/autotest/qtest/qttesttreeitem.cpp @@ -14,6 +14,7 @@ #include +#include #include using namespace Utils; @@ -303,23 +304,30 @@ ITestConfiguration *QtTestTreeItem::debugConfiguration() const QList QtTestTreeItem::getAllTestConfigurations() const { QList result; + QList> allTargets; ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject(); if (!project || type() != Root) return result; - forFirstLevelChildren([&result](ITestTreeItem *child) { - if (child->type() == TestCase) { - ITestConfiguration *tc = child->testConfiguration(); - QTC_ASSERT(tc, return); - result << tc; - } else if (child->type() == GroupNode) { - child->forFirstLevelChildren([&result](ITestTreeItem *groupChild) { - ITestConfiguration *tc = groupChild->testConfiguration(); - QTC_ASSERT(tc, return); - result << tc; - }); + // avoid executing tests with multiple test classes multiple times + auto appendConfiguration = [&result, &allTargets](ITestTreeItem *item) { + ITestConfiguration *config = item->testConfiguration(); + QTC_ASSERT(config, return); + const QSet targets = static_cast(config)->internalTargets(); + if (allTargets.contains(targets)) { + delete config; + } else { + result << config; + allTargets << targets; } + }; + + forFirstLevelChildren([appendConfiguration](ITestTreeItem *child) { + if (child->type() == TestCase) + appendConfiguration(child); + else if (child->type() == GroupNode) + child->forFirstLevelChildren(appendConfiguration); }); return result; } diff --git a/src/plugins/autotest/testtreeitem.cpp b/src/plugins/autotest/testtreeitem.cpp index 6cf746a89e1..9fcdfb0b533 100644 --- a/src/plugins/autotest/testtreeitem.cpp +++ b/src/plugins/autotest/testtreeitem.cpp @@ -116,8 +116,8 @@ bool ITestTreeItem::lessThan(const ITestTreeItem *other, ITestTreeItem::SortMode return filePath().path().compare(other->filePath().path(), Qt::CaseInsensitive) > 0; } - const Link &leftLink = data(0, LinkRole).value(); - const Link &rightLink = other->data(0, LinkRole).value(); + const Link leftLink{m_filePath, m_line}; + const Link rightLink{other->m_filePath, other->m_line}; const int comparison = leftLink.targetFilePath.path() .compare(rightLink.targetFilePath.path(), Qt::CaseInsensitive); if (comparison == 0) { diff --git a/src/plugins/axivion/axivionperspective.cpp b/src/plugins/axivion/axivionperspective.cpp index a17fa4e1671..e8bf0c84852 100644 --- a/src/plugins/axivion/axivionperspective.cpp +++ b/src/plugins/axivion/axivionperspective.cpp @@ -92,10 +92,6 @@ static std::optional findPathMappingMatch(const QString &projectNam for (const PathMapping &mapping : settings().validPathMappings()) { if (mapping.projectName != projectName) continue; - if (mapping.localPath.isRelativePath()) // TODO mark inside settings - continue; - if (mapping.analysisPath.isAbsolutePath()) // TODO mark inside settings - continue; if (mapping.analysisPath.isEmpty()) return mapping; diff --git a/src/plugins/axivion/axivionsettings.cpp b/src/plugins/axivion/axivionsettings.cpp index dc6214e6633..508f4e47225 100644 --- a/src/plugins/axivion/axivionsettings.cpp +++ b/src/plugins/axivion/axivionsettings.cpp @@ -86,6 +86,31 @@ bool PathMapping::operator!=(const PathMapping &other) const return !(*this == other); } +static bool analysisPathValid(const FilePath &analysisPath, QString *error) +{ + if (analysisPath.isEmpty()) + return true; + if (analysisPath.needsDevice() || analysisPath.isAbsolutePath()) { + if (error) + *error = QString("Path must be relative."); + return false; + } + static const QRegularExpression invalid("^(.*/)?\\.\\.?(/.*)?$"); + if (invalid.match(analysisPath.path()).hasMatch()) { + if (error) + *error = QString("Invalid path elements (. or ..)"); + return false; + } + return true; +} + +bool PathMapping::isValid() const + { + return !projectName.isEmpty() && !localPath.isEmpty() + && !localPath.needsDevice() && localPath.isAbsolutePath() + && analysisPathValid(analysisPath, nullptr); +} + static FilePath axivionJsonFilePath() { return FilePath::fromString(ICore::settings()->fileName()).parentDir() @@ -549,8 +574,22 @@ public: { m_projectName.setLabelText(Tr::tr("Project name:")); m_projectName.setDisplayStyle(StringAspect::LineEditDisplay); + m_projectName.setValidationFunction([](FancyLineEdit *edit, QString *error) { + QTC_ASSERT(edit, return false); + if (!edit->text().isEmpty()) + return true; + if (error) + *error = QString("Project name must be non-empty."); + return false; + }); m_analysisPath.setLabelText(Tr::tr("Analysis path:")); m_analysisPath.setDisplayStyle(StringAspect::LineEditDisplay); + m_analysisPath.setValidationFunction([](FancyLineEdit *edit, QString *error) { + QTC_ASSERT(edit, return false); + // do NOT use fromUserInput() as this also cleans the path + const FilePath fp = FilePath::fromString(edit->text().replace('\\', '/')); + return analysisPathValid(fp, error); + }); m_localPath.setLabelText(Tr::tr("Local path:")); m_localPath.setExpectedKind(PathChooser::ExistingDirectory); m_localPath.setAllowPathFromDevice(false); diff --git a/src/plugins/axivion/axivionsettings.h b/src/plugins/axivion/axivionsettings.h index 6d25e15ca64..aee3154fa71 100644 --- a/src/plugins/axivion/axivionsettings.h +++ b/src/plugins/axivion/axivionsettings.h @@ -43,7 +43,7 @@ public: bool operator==(const PathMapping &other) const; bool operator!=(const PathMapping &other) const; - bool isValid() const {return !projectName.isEmpty() && !localPath.isEmpty(); } + bool isValid() const; QString projectName; Utils::FilePath analysisPath; Utils::FilePath localPath; diff --git a/src/plugins/axivion/issueheaderview.cpp b/src/plugins/axivion/issueheaderview.cpp index 3f23c9b8609..9c2f4b68cd3 100644 --- a/src/plugins/axivion/issueheaderview.cpp +++ b/src/plugins/axivion/issueheaderview.cpp @@ -10,10 +10,12 @@ #include #include +#include #include #include #include #include +#include #include namespace Axivion::Internal { @@ -37,6 +39,22 @@ static QString infoText() "\"\" matches issues having an empty value in this column\n" "!\"\" matches issues having any non-empty value in this column"); } + +static void fixGlobalPosOnScreen(QPoint *globalPos, const QSize &size) +{ + QScreen *qscreen = QGuiApplication::screenAt(*globalPos); + if (!qscreen) + qscreen = QGuiApplication::primaryScreen(); + const QRect screen = qscreen->availableGeometry(); + + if (globalPos->x() + size.width() > screen.width()) + globalPos->setX(screen.width() - size.width()); + if (globalPos->y() + size.height() > screen.height()) + globalPos->setY(screen.height() - size.height()); + if (globalPos->y() < 0) + globalPos->setY(0); +} + class FilterPopupWidget : public QFrame { public: @@ -262,8 +280,10 @@ void IssueHeaderView::mouseReleaseEvent(QMouseEvent *event) popup->setOnApply(onApply); const int right = sectionViewportPosition(logical) + sectionSize(logical); const QSize size = popup->sizeHint(); - popup->move(mapToGlobal(QPoint{x() + right - size.width(), - this->y() - size.height()})); + QPoint globalPos = mapToGlobal(QPoint{std::max(0, x() + right - size.width()), + this->y() - size.height()}); + fixGlobalPosOnScreen(&globalPos, size); + popup->move(globalPos); popup->show(); } } diff --git a/src/plugins/clangformat/clangformatutils.cpp b/src/plugins/clangformat/clangformatutils.cpp index 2bc651000e0..dcc7cbc84c8 100644 --- a/src/plugins/clangformat/clangformatutils.cpp +++ b/src/plugins/clangformat/clangformatutils.cpp @@ -41,7 +41,10 @@ clang::format::FormatStyle calculateQtcStyle() style.Language = FormatStyle::LK_Cpp; style.AccessModifierOffset = -4; style.AlignAfterOpenBracket = FormatStyle::BAS_Align; -#if LLVM_VERSION_MAJOR >= 15 +#if LLVM_VERSION_MAJOR >= 18 + style.AlignConsecutiveAssignments = {false, false, false, false, false, false}; + style.AlignConsecutiveDeclarations = {false, false, false, false, false, false}; +#elif LLVM_VERSION_MAJOR >= 15 style.AlignConsecutiveAssignments = {false, false, false, false, false}; style.AlignConsecutiveDeclarations = {false, false, false, false, false}; #else diff --git a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp index fd6cedc2e5b..44ac1038672 100644 --- a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp +++ b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp @@ -720,7 +720,9 @@ static void addCompileGroups(ProjectNode *targetRoot, targetRoot); if (showSourceFolders) { FilePath baseDir = sourceDirectory.pathAppended(td.sourceGroups[i]); - if (!baseDir.exists()) + const bool caseSensitiveMatch = baseDir.nativePath() + == baseDir.canonicalPath().nativePath(); + if (!baseDir.exists() || !caseSensitiveMatch) baseDir = sourceDirectory; insertNode->addNestedNodes(std::move(current), baseDir); } else { diff --git a/src/plugins/coreplugin/manhattanstyle.cpp b/src/plugins/coreplugin/manhattanstyle.cpp index a4df206fcf0..22969b68525 100644 --- a/src/plugins/coreplugin/manhattanstyle.cpp +++ b/src/plugins/coreplugin/manhattanstyle.cpp @@ -484,9 +484,11 @@ static void drawPrimitiveTweakedForDarkTheme(QStyle::PrimitiveElement element, case QStyle::PE_FrameGroupBox: { QRect groupBoxFrame = option->rect; int topMargin = 0; - if (widget) { + if (auto control = dynamic_cast(widget)) { + const bool emptyTitle = !control->isCheckable() && control->title().isEmpty(); // Before Qt 6.6.3, QStyle::subControlRect() returned wrong QRect for SC_GroupBoxFrame - static const bool validSCRect = QLibraryInfo::version() >= QVersionNumber(6, 6, 3); + const bool validSCRect = QLibraryInfo::version() >= QVersionNumber(6, 6, 3) + && !emptyTitle; // QTCREATORBUG-31960 if (validSCRect) { QStyleOptionGroupBox opt; opt.initFrom(widget); @@ -497,8 +499,7 @@ static void drawPrimitiveTweakedForDarkTheme(QStyle::PrimitiveElement element, } else { // Snippet from pre-6.6.3 FusionStyle::drawPrimitive - BEGIN static const int groupBoxTopMargin = 3; - auto control = dynamic_cast(widget); - if (!control->isCheckable() && control->title().isEmpty()) { + if (emptyTitle) { // Shrinking the topMargin if Not checkable AND title is empty topMargin = groupBoxTopMargin; } else { @@ -630,8 +631,10 @@ void ManhattanStyle::drawPrimitiveForPanelWidget(PrimitiveElement element, if (!enabled) painter->setOpacity(0.75); QBrush baseBrush = option->palette.base(); - if (widget && qobject_cast(widget->parentWidget())) + if (widget && qobject_cast(widget->parentWidget()) + && StyleHelper::isQDSTheme()) { baseBrush = creatorColor(Theme::DScontrolBackgroundDisabled); + } painter->fillRect(backgroundRect, baseBrush); painter->restore(); } else { diff --git a/src/plugins/coreplugin/plugindialog.cpp b/src/plugins/coreplugin/plugindialog.cpp index 3d252e31e67..c51389c0292 100644 --- a/src/plugins/coreplugin/plugindialog.cpp +++ b/src/plugins/coreplugin/plugindialog.cpp @@ -84,7 +84,7 @@ PluginDialog::PluginDialog() connect(m_view, &ExtensionSystem::PluginView::pluginsChanged, this, [this](const QSet &plugins, bool enable) { for (PluginSpec *plugin : plugins) { - if (enable && plugin->isSoftLoadable()) { + if (enable && plugin->isEffectivelySoftloadable()) { m_softLoad.insert(plugin); } else { m_softLoad.remove(plugin); // In case it was added, harmless otherwise. diff --git a/src/plugins/coreplugin/secretaspect.cpp b/src/plugins/coreplugin/secretaspect.cpp index 5c646cc4476..3017f6458bb 100644 --- a/src/plugins/coreplugin/secretaspect.cpp +++ b/src/plugins/coreplugin/secretaspect.cpp @@ -207,7 +207,7 @@ void SecretAspect::addToLayoutImpl(Layouting::Layout &parent) d->wasEdited = true; }); - addLabeledItem(parent, Layouting::Row{edit, warningLabel, showPasswordButton}.emerge()); + addLabeledItem(parent, Layouting::Row{Layouting::noMargin, edit, warningLabel, showPasswordButton}.emerge()); } void SecretAspect::requestValue( diff --git a/src/plugins/debugger/debuggerdialogs.cpp b/src/plugins/debugger/debuggerdialogs.cpp index 59bd4b52145..3409109d157 100644 --- a/src/plugins/debugger/debuggerdialogs.cpp +++ b/src/plugins/debugger/debuggerdialogs.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -402,6 +403,11 @@ void StartApplicationDialog::run(bool attachRemote) } IDevice::ConstPtr dev = RunDeviceKitAspect::device(k); + if (!dev) { + QMessageBox::critical( + &dialog, Tr::tr("Cannot debug"), Tr::tr("Cannot debug application: Kit has no device")); + return; + } ProcessRunData inferior = newParameters.runnable; const QString inputAddress = dialog.d->channelOverrideEdit->text(); if (!inputAddress.isEmpty()) @@ -419,7 +425,7 @@ void StartApplicationDialog::run(bool attachRemote) if (!newParameters.sysRoot.isEmpty()) debugger->setSysRoot(newParameters.sysRoot); - bool isLocal = !dev || (dev->type() == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE); + bool isLocal = dev->type() == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE; if (isLocal) // FIXME: Restriction needed? debugger->setInferiorEnvironment(k->runEnvironment()); diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp index dcb55c868a3..4882325cb1b 100644 --- a/src/plugins/docker/dockerdevice.cpp +++ b/src/plugins/docker/dockerdevice.cpp @@ -279,6 +279,8 @@ public: QString m_container; + std::unique_ptr m_startProcess; + std::optional m_cachedEnviroment; bool m_isShutdown = false; SynchronizedValue> m_fileAccess; @@ -771,13 +773,11 @@ void DockerDevicePrivate::stopCurrentContainer() } } - Process proc; - proc.setCommand({settings().dockerBinaryPath(), {"container", "kill", m_container}}); + if (m_startProcess && m_startProcess->isRunning()) + m_startProcess->kill(); // Kill instead of stop so we don't wait for the process to finish. m_container.clear(); - proc.runBlocking(); - m_cachedEnviroment.reset(); } @@ -972,18 +972,45 @@ expected_str DockerDevicePrivate::createContainer() expected_str DockerDevicePrivate::startContainer() { + using namespace std::chrono_literals; + auto createResult = createContainer(); if (!createResult) return make_unexpected(createResult.error()); - Process startProcess; - startProcess.setCommand({settings().dockerBinaryPath(), {"container", "start", m_container}}); - startProcess.runBlocking(); - if (startProcess.result() != ProcessResult::FinishedWithSuccess) { - return make_unexpected(Tr::tr("Failed starting Docker container. Exit code: %1, output: %2") - .arg(startProcess.exitCode()) - .arg(startProcess.allOutput())); + if (m_startProcess) + m_startProcess->stop(); + + m_startProcess = std::make_unique(); + + m_startProcess->setCommand( + {settings().dockerBinaryPath(), {"container", "start", "-a", "-i", m_container}}); + m_startProcess->setProcessMode(ProcessMode::Writer); + m_startProcess->start(); + if (!m_startProcess->waitForStarted(5s)) { + if (m_startProcess->state() == QProcess::NotRunning) { + return make_unexpected( + Tr::tr("Failed starting Docker container. Exit code: %1, output: %2") + .arg(m_startProcess->exitCode()) + .arg(m_startProcess->allOutput())); + } + // Lets assume it will start soon + qCWarning(dockerDeviceLog) + << "Docker container start process took more than 5 seconds to start."; } + + QDeadlineTimer deadline(5s); + while (!DockerApi::instance()->isContainerRunning(m_container) && !deadline.hasExpired()) { + QThread::msleep(100); + } + + if (deadline.hasExpired() && !DockerApi::instance()->isContainerRunning(m_container)) { + m_startProcess->stop(); + return make_unexpected(Tr::tr("Failed to start container: %1").arg(m_container)); + } + + qCDebug(dockerDeviceLog) << "Started container: " << m_startProcess->commandLine(); + return {}; } diff --git a/src/plugins/extensionmanager/extensionmanagerwidget.cpp b/src/plugins/extensionmanager/extensionmanagerwidget.cpp index 84242e99021..0c367adc5ea 100644 --- a/src/plugins/extensionmanager/extensionmanagerwidget.cpp +++ b/src/plugins/extensionmanager/extensionmanagerwidget.cpp @@ -389,7 +389,6 @@ private: QStackedWidget *m_detailsStack; CollapsingWidget *m_secondaryDescriptionWidget; HeadingWidget *m_headingWidget; - QWidget *m_primaryContent; QWidget *m_secondaryContent; MarkdownBrowser *m_description; QLabel *m_dateUpdatedTitle; @@ -457,17 +456,8 @@ ExtensionManagerWidget::ExtensionManagerWidget() QPalette browserPal = m_description->palette(); browserPal.setColor(QPalette::Base, creatorColor(Theme::Token_Background_Default)); m_description->setPalette(browserPal); - - using namespace Layouting; - auto primary = new QWidget; - const auto spL = spacing(SpacingTokens::VPaddingL); - // clang-format off - Column { - m_description, - noMargin, spacing(SpacingTokens::ExVPaddingGapXl), - }.attachTo(primary); - // clang-format on - m_primaryContent = toScrollableColumn(primary); + const int verticalPadding = SpacingTokens::ExVPaddingGapXl - SpacingTokens::VPaddingM; + m_description->setMargins({verticalPadding, 0, verticalPadding, 0}); m_dateUpdatedTitle = sectionTitle(h6TF, Tr::tr("Last Update")); m_dateUpdated = tfLabel(contentTF, false); @@ -482,7 +472,11 @@ ExtensionManagerWidget::ExtensionManagerWidget() m_pluginStatus = new PluginStatusWidget; auto secondary = new QWidget; + + using namespace Layouting; + const auto spL = spacing(SpacingTokens::VPaddingL); const auto spXxs = spacing(SpacingTokens::VPaddingXxs); + // clang-format off Column { sectionTitle(h6CapitalTF, Tr::tr("Extension details")), Column { @@ -515,7 +509,7 @@ ExtensionManagerWidget::ExtensionManagerWidget() customMargins(SpacingTokens::ExVPaddingGapXl, SpacingTokens::ExVPaddingGapXl, SpacingTokens::ExVPaddingGapXl, SpacingTokens::ExVPaddingGapXl), }, - m_primaryContent, + m_description, }, }, m_secondaryDescriptionWidget, @@ -536,6 +530,7 @@ ExtensionManagerWidget::ExtensionManagerWidget() }, noMargin, spacing(0), }.attachTo(this); + // clang-format on WelcomePageHelpers::setBackgroundColor(this, Theme::Token_Background_Default); @@ -580,6 +575,7 @@ void ExtensionManagerWidget::updateView(const QModelIndex ¤t) { const QString description = current.data(RoleDescriptionLong).toString(); m_description->setMarkdown(description); + m_description->document()->setDocumentMargin(SpacingTokens::VPaddingM); } { diff --git a/src/plugins/git/gitplugin.cpp b/src/plugins/git/gitplugin.cpp index 5978c439b7e..38b3b344710 100644 --- a/src/plugins/git/gitplugin.cpp +++ b/src/plugins/git/gitplugin.cpp @@ -986,17 +986,21 @@ void GitPluginPrivate::logFile() */ QStringList GitPluginPrivate::lineRange(int &firstLine, bool allowSingleLine) const { + auto buildLineRange = [](int firstLine, int lastLine = -1) { + int stop = (lastLine == -1) ? firstLine : lastLine; + return QStringList{"-L " + QString::number(firstLine) + ',' + QString::number(stop)}; + }; + if (BaseTextEditor *textEditor = BaseTextEditor::currentTextEditor()) { QTextCursor cursor = textEditor->textCursor(); if (cursor.hasSelection()) { - QString argument = "-L "; int selectionStart = cursor.selectionStart(); int selectionEnd = cursor.selectionEnd(); cursor.setPosition(selectionStart); const int startBlock = cursor.blockNumber(); cursor.setPosition(selectionEnd); int endBlock = cursor.blockNumber(); - if (startBlock != endBlock) { + if (startBlock != endBlock || allowSingleLine) { firstLine = startBlock + 1; if (cursor.atBlockStart()) --endBlock; @@ -1005,13 +1009,18 @@ QStringList GitPluginPrivate::lineRange(int &firstLine, bool allowSingleLine) co if (previousFirstLine > 0) firstLine = previousFirstLine; } - argument += QString::number(firstLine) + ','; - argument += QString::number(endBlock + firstLine - startBlock); - return {argument}; + return buildLineRange(firstLine, firstLine + endBlock - startBlock); + } else if (startBlock == endBlock) { + QTextCursor lineCursor = textEditor->textCursor(); + lineCursor.movePosition(QTextCursor::StartOfLine); + const bool startsAtLineStart = (lineCursor.position() == selectionStart); + lineCursor.movePosition(QTextCursor::EndOfLine); + const bool endsAtLineEnd = (lineCursor.position() == selectionEnd); + if (startsAtLineStart && endsAtLineEnd) + return buildLineRange(lineCursor.blockNumber() + 1); } } else if (allowSingleLine) { - firstLine = cursor.blockNumber() + 1; - return {"-L " + QString::number(firstLine) + ',' + QString::number(firstLine)}; + return buildLineRange(cursor.blockNumber() + 1); } } return {}; diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp index 0f6a3ce9035..02bb7ca9a1b 100644 --- a/src/plugins/languageclient/client.cpp +++ b/src/plugins/languageclient/client.cpp @@ -1829,6 +1829,7 @@ LanguageClientValue ClientPrivate::showMessageBox( const ShowMessageRequestParams &message) { QMessageBox box; + box.setWindowTitle(q->name()); box.setText(message.toString()); switch (message.type()) { case Error: diff --git a/src/plugins/languageclient/languageclientcompletionassist.cpp b/src/plugins/languageclient/languageclientcompletionassist.cpp index 1b0d46bf37c..7919de0de81 100644 --- a/src/plugins/languageclient/languageclientcompletionassist.cpp +++ b/src/plugins/languageclient/languageclientcompletionassist.cpp @@ -43,7 +43,11 @@ QString LanguageClientCompletionItem::text() const { return m_item.label(); } bool LanguageClientCompletionItem::implicitlyApplies() const -{ return false; } +{ + // only implicitly apply this item if there is no textEdit otherwise the user has to confirm + // the completion + return !m_item.textEdit(); +} bool LanguageClientCompletionItem::prematurelyApplies(const QChar &typedCharacter) const { diff --git a/src/plugins/languageclient/lspinspector.cpp b/src/plugins/languageclient/lspinspector.cpp index f7812217625..634f0d456c7 100644 --- a/src/plugins/languageclient/lspinspector.cpp +++ b/src/plugins/languageclient/lspinspector.cpp @@ -166,6 +166,7 @@ void LspCapabilitiesWidget::setCapabilities(const Capabilities &serverCapabiliti { m_capabilitiesView->setModel( createJsonModel(Tr::tr("Server Capabilities"), QJsonObject(serverCapabilities.capabilities))); + m_dynamicCapabilities = serverCapabilities.dynamicCapabilities; const QStringList &methods = m_dynamicCapabilities.registeredMethods(); if (methods.isEmpty()) { @@ -502,7 +503,7 @@ LspInspectorWidget::LspInspectorWidget(LspInspector *inspector) TabWidget { bindTo(&m_tabWidget), Tab(Tr::tr("Log"), Column { m_logWidget }), - Tab(Tr::tr("Capabilities"), Column {new LspCapabilitiesWidget}), + Tab(Tr::tr("Capabilities"), Column { m_capWidget }), }, buttonBox, }.attachTo(this); diff --git a/src/plugins/languageclient/lualanguageclient/lualanguageclient.cpp b/src/plugins/languageclient/lualanguageclient/lualanguageclient.cpp index 28efc40fb95..ce37bdecda4 100644 --- a/src/plugins/languageclient/lualanguageclient/lualanguageclient.cpp +++ b/src/plugins/languageclient/lualanguageclient/lualanguageclient.cpp @@ -490,6 +490,7 @@ public: m_initializationOptions = options.as(); emit optionsChanged(); + LanguageClientManager::applySettings(); m_isUpdatingAsyncOptions = false; }); @@ -703,7 +704,7 @@ static void registerLuaApi() "sendMessageWithIdForDocument_cb", &LuaClientWrapper::sendMessageWithIdForDocument_cb, "create", - [](const sol::table &options) -> std::shared_ptr { + [](const sol::main_table &options) -> std::shared_ptr { auto luaClientWrapper = std::make_shared(options); auto clientSettings = new LuaClientSettings(luaClientWrapper); diff --git a/src/plugins/lua/bindings/gui.cpp b/src/plugins/lua/bindings/gui.cpp index 605df2bb7ca..36c42326148 100644 --- a/src/plugins/lua/bindings/gui.cpp +++ b/src/plugins/lua/bindings/gui.cpp @@ -111,10 +111,24 @@ CREATE_HAS_FUNC(setTextInteractionFlags, Qt::TextInteractionFlags()) CREATE_HAS_FUNC(setFixedSize, QSize()) CREATE_HAS_FUNC(setVisible, bool()) CREATE_HAS_FUNC(setIcon, Utils::Icon()); +CREATE_HAS_FUNC(setContentsMargins, int(), int(), int(), int()); +CREATE_HAS_FUNC(setCursor, Qt::CursorShape()) template void setProperties(std::unique_ptr &item, const sol::table &children, QObject *guard) { + if constexpr (has_setContentsMargins) { + sol::optional margins = children.get>("contentMargins"); + if (margins) + item->setContentsMargins(margins->left(), margins->top(), margins->right(), margins->bottom()); + } + + if constexpr (has_setCursor) { + const auto cursor = children.get>("cursor"); + if (cursor) + item->setCursor(*cursor); + } + if constexpr (has_setVisible) { const auto visible = children.get>("visible"); if (visible) @@ -243,8 +257,8 @@ void setProperties(std::unique_ptr &item, const sol::table &children, QObject } if constexpr (has_onTextChanged) { - sol::optional onTextChanged - = children.get>("onTextChanged"); + sol::optional onTextChanged + = children.get>("onTextChanged"); if (onTextChanged) { item->onTextChanged( [f = *onTextChanged](const QString &text) { @@ -255,8 +269,8 @@ void setProperties(std::unique_ptr &item, const sol::table &children, QObject } } if constexpr (has_onClicked) { - sol::optional onClicked - = children.get>("onClicked"); + sol::optional onClicked + = children.get>("onClicked"); if (onClicked) { item->onClicked( [f = *onClicked]() { @@ -525,6 +539,10 @@ void setupGuiModule() sol::property(&Widget::isVisible, &Widget::setVisible), "enabled", sol::property(&Widget::isEnabled, &Widget::setEnabled), + "focus", + sol::property([](Widget *self) { return self->emerge()->hasFocus(); }), + "setFocus", + [](Widget *self) { self->emerge()->setFocus(); }, sol::base_classes, sol::bases()); @@ -532,6 +550,7 @@ void setupGuiModule() mirrorEnum(gui, QMetaEnum::fromType()); mirrorEnum(gui, QMetaEnum::fromType()); mirrorEnum(gui, QMetaEnum::fromType()); + mirrorEnum(gui, QMetaEnum::fromType()); gui.new_usertype( "Stack", diff --git a/src/plugins/lua/bindings/qt.cpp b/src/plugins/lua/bindings/qt.cpp index ecd2d96ada8..4e43272d509 100644 --- a/src/plugins/lua/bindings/qt.cpp +++ b/src/plugins/lua/bindings/qt.cpp @@ -36,7 +36,7 @@ void setupQtModule() &QCompleter::completionMode, [](QCompleter *c, QCompleter::CompletionMode mode) { c->setCompletionMode(mode); }), "onActivated", - sol::property([guard = pluginSpec](QCompleter &obj, sol::function callback) { + sol::property([guard = pluginSpec](QCompleter &obj, sol::main_function callback) { QObject::connect( &obj, QOverload::of(&QCompleter::activated), diff --git a/src/plugins/lua/bindings/settings.cpp b/src/plugins/lua/bindings/settings.cpp index 8bd9b2a4ec8..47f34f71da9 100644 --- a/src/plugins/lua/bindings/settings.cpp +++ b/src/plugins/lua/bindings/settings.cpp @@ -294,7 +294,7 @@ sol::usertype addTypedAspect(sol::table &lua, const QString &name) return lua.new_usertype( name, "create", - [](const sol::table &options) { + [](const sol::main_table &options) { return createAspectFromTable(options, &typedAspectCreate); }, sol::base_classes, @@ -355,7 +355,7 @@ void setupSettingsModule() settings.new_usertype( "SecretAspect", "create", - [](const sol::table &options) { + [](const sol::main_table &options) { return createAspectFromTable( options, [](SecretAspect *aspect, const std::string &key, const sol::object &value) { @@ -392,7 +392,7 @@ void setupSettingsModule() settings.new_usertype( "SelectionAspect", "create", - [](const sol::table &options) { + [](const sol::main_table &options) { return createAspectFromTable( options, [](SelectionAspect *aspect, const std::string &key, const sol::object &value) { @@ -473,7 +473,7 @@ void setupSettingsModule() settings.new_usertype( "ToggleAspect", "create", - [](const sol::table &options) { + [](const sol::main_table &options) { return createAspectFromTable( options, [](ToggleAspect *aspect, const std::string &key, const sol::object &value) { @@ -521,7 +521,7 @@ void setupSettingsModule() settings.new_usertype( "TriStateAspect", "create", - [](const sol::table &options) { + [](const sol::main_table &options) { return createAspectFromTable( options, [](TriStateAspect *aspect, const std::string &key, const sol::object &value) { @@ -553,7 +553,7 @@ void setupSettingsModule() settings.new_usertype( "TextDisplay", "create", - [](const sol::table &options) { + [](const sol::main_table &options) { return createAspectFromTable( options, [](TextDisplay *aspect, const std::string &key, const sol::object &value) { @@ -587,7 +587,7 @@ void setupSettingsModule() settings.new_usertype( "AspectList", "create", - [](const sol::table &options) { + [](const sol::main_table &options) { return createAspectFromTable( options, [](AspectList *aspect, const std::string &key, const sol::object &value) { @@ -682,7 +682,7 @@ void setupSettingsModule() settings.new_usertype( "OptionsPage", "create", - [&pool, pluginSpec](const sol::table &options) { + [&pool, pluginSpec](const sol::main_table &options) { return pool.makePage(pluginSpec, options); }, "show", diff --git a/src/plugins/lua/bindings/texteditor.cpp b/src/plugins/lua/bindings/texteditor.cpp index 2ba53d8ca68..2fc4e3b0e88 100644 --- a/src/plugins/lua/bindings/texteditor.cpp +++ b/src/plugins/lua/bindings/texteditor.cpp @@ -4,8 +4,11 @@ #include "../luaengine.h" #include "../luatr.h" +#include "utils.h" + #include #include +#include #include #include #include @@ -47,14 +50,61 @@ TextEditor::TextEditorWidget *getSuggestionReadyEditorWidget(TextEditor::TextDoc } std::unique_ptr addEmbeddedWidget( - BaseTextEditor *editor, QWidget *widget, int cursorPosition) + BaseTextEditor *editor, QWidget *widget, std::variant cursorPosition) { + if (!editor->textDocument() || !editor->textDocument()->document()) + throw sol::error("No text document set"); + widget->setParent(editor->editorWidget()->viewport()); TextEditorWidget *editorWidget = editor->editorWidget(); - std::unique_ptr embed - = editorWidget->insertWidget(widget, cursorPosition); + + int pos = cursorPosition.index() == 0 + ? std::get(cursorPosition) + : std::get(cursorPosition) + .positionInDocument(editor->textDocument()->document()); + + std::unique_ptr embed = editorWidget->insertWidget(widget, pos); return embed; } + +void clearRefactorMarkers(BaseTextEditor *editor, const Utils::Id &id) +{ + TextEditorWidget *editorWidget = editor->editorWidget(); + QTC_ASSERT(editorWidget, throw sol::error("TextEditorWidget is not valid")); + + editorWidget->clearRefactorMarkers(id); +} + +void setRefactorMarker( + BaseTextEditor *editor, + const Utils::Icon &icon, + int position, + const Utils::Id &id, + bool anchorLeft, + sol::main_function callback) +{ + TextEditorWidget *editorWidget = editor->editorWidget(); + QTC_ASSERT(editorWidget, throw sol::error("TextEditorWidget is not valid")); + + TextDocument *textDocument = editor->textDocument(); + QTextCursor cursor = QTextCursor(textDocument->document()); + cursor.setPosition(position); + + // Move cursor to start of line + if (anchorLeft) + cursor.movePosition(QTextCursor::MoveOperation::StartOfBlock); + + TextEditor::RefactorMarker marker; + marker.cursor = cursor; + marker.icon = icon.icon(); + marker.callback = [callback](TextEditorWidget *) { + expected_str res = Lua::void_safe_call(callback); + QTC_CHECK_EXPECTED(res); + }; + marker.type = id; + + editorWidget->setRefactorMarkers({std::move(marker)}, id); +} } // namespace namespace Lua::Internal { @@ -148,6 +198,9 @@ void setupTextEditorModule() TextEditorRegistry::instance(); registerProvider("TextEditor", [](sol::state_view lua) -> sol::object { + const ScriptPluginSpec *pluginSpec = lua.get("PluginSpec"); + QObject *guard = pluginSpec->connectionGuard.get(); + sol::table result = lua.create_table(); result["currentEditor"] = []() -> TextEditorPtr { @@ -168,18 +221,18 @@ void setupTextEditorModule() "Position", sol::no_constructor, "line", - sol::property([](Position &pos) { return pos.line; }), + sol::property(&Position::line, &Position::line), "column", - sol::property([](Position &pos) { return pos.column; })); + sol::property(&Position::column, &Position::column)); // In range can't use begin/end as "end" is a reserved word for LUA scripts result.new_usertype( "Range", sol::no_constructor, "from", - sol::property([](Range &range) { return range.begin; }), + sol::property(&Range::begin, &Range::begin), "to", - sol::property([](Range &range) { return range.end; })); + sol::property(&Range::end, &Range::end)); result.new_usertype( "TextCursor", @@ -246,7 +299,26 @@ void setupTextEditorModule() "resize", &EmbeddedWidgetInterface::resize, "close", - &EmbeddedWidgetInterface::close); + &EmbeddedWidgetInterface::close, + "onShouldClose", + [guard](EmbeddedWidgetInterface *widget, sol::main_function func) { + QObject::connect(widget, &EmbeddedWidgetInterface::shouldClose, guard, [func]() { + expected_str res = void_safe_call(func); + QTC_CHECK_EXPECTED(res); + }); + }); + + std::shared_ptr>> activeMarkers + = std::make_shared>>(); + + QObject::connect(guard, &QObject::destroyed, [activeMarkers] { + for (const auto &[k, v] : activeMarkers->asKeyValueRange()) { + if (k) { + for (const auto &id : v) + k->editorWidget()->clearRefactorMarkers(id); + } + } + }); result.new_usertype( "TextEditor", @@ -257,10 +329,39 @@ void setupTextEditorModule() return textEditor->textDocument(); }, "addEmbeddedWidget", - [](const TextEditorPtr &textEditor, LayoutOrWidget widget, int position) { + [](const TextEditorPtr &textEditor, + LayoutOrWidget widget, + std::variant position) { QTC_ASSERT(textEditor, throw sol::error("TextEditor is not valid")); return addEmbeddedWidget(textEditor, toWidget(widget), position); }, + "setRefactorMarker", + [pluginSpec, activeMarkers]( + const TextEditorPtr &textEditor, + const IconFilePathOrString &icon, + int position, + const QString &id, + bool anchorLeft, + sol::main_function callback) { + QTC_ASSERT(textEditor, throw sol::error("TextEditor is not valid")); + QTC_ASSERT(!id.isEmpty(), throw sol::error("Id is empty")); + QTC_ASSERT(!icon.valueless_by_exception(), throw sol::error("Icon is invalid")); + + Id finalId = Utils::Id::fromString(QString(pluginSpec->id + "." + id)); + (*activeMarkers)[textEditor].insert(finalId); + + setRefactorMarker(textEditor, *toIcon(icon), position, finalId, anchorLeft, callback); + }, + "clearRefactorMarkers", + [pluginSpec, activeMarkers](const TextEditorPtr &textEditor, const QString &id) { + QTC_ASSERT(textEditor, throw sol::error("TextEditor is not valid")); + QTC_ASSERT(!id.isEmpty(), throw sol::error("Id is empty")); + + Id finalId = Utils::Id::fromString(QString(pluginSpec->id + "." + id)); + (*activeMarkers)[textEditor].remove(finalId); + + clearRefactorMarkers(textEditor, finalId); + }, "cursor", [](const TextEditorPtr &textEditor) { QTC_ASSERT(textEditor, throw sol::error("TextEditor is not valid")); diff --git a/src/plugins/lua/bindings/utils.cpp b/src/plugins/lua/bindings/utils.cpp index 0937950dcc0..ca56340e9ba 100644 --- a/src/plugins/lua/bindings/utils.cpp +++ b/src/plugins/lua/bindings/utils.cpp @@ -251,7 +251,7 @@ void setupUtilsModule() utils.new_usertype( "Timer", "create", - [guard = pluginSpec](int timeout, bool singleShort, sol::function callback) + [guard = pluginSpec](int timeout, bool singleShort, sol::main_function callback) -> std::unique_ptr { auto timer = std::make_unique(); timer->setInterval(timeout); diff --git a/src/plugins/lua/meta/action.lua b/src/plugins/lua/meta/action.lua index bc3cb4e6a80..e6454bd4320 100644 --- a/src/plugins/lua/meta/action.lua +++ b/src/plugins/lua/meta/action.lua @@ -1,7 +1,9 @@ ---@meta Action - local action = {} +---@module 'Utils' +local Utils + ---@enum CommandAttributes action.CommandAttribute = { ---Hide the command from the menu. @@ -17,7 +19,7 @@ action.CommandAttribute = { ---@class ActionOptions ---@field context? string The context in which the action is available. ---@field text? string The text to display for the action. ----@field icon? FilePath|string The icon to display for the action. +---@field icon? Utils.Icon|FilePath|string The icon to display for the action. ---@field iconText? string The icon text to display for the action. ---@field toolTip? string The toolTip to display for the action. ---@field onTrigger? function The callback to call when the action is triggered. diff --git a/src/plugins/lua/meta/gui.lua b/src/plugins/lua/meta/gui.lua index 7f6952c6606..4d122dbb05f 100644 --- a/src/plugins/lua/meta/gui.lua +++ b/src/plugins/lua/meta/gui.lua @@ -38,6 +38,8 @@ gui.baseWidgetOptions = {} ---@field flat? boolean A boolean, representing whether the widget should be flat, if applicable. ---@field [1]? Layout The layout of the widget, if applicable. ---@field fixedSize? integer[] Two integers representing the width and height +---@field contentMargins? integer[] Four integers represending left, top, right and bottom margins. +---@field cursor? CursorShape The cursor shape for the widget. gui.widgetOptions = {} ---@param options WidgetOptions @@ -430,6 +432,37 @@ gui.WidgetAttribute = { WA_ContentsMarginsRespectsSafeArea = 0, WA_StyleSheetTarget = 0 } + +--- Enum representing cursor shape for the widget +---@enum CursorShape +gui.CursorShape = { + ArrowCursor = 0 = 0, + UpArrowCursor = 0, + CrossCursor = 0, + WaitCursor = 0, + IBeamCursor = 0, + SizeVerCursor = 0, + SizeHorCursor = 0, + SizeBDiagCursor = 0, + SizeFDiagCursor = 0, + SizeAllCursor = 0, + BlankCursor = 0, + SplitVCursor = 0, + SplitHCursor = 0, + PointingHandCursor = 0, + ForbiddenCursor = 0, + WhatsThisCursor = 0, + BusyCursor = 0, + OpenHandCursor = 0, + ClosedHandCursor = 0, + DragCopyCursor = 0, + DragMoveCursor = 0, + DragLinkCursor = 0, + LastCursor = DragLinkCursor, + BitmapCursor = 0, + CustomCursor = 0 +} + ---@class Space : Layout gui.space = {} diff --git a/src/plugins/lua/meta/texteditor.lua b/src/plugins/lua/meta/texteditor.lua index 2123b0a1fca..e77e3d5eed5 100644 --- a/src/plugins/lua/meta/texteditor.lua +++ b/src/plugins/lua/meta/texteditor.lua @@ -1,6 +1,10 @@ ---@meta TextEditor local textEditor = {} +---@module 'Utils' +local Utils + + ---@class Position ---@field line integer The line number. ---@field column integer The column number. @@ -116,12 +120,28 @@ function EmbeddedWidget:close() end ---Resizes the floating widget according to its layout. function EmbeddedWidget:resize() end +---Set the callback to be called when the widget should close. (E.g. if the user presses the escape key) +---@param fn function The function to be called when the embed should close. +function EmbeddedWidget:onShouldClose(fn) end + ---Embeds a widget at the specified cursor position in the text editor. ---@param widget Widget|Layout The widget to be added as a floating widget. ----@param position integer The position in the document where the widget should appear. +---@param position integer|Position The position in the document where the widget should appear. ---@return EmbeddedWidget interface An interface to control the floating widget. function TextEditor:addEmbeddedWidget(widget, position) end +---Adds an refactor marker in the text editor at given cursor position. +---@param icon Utils.Icon|FilePath|string Icon to be used. If specified icon is invalid the default QtCreator for markers is used. +---@param position integer The position in the document where the marker should appear. +---@param id string The identifier of the marker. +---@param anchorLeft boolean Specifies if the marker should appear at the beginning of the TextCursor block. +---@param callback function A function to be called once the marker is pressed. +function TextEditor:setRefactorMarker(icon, position, id, anchorLeft, callback) end + +---Removes the refactor markers with given id. +---param id string The identifier of the marker. +function TextEditor:clearRefactorMarkers(icon, position, id, anchorLeft, callback) end + ---Checks if the current suggestion is locked. The suggestion is locked when the user can use it. ---@return boolean True if the suggestion is locked, false otherwise. function TextEditor:hasLockedSuggestion() end diff --git a/src/plugins/mesonprojectmanager/MesonProjectManager.json.in b/src/plugins/mesonprojectmanager/MesonProjectManager.json.in index 45c0ae2d244..6aa737741c7 100644 --- a/src/plugins/mesonprojectmanager/MesonProjectManager.json.in +++ b/src/plugins/mesonprojectmanager/MesonProjectManager.json.in @@ -26,5 +26,18 @@ ], "Url" : "https://www.mesonbuild.com", "DocumentationUrl" : "https://doc.qt.io/qtcreator/creator-project-meson.html", - ${IDE_PLUGIN_DEPENDENCIES} + ${IDE_PLUGIN_DEPENDENCIES}, + + "Mimetypes" : [ + "", + "", + " ", + " ", + " Meson build description", + " ", + " ", + " ", + " ", + "" + ] } diff --git a/src/plugins/projectexplorer/kitaspect.cpp b/src/plugins/projectexplorer/kitaspect.cpp index 52b8a9028d2..b14ef91630f 100644 --- a/src/plugins/projectexplorer/kitaspect.cpp +++ b/src/plugins/projectexplorer/kitaspect.cpp @@ -198,8 +198,6 @@ void KitAspect::addToInnerLayout(Layouting::Layout &parentItem) void KitAspect::addListAspectSpec(const ListAspectSpec &listAspectSpec) { const auto comboBox = createSubWidget(); - comboBox->setSizePolicy(QSizePolicy::Preferred, comboBox->sizePolicy().verticalPolicy()); - comboBox->setEnabled(true); const auto sortModel = new KitAspectSortModel(this); sortModel->setSourceModel(listAspectSpec.model); comboBox->setModel(sortModel); diff --git a/src/plugins/projectexplorer/miniprojecttargetselector.cpp b/src/plugins/projectexplorer/miniprojecttargetselector.cpp index 38ecee2e9ff..0d9b9e4abe9 100644 --- a/src/plugins/projectexplorer/miniprojecttargetselector.cpp +++ b/src/plugins/projectexplorer/miniprojecttargetselector.cpp @@ -865,7 +865,10 @@ void MiniProjectTargetSelector::doLayout() m_kitAreaWidget->move(0, 0); - int kitAreaHeight = m_kitAreaWidget->isVisibleTo(this) ? m_kitAreaWidget->sizeHint().height() : 0; + const int kitAreaHeight = m_kitAreaWidget->isVisibleTo(this) + ? m_kitAreaWidget->sizeHint().height() : 0; + const int kitAreaWidth = m_kitAreaWidget->isVisibleTo(this) + ? m_kitAreaWidget->sizeHint().width() : 0; // 1. Calculate the summary label height int summaryLabelY = 1 + kitAreaHeight; @@ -900,6 +903,7 @@ void MiniProjectTargetSelector::doLayout() QRect newGeometry; + const int minWidth = std::max({m_summaryLabel->sizeHint().width(), kitAreaWidth, 250}); if (!onlySummary) { // list widget height int maxItemCount = m_projectListWidget->maxCount(); @@ -920,8 +924,6 @@ void MiniProjectTargetSelector::doLayout() int listHeight = heightWithoutKitArea + kitAreaHeight - bottomMargin - listY + 1; // list widget widths - int minWidth = qMax(m_summaryLabel->sizeHint().width(), 250); - minWidth = qMax(minWidth, m_kitAreaWidget->sizeHint().width()); QVector widths = listWidgetWidths(minWidth, Core::ICore::mainWindow()->width() * 0.9); const int runColumnWidth = widths[RUN] == -1 ? 0 : RunColumnWidth; @@ -952,7 +954,7 @@ void MiniProjectTargetSelector::doLayout() heightWithoutKitArea = qMax(summaryLabelHeight + bottomMargin, alignedWithActionHeight); m_summaryLabel->resize(m_summaryLabel->sizeHint().width(), heightWithoutKitArea - bottomMargin); m_kitAreaWidget->resize(m_kitAreaWidget->sizeHint()); - newGeometry.setSize({m_summaryLabel->width() + 1, heightWithoutKitArea + kitAreaHeight}); + newGeometry.setSize({minWidth + 1, heightWithoutKitArea + kitAreaHeight}); } newGeometry.translate(statusBar->mapToGlobal(QPoint{0, 0})); diff --git a/src/plugins/projectexplorer/toolchainoptionspage.cpp b/src/plugins/projectexplorer/toolchainoptionspage.cpp index cc24e487ba3..8fe63773ecc 100644 --- a/src/plugins/projectexplorer/toolchainoptionspage.cpp +++ b/src/plugins/projectexplorer/toolchainoptionspage.cpp @@ -578,6 +578,7 @@ void ToolChainOptionsWidget::apply() removedTcs << Utils::transform(notRegistered, &Toolchain::displayName); } for (ExtendedToolchainTreeItem * const item : std::as_const(m_toAddList)) { + m_model.takeItem(item); item->bundle->deleteToolchains(); delete item; } diff --git a/src/plugins/qtsupport/baseqtversion.cpp b/src/plugins/qtsupport/baseqtversion.cpp index 3295121ba2d..82598fdc265 100644 --- a/src/plugins/qtsupport/baseqtversion.cpp +++ b/src/plugins/qtsupport/baseqtversion.cpp @@ -1373,17 +1373,23 @@ FilePath QtVersion::examplesPath() const // QT_INSTALL_EXAMPLES return d->data().examplesPath; } +/*! + \internal + Returns a list of directories containing Qt related shared objects +*/ FilePaths QtVersion::qtSoPaths() const { FilePaths paths; - const FilePaths qtPaths = {libraryPath(), pluginPath(), qmlPath(), importsPath()}; + const FilePath qtPaths[] = {libraryPath(), pluginPath(), qmlPath(), importsPath()}; for (const FilePath &qtPath : qtPaths) { if (qtPath.isEmpty()) continue; + // FIXME: Could be sped up, we need just the info whether there is one such entry const FilePaths soPaths = qtPath.dirEntries({{"*.so"}, QDir::Files, QDirIterator::Subdirectories}); - paths.append(soPaths); + for (const FilePath &soPath : soPaths) + paths.append(soPath.parentDir()); } FilePath::removeDuplicates(paths); return paths; diff --git a/src/plugins/qtsupport/images/icons/tutorialicon.png b/src/plugins/qtsupport/images/icons/tutorialicon.png index 6bd111c1324..a6f3dc2f638 100644 Binary files a/src/plugins/qtsupport/images/icons/tutorialicon.png and b/src/plugins/qtsupport/images/icons/tutorialicon.png differ diff --git a/src/plugins/qtsupport/images/icons/tutorialicon@2x.png b/src/plugins/qtsupport/images/icons/tutorialicon@2x.png index 23d8af8c1ca..a04a8015fbf 100644 Binary files a/src/plugins/qtsupport/images/icons/tutorialicon@2x.png and b/src/plugins/qtsupport/images/icons/tutorialicon@2x.png differ diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index 81f0bbcf084..1ae3b953b73 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -750,6 +750,7 @@ public: qreal charWidth() const; std::unique_ptr insertWidget(QWidget *widget, int line); + void forceUpdateScrollbarSize(); // actions void registerActions(); @@ -947,6 +948,7 @@ public: void updateSuggestion(); void clearCurrentSuggestion(); QTextBlock m_suggestionBlock; + int m_numEmbeddedWidgets = 0; Context m_editorContext; QAction *m_undoAction = nullptr; @@ -1839,6 +1841,7 @@ void TextEditorWidgetPrivate::insertSuggestion(std::unique_ptr & auto options = suggestion->replacementDocument()->defaultTextOption(); m_suggestionBlock = cursor.block(); m_document->insertSuggestion(std::move(suggestion)); + forceUpdateScrollbarSize(); } void TextEditorWidgetPrivate::updateSuggestion() @@ -3500,7 +3503,10 @@ bool TextEditorWidget::event(QEvent *e) if (ke->key() == Qt::Key_Escape && (d->m_snippetOverlay->isVisible() || multiTextCursor().hasMultipleCursors() - || d->m_suggestionBlock.isValid())) { + || d->m_suggestionBlock.isValid() + || d->m_numEmbeddedWidgets > 0)) { + if (d->m_numEmbeddedWidgets > 0) + emit embeddedWidgetsShouldClose(); e->accept(); } else { // hack copied from QInputControl::isCommonTextEditShortcut @@ -3920,10 +3926,8 @@ qreal TextEditorWidgetPrivate::charWidth() const { return QFontMetricsF(q->font()).horizontalAdvance(QLatin1Char('x')); } - class CarrierWidget : public QWidget { - Q_OBJECT public: CarrierWidget(TextEditorWidget *textEditorWidget, QWidget *embed) : QWidget(textEditorWidget->viewport()) @@ -3942,7 +3946,7 @@ public: }); } - int embedHeight() { return m_embed->minimumSizeHint().height(); } + int embedHeight() { return m_embed->sizeHint().height(); } private: QWidget *m_embed; @@ -3964,12 +3968,27 @@ void EmbeddedWidgetInterface::close() emit closed(); } +void TextEditorWidgetPrivate::forceUpdateScrollbarSize() +{ + // We use resizeEvent here as a workaround as we can't get access to the + // scrollarea which is a private part of the QPlainTextEdit. + // During the resizeEvent the plain text edit will resize its scrollbars. + // The TextEditorWidget will also update its scrollbar overlays. + q->resizeEvent(new QResizeEvent(q->size(), q->size())); +} + std::unique_ptr TextEditorWidgetPrivate::insertWidget( QWidget *widget, int line) { QPointer carrier = new CarrierWidget(q, widget); std::unique_ptr result(new EmbeddedWidgetInterface()); + connect( + q, + &TextEditorWidget::embeddedWidgetsShouldClose, + result.get(), + &EmbeddedWidgetInterface::shouldClose); + struct State { int height = 0; @@ -4032,9 +4051,14 @@ std::unique_ptr TextEditorWidgetPrivate::insertWidget( QTextBlock block = pState->cursor.block(); auto userData = TextDocumentLayout::userData(block); userData->removeEmbeddedWidget(carrier); + m_numEmbeddedWidgets--; + forceUpdateScrollbarSize(); }); connect(q->document()->documentLayout(), &QAbstractTextDocumentLayout::update, carrier, position); - connect(result.get(), &EmbeddedWidgetInterface::resized, carrier, position); + connect(result.get(), &EmbeddedWidgetInterface::resized, carrier, [position, this]() { + position(); + forceUpdateScrollbarSize(); + }); connect(result.get(), &EmbeddedWidgetInterface::closed, this, [this, carrier] { if (carrier) carrier->deleteLater(); @@ -4042,7 +4066,11 @@ std::unique_ptr TextEditorWidgetPrivate::insertWidget( QTimer::singleShot(0, layout, [layout] { layout->update(); }); }); + m_numEmbeddedWidgets++; + carrier->show(); + + forceUpdateScrollbarSize(); return result; } @@ -7078,6 +7106,15 @@ void TextEditorWidget::mouseReleaseEvent(QMouseEvent *e) if (!HostOsInfo::isLinuxHost() && handleForwardBackwardMouseButtons(e)) return; + // If the refactor marker was pressed then don't propagate release event to editor + RefactorMarker refactorMarker = d->m_refactorOverlay->markerAt(e->pos()); + if (refactorMarker.isValid()) { + if (refactorMarker.callback) { + e->accept(); + return; + } + } + QPlainTextEdit::mouseReleaseEvent(e); d->setClipboardSelection(); diff --git a/src/plugins/texteditor/texteditor.h b/src/plugins/texteditor/texteditor.h index d98598e4661..da2a4eb6dca 100644 --- a/src/plugins/texteditor/texteditor.h +++ b/src/plugins/texteditor/texteditor.h @@ -113,6 +113,7 @@ public: signals: void resized(); void closed(); + void shouldClose(); }; class TEXTEDITOR_EXPORT BaseTextEditor : public Core::IEditor @@ -561,6 +562,7 @@ signals: void addCurrentStateToNavigationHistory(); void resized(); + void embeddedWidgetsShouldClose(); protected: QTextBlock blockForVisibleRow(int row) const; diff --git a/src/share/3rdparty/package-manager/auto-setup.cmake b/src/share/3rdparty/package-manager/auto-setup.cmake index efaaa9917aa..6f6aa159d88 100644 --- a/src/share/3rdparty/package-manager/auto-setup.cmake +++ b/src/share/3rdparty/package-manager/auto-setup.cmake @@ -207,7 +207,7 @@ macro(qtc_auto_setup_vcpkg) option(QT_CREATOR_SKIP_VCPKG_SETUP "Skip Qt Creator's vcpkg package manager auto-setup" OFF) find_program(vcpkg_program vcpkg - PATHS $ENV{VCPKG_ROOT} ${CMAKE_SOURCE_DIR}/vcpkg + PATHS $ENV{VCPKG_ROOT} ${CMAKE_SOURCE_DIR}/vcpkg ${CMAKE_SOURCE_DIR}/3rdparty/vcpkg NO_DEFAULT_PATH ) if (NOT vcpkg_program) @@ -247,7 +247,23 @@ macro(qtc_auto_setup_vcpkg) if (VCPKG_TARGET_TRIPLET) set(vcpkg_triplet ${VCPKG_TARGET_TRIPLET}) else() - if (WIN32) + if (ANDROID_ABI) + if (ANDROID_ABI STREQUAL "armeabi-v7a") + set(vcpkg_triplet arm-neon-android) + elseif (ANDROID_ABI STREQUAL "arm64-v8a") + set(vcpkg_triplet arm64-android) + elseif (ANDROID_ABI STREQUAL "x86") + set(vcpkg_triplet x86-android) + elseif (ANDROID_ABI STREQUAL "x86_64") + set(vcpkg_triplet x64-android) + else() + message(FATAL_ERROR "Unsupported Android ABI: ${ANDROID_ABI}") + endif() + # Needed by vcpkg/scripts/toolchains/android.cmake + file(APPEND "${CMAKE_BINARY_DIR}/vcpkg-dependencies/toolchain.cmake" " + set(ENV{ANDROID_NDK_HOME} \"${ANDROID_NDK}\") + ") + elseif (WIN32) set(vcpkg_triplet x64-mingw-static) if (CMAKE_CXX_COMPILER MATCHES ".*/(.*)/cl.exe") set(vcpkg_triplet ${CMAKE_MATCH_1}-windows) diff --git a/src/share/3rdparty/package-manager/conan.cmake b/src/share/3rdparty/package-manager/conan.cmake index 4f5f67e74ca..7c78f74154e 100644 --- a/src/share/3rdparty/package-manager/conan.cmake +++ b/src/share/3rdparty/package-manager/conan.cmake @@ -55,7 +55,7 @@ function(_get_msvc_ide_version result) set(${result} 15 PARENT_SCOPE) elseif(NOT MSVC_VERSION VERSION_LESS 1920 AND MSVC_VERSION VERSION_LESS 1930) set(${result} 16 PARENT_SCOPE) - elseif(NOT MSVC_VERSION VERSION_LESS 1930 AND MSVC_VERSION VERSION_LESS 1940) + elseif(NOT MSVC_VERSION VERSION_LESS 1930 AND MSVC_VERSION VERSION_LESS 1950) set(${result} 17 PARENT_SCOPE) else() message(FATAL_ERROR "Conan: Unknown MSVC compiler version [${MSVC_VERSION}]") diff --git a/src/shared/CMakeLists.txt b/src/shared/CMakeLists.txt index b945e8faa92..3e36e6880aa 100644 --- a/src/shared/CMakeLists.txt +++ b/src/shared/CMakeLists.txt @@ -9,6 +9,7 @@ qtc_add_public_header(qtcreator_pch.h) qtc_add_public_header(qtcreator_gui_pch.h) option(BUILD_QBS "Build Qbs together with Qt Creator" OFF) +option(WITH_QBS_DOCS "Build Qbs documentation" ${WITH_DOCS}) add_feature_info("Build Qbs" BUILD_QBS "") if (BUILD_QBS) @@ -30,6 +31,6 @@ if (BUILD_QBS) set(INSTALL_PUBLIC_HEADERS OFF CACHE BOOL "") set(WITH_TESTS OFF) set(WITH_PROJECT_FILE_UPDATES ON CACHE BOOL "") - set(QBS_INSTALL_QCH_DOCS ${WITH_DOCS} CACHE BOOL "") + set(QBS_INSTALL_QCH_DOCS ${WITH_QBS_DOCS} CACHE BOOL "") add_subdirectory(qbs) endif() diff --git a/src/shared/qtsingleapplication/qtsingleapplication.cpp b/src/shared/qtsingleapplication/qtsingleapplication.cpp index 6bfdc0edfa4..05674ed05c1 100644 --- a/src/shared/qtsingleapplication/qtsingleapplication.cpp +++ b/src/shared/qtsingleapplication/qtsingleapplication.cpp @@ -198,9 +198,10 @@ public: const auto end = system_clock::now(); const auto freeze = duration_cast(end - start); if (freeze > m_threshold) { + m_total += freeze; const QString time = QTime::currentTime().toString(Qt::ISODateWithMs); - qDebug().noquote() << QString("FREEZE [%1]").arg(time) - << "of" << freeze.count() << "ms, on:" << event; + qDebug().noquote() << QString("FREEZE [%1]").arg(time) << "of" << freeze.count() + << "ms, total" << m_total.count() << "ms, on:" << event; const QString receiverMessage = name.isEmpty() ? QString("receiver class: %1").arg(className) : QString("receiver class: %1, object name: %2").arg(className, name); @@ -215,6 +216,7 @@ private: bool m_inNotify = false; const QString m_align; milliseconds m_threshold{100}; + milliseconds m_total{0}; }; QtSingleApplication *createApplication(const QString &id, int &argc, char **argv) diff --git a/src/tools/icons/qtcreatoricons.svg b/src/tools/icons/qtcreatoricons.svg index 7714c390550..b98508da4d3 100644 --- a/src/tools/icons/qtcreatoricons.svg +++ b/src/tools/icons/qtcreatoricons.svg @@ -460,27 +460,6 @@ effect="fill_between_many" linkedpaths="#path2259-2-0-6,0" id="path-effect2469-8" /> - - - - - - - + + + + + + + + + + tasks { 1000000ms, 0ms }; + const QList tasks { s_endlessTime, 0ms }; const LoopList iterator(tasks); const auto onSetup = [storage, iterator](TaskObject &taskObject) { @@ -3790,25 +3781,28 @@ void tst_Tasking::testTree_data() { // withCancel / withAccept - const Storage> tickStorage; + const Storage> tickStorage; const auto onSetup = [tickStorage](milliseconds timeout) { - return [tickStorage, timeout] { tickStorage->reset(new Tick(timeout)); }; + return [tickStorage, timeout] { + tickStorage->reset(new QTimer); + tickStorage->get()->start(timeout); + }; }; const auto onTickSetup = [tickStorage] { - return std::make_pair(tickStorage->get(), &Tick::tick); + return std::make_pair(tickStorage->get(), &QTimer::timeout); }; const Group cancelRecipe { storage, tickStorage, - onGroupSetup(onSetup(0ms)), + onGroupSetup(onSetup(0ms)), // 1st queued call Group { groupSetup(1), - createSuccessTask(2, 1ms), + createSuccessTask(2, s_endlessTime), groupDone(1) - }.withCancel(onTickSetup) + }.withCancel(onTickSetup) // 2nd queued call }; const Log cancelLog { diff --git a/tests/auto/utils/filepath/tst_filepath.cpp b/tests/auto/utils/filepath/tst_filepath.cpp index 8f991be291c..917f3c2246a 100644 --- a/tests/auto/utils/filepath/tst_filepath.cpp +++ b/tests/auto/utils/filepath/tst_filepath.cpp @@ -126,6 +126,9 @@ private slots: void dontBreakPathOnWierdWindowsPaths(); + void isRelativePath(); + void isRelativePath_data(); + private: QTemporaryDir tempDir; QString rootPath; @@ -506,6 +509,10 @@ void tst_filepath::rootLength_data() QTest::newRow("unc-localhost-drive") << "//localhost/c$" << 12; QTest::newRow("unc-localhost-drive-slash") << "//localhost//c$/" << 12; QTest::newRow("unc-localhost-drive-slash-rest") << "//localhost//c$/x" << 12; + + QTest::newRow("windows-1") << "C:" << 2; + QTest::newRow("windows-2") << "C:/" << 3; + QTest::newRow("windows-3") << "C:/foor" << 3; } void tst_filepath::rootLength() @@ -1323,7 +1330,7 @@ void tst_filepath::startsWithDriveLetter_data() QTest::newRow("remote-slash") << FilePath::fromString("docker://1234/") << false; QTest::newRow("remote-single-letter") << FilePath::fromString("docker://1234/c") << false; QTest::newRow("remote-drive") << FilePath::fromString("docker://1234/c:") << true; - QTest::newRow("remote-invalid-drive") << FilePath::fromString("docker://1234/c:a") << true; + QTest::newRow("remote-invalid-drive") << FilePath::fromString("docker://1234/c:a") << false; QTest::newRow("remote-with-path") << FilePath::fromString("docker://1234/c:/a") << true; QTest::newRow("remote-z") << FilePath::fromString("docker://1234/z:") << true; QTest::newRow("remote-1") << FilePath::fromString("docker://1234/1:") << false; @@ -1810,15 +1817,66 @@ void tst_filepath::makeTemporaryFile() } } +void tst_filepath::isRelativePath_data() +{ + QTest::addColumn("path"); + QTest::addColumn("expected"); + + QTest::newRow("empty") << "" << true; + QTest::newRow("root") << "/" << false; + QTest::newRow("relative") << "foo" << true; + QTest::newRow("relative-path") << "foo/bar" << true; + QTest::newRow("absolute") << "/foo" << false; + QTest::newRow("absolute-path") << "/foo/bar" << false; + QTest::newRow("remote") << "device://host/foo" << false; + QTest::newRow("remote-path") << "device://host/foo/bar" << false; + + QTest::newRow("windows-current-dir") << "c:" << true; + QTest::newRow("windows-path") << "c:/" << false; + QTest::newRow("windows-path-with-dir") << "c:/foo" << false; + + QTest::newRow("windows-remote-current-dir") << "device://host/C:" << true; + QTest::newRow("windows-remote-path") << "device://host/C:/" << false; + QTest::newRow("windows-remote-path-with-dir") << "device://host/C:/foo" << false; +} + +void tst_filepath::isRelativePath() +{ + QFETCH(QString, path); + QFETCH(bool, expected); + + QCOMPARE(FilePath::fromUserInput(path).isRelativePath(), expected); +} + void tst_filepath::dontBreakPathOnWierdWindowsPaths() { FilePath path = FilePath::fromString( - "device://host/./C:/Users/ckandeler/Documents/" + "device://host/./C:/Users/johndoe/Documents/" "build-iartest-IAR-Debugx/Debug_IAR_55df6f02d5b3d06d/iartest.a152245e/iartest.out"); QCOMPARE( path.toString(), - "device://host/C:/Users/ckandeler/Documents/" + "device://host/C:/Users/johndoe/Documents/" "build-iartest-IAR-Debugx/Debug_IAR_55df6f02d5b3d06d/iartest.a152245e/iartest.out"); + + FilePath pathWithBackslash = FilePath::fromUserInput( + "device://host/C:\\Users\\johndoe\\Documents\\test.elf"); + QCOMPARE(pathWithBackslash.toString(), "device://host/C:/Users/johndoe/Documents/test.elf"); + QCOMPARE(pathWithBackslash.path(), "C:/Users/johndoe/Documents/test.elf"); + + const FilePath bin = FilePath::fromString(pathWithBackslash.path()); + QCOMPARE(bin.toString(), "C:/Users/johndoe/Documents/test.elf"); + + // Make sure the optimization still works + FilePath path2 = FilePath::fromString("/./"); + QCOMPARE(path2.toString(), ""); + + // Make sure unix paths are not affected + FilePath path3 = FilePath::fromString("/./foo/bar"); + QCOMPARE(path3.toString(), "foo/bar"); + + // Make sure unix paths with device also work + FilePath path4 = FilePath::fromString("device://host/./foo/bar"); + QCOMPARE(path4.toString(), "device://host/./foo/bar"); } } // Utils diff --git a/tests/manual/docker/linux/DockerFile-with-creator b/tests/manual/docker/linux/DockerFile-with-creator new file mode 100644 index 00000000000..8510286fe7b --- /dev/null +++ b/tests/manual/docker/linux/DockerFile-with-creator @@ -0,0 +1,31 @@ +FROM ubuntu:22.04 + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && \ + apt-get install -y \ + libgl1-mesa-dev \ + qt6-base-dev \ + qt6-base-private-dev \ + qt6-declarative-dev \ + qt6-declarative-private-dev \ + libqt6core5compat6-dev \ + build-essential \ + clang-format clang-tidy clang-tools clang clangd libc++-dev libc++1 libc++abi-dev libc++abi1 libclang-dev libclang1 liblldb-dev libllvm-ocaml-dev libomp-dev libomp5 lld lldb llvm-dev llvm-runtime llvm python3-clang python3-lldb \ + cmake \ + nano \ + ninja-build \ + ca-certificates \ + curl \ + gnupg \ + lsb-release \ + qtcreator \ + gdb \ + && curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg \ + && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null \ + && apt-get update \ + && apt-get install -y docker-ce-cli \ + && rm -rf /var/lib/apt/lists/* # buildkit + +# Fix lldb python installation +RUN ln -s /usr/lib/llvm-14/lib/python3.10/dist-packages/lldb/* /usr/lib/python3/dist-packages/lldb/ diff --git a/tests/manual/docker/linux/Dockerfile b/tests/manual/docker/linux/Dockerfile new file mode 100644 index 00000000000..4d1c07cf87d --- /dev/null +++ b/tests/manual/docker/linux/Dockerfile @@ -0,0 +1,22 @@ +FROM ubuntu:22.04 + +RUN apt-get update && \ + apt-get install -y \ + libgl1-mesa-dev \ + qt6-base-dev \ + qt6-base-private-dev \ + qt6-declarative-dev \ + qt6-declarative-private-dev \ + libqt6core5compat6-dev \ + build-essential \ + cmake \ + nano \ + ninja-build \ + ca-certificates \ + curl \ + gnupg \ + lsb-release \ + gdb \ + xterm \ + && rm -rf /var/lib/apt/lists/* + diff --git a/tests/manual/docker/linux/Dockerfile-base b/tests/manual/docker/linux/Dockerfile-base new file mode 100644 index 00000000000..3693289a8db --- /dev/null +++ b/tests/manual/docker/linux/Dockerfile-base @@ -0,0 +1,20 @@ +FROM ubuntu:22.04 + +RUN apt-get update && \ + apt-get install -y \ + build-essential curl cmake libssl-dev python3 nano ninja-build \ + libglu1-mesa-dev libgl1-mesa-dev libopengl-dev libgl-dev libfontconfig1-dev libfreetype6-dev libx11-dev libx11-xcb-dev libxext-dev libxfixes-dev libxi-dev libxrender-dev libxcb1-dev libxcb-glx0-dev libxcb-keysyms1-dev libxcb-image0-dev libxcb-shm0-dev libxcb-icccm4-dev libxcb-sync-dev libxcb-xfixes0-dev libxcb-shape0-dev libxcb-randr0-dev libxcb-render-util0-dev libxcb-util-dev libxcb-xinerama0-dev libxcb-xkb-dev libxkbcommon-dev libxkbcommon-x11-dev && \ + rm -rf /var/lib/apt/lists/* + +RUN curl -L https://download.qt.io/official_releases/qt/6.4/6.4.2/single/qt-everywhere-src-6.4.2.tar.xz -o /tmp/qt.tar.xz && \ + cd /tmp && tar -xf qt.tar.xz && \ + cd /tmp/qt-everywhere-src-6.4.2 && \ + ./configure -nomake tests -debug -skip qtlocation -skip qtpositioning -no-warnings-are-errors && \ + cmake --build . --parallel 4 && \ + cmake --install . && \ + rm -rf /tmp/* + +ENV PATH="/usr/local/Qt-6.4.2/bin:$PATH" + + + diff --git a/tests/manual/docker/linux/Dockerfile-llvm b/tests/manual/docker/linux/Dockerfile-llvm new file mode 100644 index 00000000000..5f83e1432b6 --- /dev/null +++ b/tests/manual/docker/linux/Dockerfile-llvm @@ -0,0 +1,29 @@ +FROM ubuntu:22.04 + +RUN apt-get update && \ + apt-get install -y \ + libgl1-mesa-dev \ + qt6-base-dev \ + qt6-base-private-dev \ + qt6-declarative-dev \ + qt6-declarative-private-dev \ + libqt6core5compat6-dev \ + build-essential \ + clang-format clang-tidy clang-tools clang clangd libc++-dev libc++1 libc++abi-dev libc++abi1 libclang-dev libclang1 liblldb-dev libllvm-ocaml-dev libomp-dev libomp5 lld lldb llvm-dev llvm-runtime llvm python3-clang python3-lldb \ + cmake \ + nano \ + ninja-build \ + ca-certificates \ + curl \ + gnupg \ + lsb-release \ + gdb \ + xterm \ + && curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg \ + && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null \ + && apt-get update \ + && apt-get install -y docker-ce-cli \ + && rm -rf /var/lib/apt/lists/* # buildkit + +# Fix lldb python installation +RUN ln -s /usr/lib/llvm-14/lib/python3.10/dist-packages/lldb/* /usr/lib/python3/dist-packages/lldb/ diff --git a/tests/manual/docker/linux/Dockerfile-llvm-conan b/tests/manual/docker/linux/Dockerfile-llvm-conan new file mode 100644 index 00000000000..6b97d319d14 --- /dev/null +++ b/tests/manual/docker/linux/Dockerfile-llvm-conan @@ -0,0 +1,32 @@ +FROM ubuntu:22.04 + +RUN apt-get update && \ + apt-get install -y \ + libgl1-mesa-dev \ + qt6-base-dev \ + qt6-base-private-dev \ + qt6-declarative-dev \ + qt6-declarative-private-dev \ + libqt6core5compat6-dev \ + build-essential \ + clang-format clang-tidy clang-tools clang clangd libc++-dev libc++1 libc++abi-dev libc++abi1 libclang-dev libclang1 liblldb-dev libllvm-ocaml-dev libomp-dev libomp5 lld lldb llvm-dev llvm-runtime llvm python3-clang python3-lldb \ + cmake \ + nano \ + ninja-build \ + ca-certificates \ + curl \ + gnupg \ + lsb-release \ + gdb \ + && curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg \ + && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null \ + && apt-get update \ + && apt-get install -y docker-ce-cli \ + && rm -rf /var/lib/apt/lists/* # buildkit + +# Fix lldb python installation +RUN ln -s /usr/lib/llvm-14/lib/python3.10/dist-packages/lldb/* /usr/lib/python3/dist-packages/lldb/ + +RUN curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py && python3 get-pip.py && rm get-pip.py +RUN pip install conan && conan --version && mkdir /.conan && chmod 777 /.conan + diff --git a/tests/manual/docker/linux/Dockerfile-llvm-vcpkg b/tests/manual/docker/linux/Dockerfile-llvm-vcpkg new file mode 100644 index 00000000000..e20c91c38d2 --- /dev/null +++ b/tests/manual/docker/linux/Dockerfile-llvm-vcpkg @@ -0,0 +1,36 @@ +FROM ubuntu:22.04 + +RUN apt-get update && \ + apt-get install -y \ + libgl1-mesa-dev \ + qt6-base-dev \ + qt6-base-private-dev \ + qt6-declarative-dev \ + qt6-declarative-private-dev \ + libqt6core5compat6-dev \ + build-essential \ + clang-format clang-tidy clang-tools clang clangd libc++-dev libc++1 libc++abi-dev libc++abi1 libclang-dev libclang1 liblldb-dev libllvm-ocaml-dev libomp-dev libomp5 lld lldb llvm-dev llvm-runtime llvm python3-clang python3-lldb \ + cmake \ + nano \ + ninja-build \ + ca-certificates \ + curl \ + gnupg \ + lsb-release \ + git \ + zip unzip tar \ + gdb \ + && curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg \ + && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null \ + && apt-get update \ + && apt-get install -y docker-ce-cli \ + && rm -rf /var/lib/apt/lists/* # buildkit + +# Fix lldb python installation +RUN ln -s /usr/lib/llvm-14/lib/python3.10/dist-packages/lldb/* /usr/lib/python3/dist-packages/lldb/ + +RUN cd / && git clone --depth 1 https://github.com/Microsoft/vcpkg.git && cd vcpkg && ./bootstrap-vcpkg.sh + +RUN chmod -R 777 /vcpkg + +RUN ln -s /vcpkg/vcpkg /usr/local/bin/vcpkg diff --git a/tests/manual/docker/linux/Makefile b/tests/manual/docker/linux/Makefile new file mode 100644 index 00000000000..3387094f8f2 --- /dev/null +++ b/tests/manual/docker/linux/Makefile @@ -0,0 +1,43 @@ +all: help + +help: + @grep -E '^[0-9a-zA-Z._-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' + +default_run_args = --env="DISPLAY=host.docker.internal:0" --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -v $(HOME):$(HOME) --workdir=$(HOME) + +base: Dockerfile-base ## Build Ubuntu 22.04, docker-cli image, build Qt manually + docker build . -f Dockerfile-base -t qt-linux-base -t qt-linux-base:6 + +base-run: ## Build & run the base image + docker run -it --rm -v /var/run/docker.sock:/var/run/docker.sock $(default_run_args) qt-linux-base /bin/bash + +qt-linux: Dockerfile ## Build Ubuntu 22.04, Qt 6, docker-cli image + docker build . -t qt-linux -t qt-linux:6 + +qt-linux-run: qt-linux ## Build & run the image + docker run -it --rm -v /var/run/docker.sock:/var/run/docker.sock $(default_run_args) qt-linux:6 /bin/bash + +qt-linux-with-qtc: DockerFile-with-creator ## Build Ubuntu 22.04, Qt 6, docker-cli image, Includes Qt Creator + docker build . -f DockerFile-with-creator -t qt-linux-qtc -t qt-linux-qtc:6 + +qt-linux-with-qtc-run: qt-linux-with-qtc ## Build & run the image, Includes Qt Creator + docker run -it --rm $(default_run_args) qt-linux-qtc:6 /bin/bash + +qt-linux-with-llvm: Dockerfile-llvm ## Build Ubuntu 22.04, Qt 6, docker-cli image, With Clang + docker build . -f Dockerfile-llvm -t qt-linux-llvm -t qt-linux-llvm:6 + +qt-linux-with-llvm-run: qt-linux-with-llvm ## Build & run the image, With Clang + docker run -it --rm $(default_run_args) qt-linux-llvm:6 /bin/bash + +qt-linux-with-llvm-conan: Dockerfile-llvm-conan ## Build Ubuntu 22.04, Qt 6, docker-cli image, With Clang, Conan + docker build . -f Dockerfile-llvm-conan -t qt-linux-llvm-conan -t qt-linux-llvm-conan:6 + +qt-linux-with-llvm-conan-run: qt-linux-with-llvm-conan ## Build & run the image, With Clang, Conan + docker run -it --rm $(default_run_args) qt-linux-llvm-conan:6 /bin/bash + +qt-linux-with-llvm-vcpkg: Dockerfile-llvm-vcpkg ## Build Ubuntu 22.04, Qt 6, docker-cli image, With Clang, Vcpkg + docker build . -f Dockerfile-llvm-vcpkg -t qt-linux-llvm-vcpkg -t qt-linux-llvm-vcpkg:6 + +qt-linux-with-llvm-vcpkg-run: qt-linux-with-llvm-vcpkg ## Build & run the image, With Clang, Vcpkg + docker run -it --rm $(default_run_args) qt-linux-llvm-vcpkg:6 /bin/bash +