Merge remote-tracking branch 'origin/14.0'
Conflicts: src/plugins/qmljseditor/qmljseditorsettings.cpp Change-Id: I443424afdfe48cdfc3d083d8e91335e937fcfdb6
31
.github/workflows/build_cmake.yml
vendored
@@ -49,13 +49,12 @@ jobs:
|
||||
}
|
||||
- {
|
||||
name: "macOS Latest Clang", artifact: "macos-universal",
|
||||
# TODO: move back to macos-latest when macos-latest is 13 or higher
|
||||
os: macos-13,
|
||||
os: macos-latest,
|
||||
cc: "clang", cxx: "clang++"
|
||||
}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Checkout submodules
|
||||
id: git
|
||||
shell: cmake -P {0}
|
||||
@@ -63,10 +62,10 @@ jobs:
|
||||
execute_process(COMMAND git submodule set-url -- perfparser https://code.qt.io/qt-creator/perfparser.git)
|
||||
execute_process(COMMAND git submodule update --init --recursive)
|
||||
file(MAKE_DIRECTORY release)
|
||||
if (${{github.ref}} MATCHES "tags/v([0-9.]+)")
|
||||
if (${{github.ref}} MATCHES "tags/v(([0-9.]+).*)")
|
||||
file(APPEND "$ENV{GITHUB_OUTPUT}" "tag=${CMAKE_MATCH_1}\n")
|
||||
if (EXISTS "dist/changelog/changes-${CMAKE_MATCH_1}.md")
|
||||
file(READ "dist/changelog/changes-${CMAKE_MATCH_1}.md" changelog_md)
|
||||
if (EXISTS "dist/changelog/changes-${CMAKE_MATCH_2}.md")
|
||||
file(READ "dist/changelog/changes-${CMAKE_MATCH_2}.md" changelog_md)
|
||||
endif()
|
||||
file(WRITE "release/changelog.md" "These packages are not officially supported, for official packages please check out https://download.qt.io/official_releases/qtcreator\n\n")
|
||||
file(APPEND "release/changelog.md" "${changelog_md}")
|
||||
@@ -695,41 +694,41 @@ jobs:
|
||||
endif()
|
||||
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
path: build/qtcreator-${{ matrix.config.artifact }}-${{ steps.git.outputs.tag }}.7z
|
||||
name: qtcreator-${{ matrix.config.artifact }}-${{ steps.git.outputs.tag }}.7z
|
||||
|
||||
- name: Upload Devel
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
path: build/qtcreator-${{ matrix.config.artifact }}-${{ steps.git.outputs.tag }}_dev.7z
|
||||
name: qtcreator-${{ matrix.config.artifact }}-${{ steps.git.outputs.tag }}_dev.7z
|
||||
|
||||
- name: Upload wininterrupt
|
||||
if: runner.os == 'Windows'
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
path: build/wininterrupt-${{ matrix.config.artifact }}-${{ steps.git.outputs.tag }}.7z
|
||||
name: wininterrupt-${{ matrix.config.artifact }}-${{ steps.git.outputs.tag }}.7z
|
||||
|
||||
- name: Upload qtcreatorcdbext
|
||||
if: runner.os == 'Windows' && matrix.config.is_msvc
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
path: build/qtcreatorcdbext-${{ matrix.config.artifact }}-${{ steps.git.outputs.tag }}.7z
|
||||
name: qtcreatorcdbext-${{ matrix.config.artifact }}-${{ steps.git.outputs.tag }}.7z
|
||||
|
||||
- name: Upload Debian package
|
||||
if: runner.os == 'Linux'
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
path: build/build/qtcreator-${{ matrix.config.artifact }}-${{ steps.git.outputs.tag }}.deb
|
||||
name: qtcreator-${{ matrix.config.artifact }}-${{ steps.git.outputs.tag }}.deb
|
||||
|
||||
- name: Upload disk image
|
||||
if: runner.os == 'macOS' && contains(github.ref, 'tags/v')
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
path: build/qt-creator-${{ matrix.config.artifact }}-${{ steps.git.outputs.tag }}.dmg
|
||||
name: qt-creator-${{ matrix.config.artifact }}-${{ steps.git.outputs.tag }}.dmg
|
||||
@@ -739,14 +738,14 @@ jobs:
|
||||
run: cmake -E tar cf ../${{ steps.ccache.outputs.archive_name }}.tar .
|
||||
|
||||
- name: Upload ccache archive
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
path: ./${{ steps.ccache.outputs.archive_name }}.tar
|
||||
name: ${{ steps.ccache.outputs.archive_name }}
|
||||
|
||||
- name: Upload Release Changelog
|
||||
if: contains(github.ref, 'tags/v')
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
path: ./release/changelog.md
|
||||
name: changelog.md
|
||||
@@ -758,7 +757,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: release-with-dirs
|
||||
|
||||
@@ -770,7 +769,7 @@ jobs:
|
||||
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: softprops/action-gh-release@v1
|
||||
uses: softprops/action-gh-release@v2
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
|
||||
@@ -20,7 +20,7 @@ instructions:
|
||||
maxTimeBetweenOutput: 360
|
||||
userMessageOnFailure: "Failed to download elfutils package, check logs."
|
||||
- type: ExecuteCommand
|
||||
command: "/usr/bin/7z x -y {{.AgentWorkingDir}}/build/qt_temp/elfutils-release_0.175qt-linux-x86_64.7z -o{{.AgentWorkingDir}}/build/qt_temp/elfutils"
|
||||
command: "7z x -y {{.AgentWorkingDir}}/build/qt_temp/elfutils-release_0.175qt-linux-x86_64.7z -o{{.AgentWorkingDir}}/build/qt_temp/elfutils"
|
||||
maxTimeInSeconds: 3600
|
||||
maxTimeBetweenOutput: 360
|
||||
userMessageOnFailure: "Failed to extract elfutils package, check logs."
|
||||
@@ -30,7 +30,7 @@ instructions:
|
||||
maxTimeBetweenOutput: 360
|
||||
userMessageOnFailure: "Failed to download LLVM package, check logs."
|
||||
- type: ExecuteCommand
|
||||
command: "/usr/bin/7z x -y {{.AgentWorkingDir}}/build/qt_temp/libclang.7z -o{{.AgentWorkingDir}}/build/qt_temp/"
|
||||
command: "7z x -y {{.AgentWorkingDir}}/build/qt_temp/libclang.7z -o{{.AgentWorkingDir}}/build/qt_temp/"
|
||||
maxTimeInSeconds: 3600
|
||||
maxTimeBetweenOutput: 360
|
||||
userMessageOnFailure: "Failed to extract LLVM package, check logs."
|
||||
@@ -42,7 +42,7 @@ instructions:
|
||||
- type: ChangeDirectory
|
||||
directory: "{{.AgentWorkingDir}}/build/tqtc-qtsdk/packaging_tools"
|
||||
- type: ExecuteCommand
|
||||
command: "python3 -m pipenv run python -u bld_sdktool.py --qt-url {{.Env.QTC_SDKTOOL_QT_BASE_URL}}{{.Env.QTC_SDKTOOL_QT_EXT}} --qt-build {{.AgentWorkingDir}}/build/sdktool/qt --src {{.AgentWorkingDir}}/qt-creator/qt-creator/src/tools/sdktool --build {{.AgentWorkingDir}}/build/sdktool/build --install {{.AgentWorkingDir}}/build/sdktool/install --make-command make"
|
||||
command: "python3 -m pipenv run python -u bld_sdktool.py --qt-url {{.Env.QTC_SDKTOOL_QT_BASE_URL}}{{.Env.QTC_SDKTOOL_QT_EXT}} --qt-build {{.AgentWorkingDir}}/build/sdktool/qt --src {{.AgentWorkingDir}}/qt-creator/qt-creator/src/tools/sdktool --build {{.AgentWorkingDir}}/build/sdktool/build --install {{.AgentWorkingDir}}/build/sdktool/install --make-command ninja"
|
||||
maxTimeInSeconds: 36000
|
||||
maxTimeBetweenOutput: 3600
|
||||
userMessageOnFailure: "Failed to build sdktool, check logs."
|
||||
@@ -64,7 +64,7 @@ instructions:
|
||||
maxTimeBetweenOutput: 360
|
||||
userMessageOnFailure: "Failed to download LLVM package, check logs."
|
||||
- type: ExecuteCommand
|
||||
command: "/usr/local/bin/7z x -y {{.AgentWorkingDir}}/build/qt_temp/libclang.7z -o{{.AgentWorkingDir}}/build/qt_temp/"
|
||||
command: "7z x -y {{.AgentWorkingDir}}/build/qt_temp/libclang.7z -o{{.AgentWorkingDir}}/build/qt_temp/"
|
||||
maxTimeInSeconds: 3600
|
||||
maxTimeBetweenOutput: 360
|
||||
userMessageOnFailure: "Failed to extract LLVM package, check logs."
|
||||
@@ -79,7 +79,7 @@ instructions:
|
||||
variableName: MACOSX_DEPLOYMENT_TARGET
|
||||
variableValue: "{{.Env.SDKTOOL_MACOSX_DEPLOYMENT_TARGET}}"
|
||||
- type: ExecuteCommand
|
||||
command: "python3 -m pipenv run python -u bld_sdktool.py --qt-url {{.Env.QTC_SDKTOOL_QT_BASE_URL}}{{.Env.QTC_SDKTOOL_QT_EXT}} --qt-build {{.AgentWorkingDir}}/build/sdktool/qt --src {{.AgentWorkingDir}}/qt-creator/qt-creator/src/tools/sdktool --build {{.AgentWorkingDir}}/build/sdktool/build --install {{.AgentWorkingDir}}/build/sdktool/install --make-command make"
|
||||
command: "python3 -m pipenv run python -u bld_sdktool.py --qt-url {{.Env.QTC_SDKTOOL_QT_BASE_URL}}{{.Env.QTC_SDKTOOL_QT_EXT}} --qt-build {{.AgentWorkingDir}}/build/sdktool/qt --src {{.AgentWorkingDir}}/qt-creator/qt-creator/src/tools/sdktool --build {{.AgentWorkingDir}}/build/sdktool/build --install {{.AgentWorkingDir}}/build/sdktool/install --make-command ninja --universal"
|
||||
maxTimeInSeconds: 36000
|
||||
maxTimeBetweenOutput: 3600
|
||||
userMessageOnFailure: "Failed to build sdktool, check logs."
|
||||
@@ -96,7 +96,7 @@ instructions:
|
||||
maxTimeBetweenOutput: 360
|
||||
userMessageOnFailure: "Failed to download elfutils package, check logs."
|
||||
- type: ExecuteCommand
|
||||
command: "C:\\Utils\\sevenzip\\7z.exe x -y {{.AgentWorkingDir}}\\build\\qt_temp\\elfutils-release_0.175qt-windows-x86_64.7z -o{{.AgentWorkingDir}}\\build\\qt_temp\\elfutils"
|
||||
command: "7z.exe x -y {{.AgentWorkingDir}}\\build\\qt_temp\\elfutils-release_0.175qt-windows-x86_64.7z -o{{.AgentWorkingDir}}\\build\\qt_temp\\elfutils"
|
||||
maxTimeInSeconds: 3600
|
||||
maxTimeBetweenOutput: 360
|
||||
userMessageOnFailure: "Failed to extract elfutils package, check logs."
|
||||
@@ -106,7 +106,7 @@ instructions:
|
||||
maxTimeBetweenOutput: 360
|
||||
userMessageOnFailure: "Failed to download python package, check logs."
|
||||
- type: ExecuteCommand
|
||||
command: "C:\\Utils\\sevenzip\\7z.exe x -y {{.AgentWorkingDir}}\\build\\qt_temp\\Python38-win-x64.7z -o{{.AgentWorkingDir}}\\build\\qt_temp\\python"
|
||||
command: "7z.exe x -y {{.AgentWorkingDir}}\\build\\qt_temp\\Python38-win-x64.7z -o{{.AgentWorkingDir}}\\build\\qt_temp\\python"
|
||||
maxTimeInSeconds: 3600
|
||||
maxTimeBetweenOutput: 360
|
||||
userMessageOnFailure: "Failed to extract python package, check logs."
|
||||
@@ -116,7 +116,7 @@ instructions:
|
||||
maxTimeBetweenOutput: 360
|
||||
userMessageOnFailure: "Failed to download LLVM package, check logs."
|
||||
- type: ExecuteCommand
|
||||
command: "C:\\Utils\\sevenzip\\7z.exe x -y {{.AgentWorkingDir}}\\build\\qt_temp\\libclang.7z -o{{.AgentWorkingDir}}\\build\\qt_temp\\"
|
||||
command: "7z.exe x -y {{.AgentWorkingDir}}\\build\\qt_temp\\libclang.7z -o{{.AgentWorkingDir}}\\build\\qt_temp\\"
|
||||
maxTimeInSeconds: 3600
|
||||
maxTimeBetweenOutput: 360
|
||||
userMessageOnFailure: "Failed to extract LLVM package, check logs."
|
||||
@@ -128,7 +128,7 @@ instructions:
|
||||
- type: ChangeDirectory
|
||||
directory: "{{.AgentWorkingDir}}\\build\\tqtc-qtsdk\\packaging_tools"
|
||||
- type: ExecuteCommand
|
||||
command: "python -m pipenv run python -u bld_sdktool.py --qt-url {{.Env.QTC_SDKTOOL_QT_BASE_URL}}{{.Env.QTC_SDKTOOL_QT_EXT}} --qt-build {{.AgentWorkingDir}}\\build\\sdktool\\qt --src {{.AgentWorkingDir}}\\qt-creator\\qt-creator\\src\\tools\\sdktool --build {{.AgentWorkingDir}}\\build\\sdktool\\build --install {{.AgentWorkingDir}}\\build\\sdktool\\install --make-command nmake"
|
||||
command: "python -m pipenv run python -u bld_sdktool.py --qt-url {{.Env.QTC_SDKTOOL_QT_BASE_URL}}{{.Env.QTC_SDKTOOL_QT_EXT}} --qt-build {{.AgentWorkingDir}}\\build\\sdktool\\qt --src {{.AgentWorkingDir}}\\qt-creator\\qt-creator\\src\\tools\\sdktool --build {{.AgentWorkingDir}}\\build\\sdktool\\build --install {{.AgentWorkingDir}}\\build\\sdktool\\install --make-command ninja"
|
||||
maxTimeInSeconds: 36000
|
||||
maxTimeBetweenOutput: 3600
|
||||
userMessageOnFailure: "Failed to build sdktool, check logs."
|
||||
|
||||
@@ -19,10 +19,10 @@ instructions:
|
||||
variableValue: 11.0
|
||||
- type: EnvironmentVariable
|
||||
variableName: SDKTOOL_MACOSX_DEPLOYMENT_TARGET
|
||||
variableValue: 10.14
|
||||
variableValue: 11.0
|
||||
- type: EnvironmentVariable
|
||||
variableName: QTC_SDKTOOL_QT_BASE_URL
|
||||
variableValue: "https://ci-files02-hki.ci.qt.io/packages/jenkins/archive/qt/5.15/5.15.2-final-released/latest/src/submodules/qtbase-everywhere-src-5.15.2"
|
||||
variableValue: "https://ci-files02-hki.ci.qt.io/packages/jenkins/archive/qt/6.6/6.6.0-released/Qt/src/submodules/qtbase-everywhere-src-6.6.0"
|
||||
- type: Group
|
||||
instructions:
|
||||
- type: EnvironmentVariable
|
||||
|
||||
@@ -20,7 +20,7 @@ instructions:
|
||||
maxTimeInSeconds: 600
|
||||
maxTimeBetweenOutput: 600
|
||||
project: qtsdk/tqtc-qtsdk
|
||||
ref: master
|
||||
ref: production
|
||||
directory: "build/tqtc-qtsdk"
|
||||
userMessageOnFailure: "Failed to install tqtc-qtsdk, check logs"
|
||||
- type: Group
|
||||
|
||||
49
dist/changelog/changes-14.0.0.md
vendored
@@ -5,16 +5,16 @@ Qt Creator version 14 contains bug fixes and new features.
|
||||
|
||||
The most important changes are listed in this document. For a complete list of
|
||||
changes, see the Git log for the Qt Creator sources that you can check out from
|
||||
the public Git repository. For example:
|
||||
the public Git repository or view online at
|
||||
|
||||
git clone git://code.qt.io/qt-creator/qt-creator.git
|
||||
git log --cherry-pick --pretty=oneline origin/13.0..v14.0.0
|
||||
https://code.qt.io/cgit/qt-creator/qt-creator.git/log/?id=13.0..v14.0.0
|
||||
|
||||
General
|
||||
-------
|
||||
|
||||
* Started work on supporting Lua based plugins (registering language servers,
|
||||
actions, preferences, and wizards)
|
||||
([Documentation](https://doc-snapshots.qt.io/qtcreator-extending/lua-extensions.html))
|
||||
* Added `Clear` and `Save Contents` to context menus of all output views
|
||||
* Locator
|
||||
* Added the option to show results relative to project root
|
||||
@@ -42,6 +42,8 @@ Editing
|
||||
### C++
|
||||
|
||||
* Made C++ code model settings configurable per project
|
||||
* Added a setting for the naming of include guards
|
||||
([QTCREATORBUG-25117](https://bugreports.qt.io/browse/QTCREATORBUG-25117))
|
||||
* Fixed indentation after function calls with subscript operator
|
||||
([QTCREATORBUG-29225](https://bugreports.qt.io/browse/QTCREATORBUG-29225))
|
||||
* Refactoring
|
||||
@@ -58,6 +60,7 @@ Editing
|
||||
[Documentation](https://doc.qt.io/qtcreator/creator-reference-cpp-quick-fixes.html)
|
||||
|
||||
* Clangd
|
||||
* Updated the prebuilt binaries to LLVM 18.1.7
|
||||
* Increased the minimum version to LLVM 17
|
||||
* Added the `Per-project index location` and `Per-session index location`
|
||||
options in `Preferences` > `C++` > `Clangd` for setting the index location
|
||||
@@ -88,14 +91,19 @@ Editing
|
||||
([QTCREATORBUG-19226](https://bugreports.qt.io/browse/QTCREATORBUG-19226))
|
||||
* Added `Qt Design Studio` to `Open With` for `.ui.qml` files
|
||||
([Documentation](https://doc.qt.io/qtcreator/creator-quick-ui-forms.html))
|
||||
* Fixed that the color preview did not work on named colors
|
||||
([QTCREATORBUG-30594](https://bugreports.qt.io/browse/QTCREATORBUG-30594))
|
||||
* Language Server
|
||||
* Switched on by default
|
||||
* Added option for generating `qmlls.ini` files for CMake projects
|
||||
* Switched on by default for Qt 6.8 and later
|
||||
* Added an option for generating `qmlls.ini` files for CMake projects in
|
||||
`Preferences` > `Qt Quick`> `QML/JS Editing`
|
||||
([QTCREATORBUG-30394](https://bugreports.qt.io/browse/QTCREATORBUG-30394))
|
||||
([Qt Documentation](https://doc.qt.io/qt-6/cmake-variable-qt-qml-generate-qmlls-ini.html))
|
||||
* Fixed that tool tips from the built-in model were shown instead of tool tips
|
||||
from the server
|
||||
|
||||
[Documentation](https://doc.qt.io/qtcreator/creator-how-to-use-qml-language-server.html)
|
||||
|
||||
### Python
|
||||
|
||||
* Added options for updating Python Language Server
|
||||
@@ -114,6 +122,10 @@ Editing
|
||||
`Compiler Explorer`
|
||||
[Documentation](https://doc.qt.io/qtcreator/creator-how-to-create-compiler-explorer-sessions.html)
|
||||
|
||||
### Markdown
|
||||
|
||||
* Fixed the navigation history
|
||||
|
||||
### Models
|
||||
|
||||
* Added more visual attributes for relations
|
||||
@@ -135,6 +147,8 @@ Projects
|
||||
from the list in the `Projects` mode
|
||||
* Added support for user comments in the environment editor
|
||||
([Documentation](https://doc-snapshots.qt.io/qtcreator-14.0/creator-how-to-edit-environment-settings.html))
|
||||
* Added the setting `Time to wait before force-stopping applications`
|
||||
([QTCREATORBUG-31025](https://bugreports.qt.io/browse/QTCREATORBUG-31025))
|
||||
* Fixed the parsing of file links when color was used for the output
|
||||
([QTCREATORBUG-30774](https://bugreports.qt.io/browse/QTCREATORBUG-30774))
|
||||
* Fixed that the column information was not used when opening files from links
|
||||
@@ -151,12 +165,18 @@ Projects
|
||||
* Implemented `Open Online Documentation` for CMake documentation
|
||||
* Added `Clear CMake Configuration` to the context menu in the `Projects` view
|
||||
([QTCREATORBUG-24658](https://bugreports.qt.io/browse/QTCREATORBUG-24658))
|
||||
* Added support for the `CROSSCOMPILING_EMULATOR` target property
|
||||
([QTCREATORBUG-29880](https://bugreports.qt.io/browse/QTCREATORBUG-29880))
|
||||
([CMake Documentation](https://cmake.org/cmake/help/latest/prop_tgt/CROSSCOMPILING_EMULATOR.html#crosscompiling-emulator))
|
||||
* Fixed that the package manager auto-setup files were not removed with
|
||||
`Clear CMake Configuration`
|
||||
([QTCREATORBUG-30771](https://bugreports.qt.io/browse/QTCREATORBUG-30771))
|
||||
* Fixed that files generated by the Qt QML CMake API were not filtered as
|
||||
generated files
|
||||
([QTCREATORBUG-29631](https://bugreports.qt.io/browse/QTCREATORBUG-29631))
|
||||
* Fixed a crash when triggering `Follow Symbol` in a CMake file that does not
|
||||
belong to a project
|
||||
([QTCREATORBUG-31077](https://bugreports.qt.io/browse/QTCREATORBUG-31077))
|
||||
* Presets
|
||||
* Made CMake settings configurable
|
||||
([QTCREATORBUG-25972](https://bugreports.qt.io/browse/QTCREATORBUG-25972),
|
||||
@@ -200,7 +220,15 @@ Analyzer
|
||||
|
||||
### Axivion
|
||||
|
||||
* Made it possible to register multiple servers
|
||||
* Added the `Add` and `Remove` buttons to `Preferences` > `Axivion` for
|
||||
registering multiple servers
|
||||
([Documentation](https://doc-snapshots.qt.io/qtcreator-14.0/creator-preferences-axivion.html))
|
||||
|
||||
### Cppcheck
|
||||
|
||||
* Fixed that Cppcheck was not working until selecting `Apply` in the settings
|
||||
([QTCREATORBUG-28951](https://bugreports.qt.io/browse/QTCREATORBUG-28951),
|
||||
[QTCREATORBUG-30615](https://bugreports.qt.io/browse/QTCREATORBUG-30615))
|
||||
|
||||
Terminal
|
||||
--------
|
||||
@@ -249,11 +277,15 @@ Platforms
|
||||
* Added support for creating `android-desktop` devices
|
||||
* Added support for `namespace` in `build.gradle`
|
||||
([QTBUG-106907](https://bugreports.qt.io/browse/QTBUG-106907))
|
||||
* Fixed that errors when creating AVDs were not visible to the user
|
||||
([QTCREATORBUG-30852](https://bugreports.qt.io/browse/QTCREATORBUG-30852))
|
||||
|
||||
### iOS
|
||||
|
||||
* Removed Simulator management from the preferences. Use the
|
||||
`Devices and Simulators` window in Xcode instead.
|
||||
* Fixed that starting the debugger could be slow for iOS < 17
|
||||
([QTCREATORBUG-31044](https://bugreports.qt.io/browse/QTCREATORBUG-31044))
|
||||
|
||||
### Remote Linux
|
||||
|
||||
@@ -266,6 +298,11 @@ Platforms
|
||||
|
||||
* Added support for the `perf` profiler
|
||||
|
||||
### Bare Metal
|
||||
|
||||
* Fixed issues with Qbs and the IAR toolchain
|
||||
([QTCREATORBUG-24040](https://bugreports.qt.io/browse/QTCREATORBUG-24040))
|
||||
|
||||
Credits for these changes go to:
|
||||
--------------------------------
|
||||
Ahmad Samir
|
||||
|
||||
4
dist/changelog/template.md
vendored
@@ -83,6 +83,8 @@ Analyzer
|
||||
|
||||
### CTF Visualizer
|
||||
|
||||
### Cppcheck
|
||||
|
||||
Terminal
|
||||
--------
|
||||
|
||||
@@ -127,5 +129,7 @@ Platforms
|
||||
|
||||
### QNX
|
||||
|
||||
### Bare Metal
|
||||
|
||||
Credits for these changes go to:
|
||||
--------------------------------
|
||||
|
||||
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
@@ -15,13 +15,14 @@
|
||||
\c CMakeUserPresets.json has options for your local builds.
|
||||
|
||||
Create the presets files in the format described in
|
||||
\l{https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html}
|
||||
\l{https://cmake.org/cmake/help/v3.24/manual/cmake-presets.7.html}
|
||||
{cmake-presets(7)} and store them in the project's root directory.
|
||||
You can then see them in the \l {Projects} view.
|
||||
|
||||
\QC supports presets up to version 3 (introduced in CMake 3.21), but does not
|
||||
enforce version checking. It reads and uses all the fields from version 3 if
|
||||
present. It does not support test presets.
|
||||
\QC supports \e configure and \e build presets up to version 5
|
||||
(introduced in CMake 3.24), but does not enforce version checking.
|
||||
It reads and uses all the fields from version 5 if present.
|
||||
It does not support \e test presets.
|
||||
|
||||
You can import the presets the first time you \l {Open projects}
|
||||
{open a project}, when no \c CMakeLists.txt.user file exists or you have
|
||||
@@ -44,7 +45,7 @@
|
||||
\c NOT_COMMON_VALUE is displayed in \uicontrol {Initial Parameters}
|
||||
and \c AN_ENVIRONMENT_FLAG in the environment configuration field.
|
||||
|
||||
\badcode
|
||||
\code
|
||||
{
|
||||
"version": 1,
|
||||
"configurePresets": [
|
||||
@@ -91,7 +92,7 @@
|
||||
\li GNU gdb 11.2.0 for MinGW 11.2.0 64-bit debugger
|
||||
\endlist
|
||||
|
||||
\badcode
|
||||
\code
|
||||
{
|
||||
"version": 1,
|
||||
"configurePresets": [
|
||||
@@ -131,7 +132,7 @@
|
||||
generator, add \c Debug and \c Release build steps, and specify the path
|
||||
to \c ninja.exe as a value of the \c CMAKE_MAKE_PROGRAM variable:
|
||||
|
||||
\badcode
|
||||
\code
|
||||
{
|
||||
"version": 2,
|
||||
"configurePresets": [
|
||||
@@ -180,7 +181,7 @@
|
||||
|
||||
For example:
|
||||
|
||||
\badcode
|
||||
\code
|
||||
"generator": "Ninja Multi-Config",
|
||||
"toolset": {
|
||||
"value": "v142,host=x64",
|
||||
@@ -196,7 +197,7 @@
|
||||
in the \c PATH, you might also have to specify the compiler to use in
|
||||
\c cacheVariables or \c environmentVariables:
|
||||
|
||||
\badcode
|
||||
\code
|
||||
"generator": "Ninja Multi-Config",
|
||||
"toolset": {
|
||||
"value": "v142,host=x64",
|
||||
@@ -226,7 +227,7 @@
|
||||
\li \c wine emulator
|
||||
\endlist
|
||||
|
||||
\badcode
|
||||
\code
|
||||
{
|
||||
"version": 4,
|
||||
"configurePresets": [
|
||||
@@ -262,7 +263,7 @@
|
||||
if the \c hostSystemName equals \c Linux, the \c linux presets are used and
|
||||
if it equals \c Windows, the \c windows presets are used.
|
||||
|
||||
\badcode
|
||||
\code
|
||||
{
|
||||
"version": 3,
|
||||
"configurePresets": [
|
||||
|
||||
@@ -120,11 +120,14 @@
|
||||
tooltips
|
||||
\li Selecting any of the above elements and pressing \key F1 to show
|
||||
its documentation
|
||||
\li Switching to the Help mode
|
||||
\li Switching to the \uicontrol Help mode
|
||||
\endlist
|
||||
|
||||
\sa {Build with CMake}{How To: Build with CMake}, {CMake},
|
||||
{Read Documentation}{How To: Read Documentation}
|
||||
To view the documentation online, open it in the \uicontrol Help mode and
|
||||
select \inlineimage icons/online.png (\uicontrol {Open Online Documentation}).
|
||||
|
||||
\sa {Build with CMake}{How To: Build with CMake},
|
||||
{Read Documentation}{How To: Read Documentation}, {CMake}
|
||||
*/
|
||||
|
||||
/*!
|
||||
|
||||
@@ -85,9 +85,8 @@
|
||||
To enable a language server, select the checkbox next to the language
|
||||
server name and set server preferences.
|
||||
|
||||
To turn on \l{Turn on \QMLLS}{\QMLLS}, go to
|
||||
\preferences > \uicontrol {Qt Quick} > \uicontrol {QML/JS Editing} and
|
||||
select \uicontrol {Enable \QMLLS}.
|
||||
To configure \l{Configure \QMLLS}{\QMLLS}, go to
|
||||
\preferences > \uicontrol {Qt Quick} > \uicontrol {QML/JS Editing}.
|
||||
|
||||
To remove language servers from the list, select \uicontrol Delete.
|
||||
|
||||
@@ -237,35 +236,33 @@
|
||||
|
||||
\ingroup creator-how-to-lsp
|
||||
|
||||
\title Turn on \QMLLS
|
||||
\title Configure \QMLLS
|
||||
|
||||
Since Qt 6.4, \QMLLS offers code completion and
|
||||
issues warnings for QML. To use it, go to \preferences >
|
||||
\uicontrol {Qt Quick} > \uicontrol {QML/JS Editing} and select
|
||||
\uicontrol {Enable \QMLLS}.
|
||||
Since Qt 6.4, \QMLLS offers code completion and issues warnings for QML.
|
||||
|
||||
By default, enabling \QMLLS will only enable warning messages
|
||||
and code completion, while advanced features such as renaming and finding usages
|
||||
will be handled by the embedded code model.
|
||||
To disable the embedded code model and use \QMLLS for everything,
|
||||
select \uicontrol {Use \QMLLS advanced features}.
|
||||
To turn off \QMLLS, go to \preferences > \uicontrol {Qt Quick} >
|
||||
\uicontrol {QML/JS Editing} and clear \uicontrol {Turn on}.
|
||||
|
||||
By default, \QMLLS issues warning messages and provides code completion,
|
||||
while the embedded code model handles advanced features, such as renaming
|
||||
symbols and finding usages. To disable the embedded code model and use
|
||||
\QMLLS for everything, select \uicontrol {Use advanced features}.
|
||||
|
||||
Also, \QC tries to use \QMLLS shipped with the Qt version in your current
|
||||
\l{Kits}{kit}. To override that behavior and always use
|
||||
\QMLLS of the highest registered Qt version, select
|
||||
\uicontrol {Use \QMLLS from latest Qt version}.
|
||||
\uicontrol {Use from latest Qt version}.
|
||||
|
||||
To use older \QMLLS versions, select
|
||||
\uicontrol{Allow versions below Qt 6.8}.
|
||||
|
||||
\image qtcreator-qml-js-editing.webp {QML/JS Editing preferences}
|
||||
|
||||
When using \c qmlls from Qt 6.7 or later, set \l{QT_QML_GENERATE_QMLLS_INI}
|
||||
to \c{ON} in \uicontrol Projects > \uicontrol {Build Settings}
|
||||
> \uicontrol {Initial Configuration}.
|
||||
To automatically configure new CMake projects, select
|
||||
\uicontrol {Create .qmlls.ini files for new projects}.
|
||||
|
||||
\sa {Manage Language Servers}{How To: Manage Language Servers},
|
||||
{Enabling and Disabling Messages}, {CMake Build Configuration}, {Kits}
|
||||
{Enabling and Disabling Messages}, {CMake Build Configuration}, {Kits},
|
||||
{Language Servers}
|
||||
*/
|
||||
|
||||
|
||||
@@ -23,23 +23,23 @@
|
||||
|
||||
\li To view context sensitive help on a Qt class or function as a
|
||||
tooltip, move the mouse cursor over the class or function. If help
|
||||
is not available, the tooltip displays type information for the
|
||||
is not available, the tooltip shows type information for the
|
||||
symbol.
|
||||
|
||||
\li To display tooltips for function signatures regardless of the
|
||||
\li To show tooltips for function signatures regardless of the
|
||||
cursor position in the function call, press \key {Ctrl+Shift+D}.
|
||||
|
||||
\li To display the full help on a Qt class or function, press \key F1 or
|
||||
\li To show the full help on a Qt class or function, press \key F1 or
|
||||
select \uicontrol {Context Help} in the context menu.
|
||||
The documentation is displayed in a
|
||||
The documentation is shown in a
|
||||
view next to the code editor, or, if there is not enough vertical
|
||||
space, in the fullscreen \uicontrol Help mode.
|
||||
|
||||
\li To select and configure how the documentation is displayed in the
|
||||
\uicontrol Help mode, select \preferences > \uicontrol Help.
|
||||
\li To change how the documentation is shown in the
|
||||
\uicontrol Help mode, go to \preferences > \uicontrol Help.
|
||||
\endlist
|
||||
|
||||
The following image displays the context sensitive help in the \uicontrol Edit
|
||||
The following image shows the context sensitive help in the \uicontrol Edit
|
||||
mode.
|
||||
|
||||
\image qtcreator-context-sensitive-help.png {Context-sensitive help in Edit mode}
|
||||
@@ -52,43 +52,43 @@
|
||||
|
||||
\image qtcreator-preferences-help-general.webp {General tab in Help preferences}
|
||||
|
||||
You can set the default zoom level in the \uicontrol Zoom field. When
|
||||
viewing help pages, you can use the mouse scroll wheel to zoom them. To
|
||||
disable this feature, deselect the \uicontrol {Enable scroll wheel zooming}
|
||||
check box.
|
||||
Set the default zoom level in \uicontrol Zoom. When viewing help pages, use
|
||||
the mouse scroll wheel to zoom them. To turn off this feature, clear
|
||||
\uicontrol {Enable scroll wheel zooming}.
|
||||
|
||||
To disable antialiasing, deselect the \uicontrol Antialiasing check box.
|
||||
To turn off antialiasing, clear \uicontrol Antialias.
|
||||
|
||||
\section1 Return to the editor
|
||||
|
||||
To switch to the editor context when you close the last help page, select
|
||||
the \uicontrol {Return to editor on closing the last page} check box.
|
||||
\uicontrol {Return to editor on closing the last page}.
|
||||
|
||||
\section1 Select help viewer backend
|
||||
|
||||
The help viewer backend determines the style sheet that is used to display
|
||||
The help viewer backend determines the style sheet that is used to show
|
||||
the help files. The default help viewer backend that is based on litehtml
|
||||
is recommended for viewing Qt documentation. You can choose another help
|
||||
viewer backend in the \uicontrol {Viewer backend} field. To take the new
|
||||
viewer backend in the \uicontrol {Viewer backend}. To take the new
|
||||
backend to use, reload the help page.
|
||||
|
||||
\section1 View function tooltips
|
||||
|
||||
To hide function tooltips by default, select \preferences >
|
||||
\uicontrol {Text Editor} > \uicontrol Behavior >
|
||||
\uicontrol {Show help tooltips using the mouse} >
|
||||
\uicontrol {On Shift+Mouseover}. You can still view the tooltips by pressing
|
||||
and holding down the \key Shift key.
|
||||
To hide function tooltips by default:
|
||||
|
||||
\list 1
|
||||
\li Go to \preferences > \uicontrol {Text Editor} > \uicontrol Behavior.
|
||||
\image qtcreator-preferences-texteditor-behavior.webp {Text Editor Behavior preferences}
|
||||
\li In \uicontrol {Show help tooltips using the mouse}, select
|
||||
\uicontrol {On Shift+Mouseover}.
|
||||
\endlist
|
||||
|
||||
You can still view the tooltips by pressing and holding down the \key Shift
|
||||
key.
|
||||
|
||||
To use a keyboard shortcut for viewing help tooltips, select
|
||||
\uicontrol {Show help tooltips using keyboard shortcut (Alt)}.
|
||||
|
||||
\sa {Find information in Qt documentation}, {Filter documentation},
|
||||
{Search from documentation}
|
||||
|
||||
\sa {Add external documentation}, {Detach the help window},
|
||||
{Filter documentation}, {Find information in Qt documentation},
|
||||
{Select the help start page}
|
||||
\sa {Read Documentation}{How To: Read Documentation}
|
||||
*/
|
||||
|
||||
/*!
|
||||
@@ -103,15 +103,18 @@
|
||||
|
||||
\title Find information in Qt documentation
|
||||
|
||||
\QC, \QSDK and other Qt deliverables have documentation
|
||||
as .qch files. All the documentation is accessible in the \uicontrol Help mode.
|
||||
\QC installer, \QOI, and other Qt deliverables install documentation as .qch
|
||||
files. View the documentation in the \uicontrol Help mode. To view the
|
||||
currently open document online, select \inlineimage icons/online.png
|
||||
(\uicontrol {Open Online Documentation}).
|
||||
|
||||
By default, \QC registers only the latest available version of the
|
||||
documentation for each installed Qt module. To register all installed
|
||||
documentation, select \preferences > \uicontrol Kits >
|
||||
\uicontrol {Qt Versions} > \uicontrol {Register documentation}.
|
||||
documentation, go to \preferences > \uicontrol Kits >
|
||||
\uicontrol {Qt Versions} and select an option in
|
||||
\uicontrol {Register documentation}.
|
||||
|
||||
\image qtcreator-qt-versions.png {Register documentation field in Qt Versions tab in Kit Preferences}
|
||||
\image qtcreator-qt-versions.png {Register documentation in Qt Versions preferences}
|
||||
|
||||
\section1 Help mode sidebar views
|
||||
|
||||
@@ -137,7 +140,7 @@
|
||||
|
||||
\endlist
|
||||
|
||||
\sa {Add bookmarks to help pages}, {Search from documentation}
|
||||
\sa {Read Documentation}{How To: Read Documentation}
|
||||
*/
|
||||
|
||||
/*!
|
||||
@@ -152,8 +155,8 @@
|
||||
|
||||
\title Add bookmarks to help pages
|
||||
|
||||
You can add bookmarks to useful help pages to easily find them later
|
||||
in the \uicontrol Bookmarks view. You can either use the page title as the
|
||||
Add bookmarks to useful help pages to easily find them later
|
||||
in the \uicontrol Bookmarks view. Either use the page title as the
|
||||
bookmark or change it to any text. You can organize the bookmarks in
|
||||
folders in the view.
|
||||
|
||||
@@ -163,21 +166,21 @@
|
||||
|
||||
\list 1
|
||||
|
||||
\li Click the \inlineimage icons/bookmark.png
|
||||
(\uicontrol {Add Bookmark}) button on the toolbar.
|
||||
\li Select \inlineimage icons/bookmark.png (\uicontrol {Add Bookmark})
|
||||
on the toolbar.
|
||||
|
||||
\li In the \uicontrol {Add Bookmark} dialog, click \uicontrol OK to save the
|
||||
\li In the \uicontrol {Add Bookmark} dialog, select \uicontrol OK to save the
|
||||
page title as a bookmark in the selected folder.
|
||||
|
||||
\endlist
|
||||
|
||||
\section1 Import and export bookmarks
|
||||
|
||||
To import and export bookmarks, select \preferences >
|
||||
\uicontrol Help > \uicontrol General > \uicontrol {Import Bookmarks} or
|
||||
To import and export bookmarks, go to \preferences > \uicontrol Help >
|
||||
\uicontrol General and select \uicontrol {Import Bookmarks} or
|
||||
\uicontrol {Export Bookmarks}.
|
||||
|
||||
\sa {Find information in Qt documentation}
|
||||
\sa {Read Documentation}{How To: Read Documentation}
|
||||
*/
|
||||
|
||||
/*!
|
||||
@@ -192,10 +195,10 @@
|
||||
|
||||
\title Search from documentation
|
||||
|
||||
In the \uicontrol Help mode \uicontrol Search pane, you can use full-text
|
||||
In the \uicontrol Help mode \uicontrol Search pane, use full-text
|
||||
search for finding a
|
||||
particular word in all the installed documents. Enter the term you are
|
||||
looking for, and select the \uicontrol Search button. All documents that
|
||||
looking for, and select \uicontrol Search. All documents that
|
||||
have the specified term are listed. The list is sorted by document
|
||||
version (if you have installed several Qt versions, for example) and
|
||||
the number of search hits that the documents have. Select a document in
|
||||
@@ -225,16 +228,15 @@
|
||||
time when you open the \uicontrol Search pane. If you add or remove documents,
|
||||
\QC recreates the index.
|
||||
|
||||
If you cannot find words that you know are there, indexing might not have
|
||||
been completed for some reason. To regenerate the index, click
|
||||
\inlineimage icons/reload_gray.png
|
||||
If you cannot find words that you know are there, the index might not be
|
||||
complete. To recreate it, select \inlineimage icons/reload_gray.png
|
||||
(\uicontrol {Regenerate Index}).
|
||||
|
||||
Punctuation is not included in indexed terms. To find terms that have
|
||||
punctuation, such as domain names, use the asterisk as a wild card. For
|
||||
example, to find \c {Pastebin.Com}, enter the search term \c {Pastebin*}.
|
||||
|
||||
\sa {Find information in Qt documentation}
|
||||
\sa {Read Documentation}{How To: Read Documentation}
|
||||
*/
|
||||
|
||||
/*!
|
||||
@@ -249,7 +251,7 @@
|
||||
|
||||
\title Add external documentation
|
||||
|
||||
You can display external documentation in the \uicontrol Help mode.
|
||||
You can view external documentation in the \uicontrol Help mode.
|
||||
To add documentation or replace the documentation that ships with \QC and Qt:
|
||||
|
||||
\list 1
|
||||
@@ -259,14 +261,14 @@
|
||||
For information on how to prepare your documentation and create a
|
||||
.qch file, see \l{The Qt Help Framework}.
|
||||
|
||||
\li To add the .qch file to \QC, select \preferences >
|
||||
\li To add the .qch file to \QC, go to \preferences >
|
||||
\uicontrol Help > \uicontrol Documentation > \uicontrol Add.
|
||||
|
||||
\image qtcreator-preferences-help-documentation.webp {Documentation tab in Help Preferences}
|
||||
|
||||
\endlist
|
||||
|
||||
\sa {Get help}
|
||||
\sa {Read Documentation}{How To: Read Documentation}
|
||||
*/
|
||||
|
||||
/*!
|
||||
@@ -281,26 +283,24 @@
|
||||
|
||||
\title Detach the help window
|
||||
|
||||
By default, context-sensitive help is opened in a window next to the
|
||||
By default, context-sensitive help opens in a window next to the
|
||||
code editor when you press \key F1. If there is not enough vertical
|
||||
space, the help opens in the full-screen help mode.
|
||||
|
||||
\image qtcreator-context-sensitive-help.png {Context-sensitive help in Edit mode}
|
||||
|
||||
To specify that the help always opens in full-screen mode or
|
||||
is detached to an external window, select \preferences > \uicontrol Help >
|
||||
\uicontrol General.
|
||||
To specify that the help always opens in full-screen mode or in an external
|
||||
window, go to \preferences > \uicontrol Help > \uicontrol General.
|
||||
|
||||
\image qtcreator-preferences-help-general.webp {General tab in Help preferences}
|
||||
|
||||
Set preferences for displaying context-sensitive help
|
||||
in the \uicontrol {On context help} field. To detach the help window, select
|
||||
Set preferences for viewing context-sensitive help
|
||||
in \uicontrol {On context help}. To detach the help window, select
|
||||
\uicontrol {Always Show in External Window}.
|
||||
|
||||
To change this setting in a help view, select the \inlineimage icons/linkicon.png
|
||||
toolbar button.
|
||||
To change this setting in a help view, select \inlineimage icons/linkicon.png.
|
||||
|
||||
\sa {Get help}
|
||||
\sa {Read Documentation}{How To: Read Documentation}
|
||||
*/
|
||||
|
||||
/*!
|
||||
@@ -315,27 +315,27 @@
|
||||
|
||||
\title Select the help start page
|
||||
|
||||
You can select the page to display when you open the \uicontrol Help mode in the
|
||||
\preferences > \uicontrol Help > \uicontrol General >
|
||||
\uicontrol {On help start} field.
|
||||
To set the page to show when you open the \uicontrol Help mode, go to
|
||||
\preferences > \uicontrol Help > \uicontrol General and select
|
||||
\uicontrol {On help start}.
|
||||
|
||||
\image qtcreator-preferences-help-general.webp {General tab in Help preferences}
|
||||
|
||||
\list
|
||||
\li To display the page and help views that were open when you exited the mode,
|
||||
\li To show the page and help views that were open when you exited the mode,
|
||||
select the \uicontrol {Show My Tabs from Last Session} option. However, Web pages
|
||||
are not opened because loading them would slow down opening the \uicontrol Help
|
||||
mode.
|
||||
|
||||
\li To display a particular page, select \uicontrol {Show My Home Page}, and specify
|
||||
the page in the \uicontrol {Home Page} field.
|
||||
\li To show a particular page, select \uicontrol {Show My Home Page}, and specify
|
||||
the page in \uicontrol {Home Page}.
|
||||
|
||||
\li To display a blank page, select the \uicontrol {Show a Blank Page} option. You can
|
||||
also select the \uicontrol {Use Blank Page} button to set a blank page as your
|
||||
\li To show a blank page, select the \uicontrol {Show a Blank Page} option.
|
||||
Select \uicontrol {Use Blank Page} to set a blank page as your
|
||||
home page.
|
||||
\endlist
|
||||
|
||||
\sa {Get help}
|
||||
\sa {Read Documentation}{How To: Read Documentation}
|
||||
*/
|
||||
|
||||
/*!
|
||||
@@ -350,40 +350,40 @@
|
||||
|
||||
\title Filter documentation
|
||||
|
||||
You can filter the documents displayed in the \uicontrol Help mode to find
|
||||
Filter the documents in the \uicontrol Help mode to find
|
||||
relevant information faster. Select a filter from a list of filters. The
|
||||
contents of the \uicontrol Index and \uicontrol Contents
|
||||
view in the sidebar change accordingly.
|
||||
|
||||
\image qtcreator-help-filters.png {Filters field on the Help mode toolbar}
|
||||
\image qtcreator-help-filters.png {Filters on the Help mode toolbar}
|
||||
|
||||
\section1 Add filters
|
||||
|
||||
You can define your own filters to display documentation for a set of
|
||||
Define your own filters to show documentation for a set of
|
||||
Qt modules and versions.
|
||||
|
||||
To add filters:
|
||||
|
||||
\list 1
|
||||
|
||||
\li Select \preferences > \uicontrol Help > \uicontrol Filters.
|
||||
\li Go to \preferences > \uicontrol Help > \uicontrol Filters.
|
||||
|
||||
\image qtcreator-help-filter-attributes.png {Filters tab in Help preferences}
|
||||
|
||||
\li Select \inlineimage icons/plus.png
|
||||
to add a new filter in the \uicontrol {Add Filter} dialog.
|
||||
|
||||
\li In the \uicontrol {Filter name} field, enter a name for the filter,
|
||||
\li In \uicontrol {Filter name}, enter a name for the filter,
|
||||
and then select \uicontrol {OK} to return to the \uicontrol Filters
|
||||
tab.
|
||||
|
||||
\li In the \uicontrol Components field, select the Qt modules to include
|
||||
\li In \uicontrol Components, select the Qt modules to include
|
||||
in the filter.
|
||||
|
||||
\li In the \uicontrol Versions field, select the Qt versions to include
|
||||
\li In \uicontrol Versions, select the Qt versions to include
|
||||
in the filter.
|
||||
|
||||
\li Click \uicontrol OK.
|
||||
\li Select \uicontrol OK.
|
||||
|
||||
\li In the \uicontrol Help mode, select the filter in the list of
|
||||
filters to see the filtered documentation in the sidebar.
|
||||
@@ -392,15 +392,14 @@
|
||||
|
||||
\section1 Change filters
|
||||
|
||||
To modify the selected filter, add and remove Qt modules and versions, and
|
||||
To modify the selected filter, add and remove Qt modules and versions and
|
||||
then select \uicontrol Apply.
|
||||
|
||||
To rename the selected filter, select \uicontrol Rename.
|
||||
|
||||
\section1 Remove filters
|
||||
|
||||
To remove the selected filter select \inlineimage icons/minus.png
|
||||
.
|
||||
To remove the selected filter, select \inlineimage icons/minus.png.
|
||||
|
||||
\sa {Get help}
|
||||
\sa {Read Documentation}{How To: Read Documentation}
|
||||
*/
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
\ingroup creator-how-to-projects-configure
|
||||
\ingroup creator-how-to-manage-kits
|
||||
\ingroup creator-how-to-projects
|
||||
|
||||
\title Open projects
|
||||
|
||||
@@ -49,6 +50,15 @@
|
||||
\endlist
|
||||
\endlist
|
||||
|
||||
\section1 Open directories as projects
|
||||
|
||||
To open a directory as a project, go to \uicontrol File >
|
||||
\uicontrol {Open Workspace}.
|
||||
|
||||
\QC generates the \e .qtcreator/project.json project file in the directory
|
||||
for setting a project name and file exclusion filters. You can open either
|
||||
the JSON file or the workspace to open the project the next time.
|
||||
|
||||
\section1 Re-configure projects
|
||||
|
||||
\QC stores information that it needs to build projects in a .user file. If
|
||||
|
||||
@@ -637,8 +637,8 @@
|
||||
\li Use Utils::FilePath for any QString that semantically is a file or directory,
|
||||
see also \l{Passing File Names}.
|
||||
\li Prefer using Utils::FilePath over any use of QDir and QFileInfo.
|
||||
\li Prefer using Utils::QtcProcess over QProcess.
|
||||
\li If Utils::FilePath or Utils::QtcProcess functionality is not sufficient
|
||||
\li Prefer using Utils::Process over QProcess.
|
||||
\li If Utils::FilePath or Utils::Process functionality is not sufficient
|
||||
for your purpose, prefer enhancing them over falling back to QString
|
||||
or QProcess.
|
||||
\li Avoid platform #ifdefs unless they are absolutely needed for locally
|
||||
|
||||
@@ -187,7 +187,7 @@ class Dumper(DumperBase):
|
||||
self.nativeTypeEnumDisplay(nativeType, intval, form)
|
||||
return typeid
|
||||
|
||||
def listNativeValueChildren(self, nativeValue: cdbext.Value, include_bases: bool) -> list[DumperBase.Value]:
|
||||
def listNativeValueChildren(self, nativeValue: cdbext.Value, include_bases: bool):
|
||||
fields = []
|
||||
index = 0
|
||||
nativeMember = nativeValue.childFromIndex(index)
|
||||
@@ -202,13 +202,13 @@ class Dumper(DumperBase):
|
||||
nativeMember = nativeValue.childFromIndex(index)
|
||||
return fields
|
||||
|
||||
def listValueChildren(self, value: DumperBase.Value, include_bases=True) -> list[DumperBase.Value]:
|
||||
def listValueChildren(self, value: DumperBase.Value, include_bases=True):
|
||||
nativeValue = value.nativeValue
|
||||
if nativeValue is None:
|
||||
nativeValue = cdbext.createValue(value.address(), self.lookupNativeType(value.type.name, 0))
|
||||
return self.listNativeValueChildren(nativeValue, include_bases)
|
||||
|
||||
def nativeListMembers(self, value: DumperBase.Value, native_type: cdbext.Type, include_bases: bool) -> list[DumperBase.Value]:
|
||||
def nativeListMembers(self, value: DumperBase.Value, native_type: cdbext.Type, include_bases: bool):
|
||||
nativeValue = value.nativeValue
|
||||
if nativeValue is None:
|
||||
nativeValue = cdbext.createValue(value.address(), native_type)
|
||||
@@ -629,100 +629,6 @@ class Dumper(DumperBase):
|
||||
if self.showQObjectNames:
|
||||
self.tryPutQObjectGuts(value)
|
||||
|
||||
|
||||
def putFormattedPointerX(self, value: DumperBase.Value):
|
||||
self.putOriginalAddress(value.address())
|
||||
pointer = value.pointer()
|
||||
self.putAddress(pointer)
|
||||
if pointer == 0:
|
||||
self.putType(value.type)
|
||||
self.putValue('0x0')
|
||||
return
|
||||
|
||||
typeName = value.type.name
|
||||
|
||||
try:
|
||||
self.readRawMemory(pointer, 1)
|
||||
except:
|
||||
# Failure to dereference a pointer should at least
|
||||
# show the value of a pointer.
|
||||
#DumperBase.warn('BAD POINTER: %s' % value)
|
||||
self.putValue('0x%x' % pointer)
|
||||
self.putType(typeName)
|
||||
return
|
||||
|
||||
if self.currentIName.endswith('.this'):
|
||||
self.putDerefedPointer(value)
|
||||
return
|
||||
|
||||
displayFormat = self.currentItemFormat(value.type.name)
|
||||
|
||||
if value.type.targetName == 'void':
|
||||
#DumperBase.warn('VOID POINTER: %s' % displayFormat)
|
||||
self.putType(typeName)
|
||||
self.putSymbolValue(pointer)
|
||||
return
|
||||
|
||||
if displayFormat == DisplayFormat.Raw:
|
||||
# Explicitly requested bald pointer.
|
||||
#DumperBase.warn('RAW')
|
||||
self.putType(typeName)
|
||||
self.putValue('0x%x' % pointer)
|
||||
self.putExpandable()
|
||||
if self.currentIName in self.expandedINames:
|
||||
with Children(self):
|
||||
with SubItem(self, '*'):
|
||||
self.putItem(value.dereference())
|
||||
return
|
||||
|
||||
limit = self.displayStringLimit
|
||||
if displayFormat in (DisplayFormat.SeparateLatin1String, DisplayFormat.SeparateUtf8String):
|
||||
limit = 1000000
|
||||
if self.tryPutSimpleFormattedPointer(pointer, typeName,
|
||||
value.type.targetName, displayFormat, limit):
|
||||
self.putExpandable()
|
||||
return
|
||||
|
||||
if DisplayFormat.Array10 <= displayFormat and displayFormat <= DisplayFormat.Array10000:
|
||||
n = (10, 100, 1000, 10000)[displayFormat - DisplayFormat.Array10]
|
||||
self.putType(typeName)
|
||||
self.putItemCount(n)
|
||||
self.putArrayData(value.pointer(), n, value.type.targetName)
|
||||
return
|
||||
|
||||
#DumperBase.warn('AUTODEREF: %s' % self.autoDerefPointers)
|
||||
#DumperBase.warn('INAME: %s' % self.currentIName)
|
||||
if self.autoDerefPointers:
|
||||
# Generic pointer type with AutomaticFormat, but never dereference char types:
|
||||
if value.type.targetName.strip() not in (
|
||||
'char',
|
||||
'signed char',
|
||||
'int8_t',
|
||||
'qint8',
|
||||
'unsigned char',
|
||||
'uint8_t',
|
||||
'quint8',
|
||||
'wchar_t',
|
||||
'CHAR',
|
||||
'WCHAR',
|
||||
'char8_t',
|
||||
'char16_t',
|
||||
'char32_t'
|
||||
):
|
||||
self.putDerefedPointer(value)
|
||||
return
|
||||
|
||||
#DumperBase.warn('GENERIC PLAIN POINTER: %s' % value.type)
|
||||
#DumperBase.warn('ADDR PLAIN POINTER: 0x%x' % value.laddress)
|
||||
self.putType(typeName)
|
||||
self.putSymbolValue(pointer)
|
||||
self.putExpandable()
|
||||
if self.currentIName in self.expandedINames:
|
||||
with Children(self):
|
||||
with SubItem(self, '*'):
|
||||
self.putItem(value.dereference())
|
||||
|
||||
|
||||
def putCStyleArray(self, value: DumperBase.Value):
|
||||
arrayType = value.type
|
||||
innerType = arrayType.target()
|
||||
|
||||
@@ -1295,8 +1295,6 @@ class DumperBase():
|
||||
innerType = arrayType.target()
|
||||
#self.warn("ARRAY TYPE: %s" % arrayType)
|
||||
#self.warn("INNER TYPE: %s" % innerType)
|
||||
if innerType is None:
|
||||
innerType = value.type.target()
|
||||
|
||||
address = value.address()
|
||||
if address:
|
||||
|
||||
@@ -680,11 +680,18 @@ int main(int argc, char **argv)
|
||||
setPixmapCacheLimit();
|
||||
loadFonts();
|
||||
|
||||
// On 100% or 200% scaling we can use the default 'Vista' style on Windows
|
||||
if (Utils::HostOsInfo::isWindowsHost() && !hasStyleOption) {
|
||||
// The Windows 11 default style (Qt 6.7) has major issues, therefore
|
||||
// set the previous default style: "windowsvista"
|
||||
// FIXME: check newer Qt Versions
|
||||
QApplication::setStyle(QLatin1String("windowsvista"));
|
||||
|
||||
// On scaling different than 100% or 200% use the "fusion" style
|
||||
qreal tmp;
|
||||
const bool fractionalDpi = !qFuzzyIsNull(std::modf(qApp->devicePixelRatio(), &tmp));
|
||||
if (Utils::HostOsInfo::isWindowsHost() && fractionalDpi && !hasStyleOption)
|
||||
if (fractionalDpi)
|
||||
QApplication::setStyle(QLatin1String("fusion"));
|
||||
}
|
||||
|
||||
const int threadCount = QThreadPool::globalInstance()->maxThreadCount();
|
||||
QThreadPool::globalInstance()->setMaxThreadCount(qMax(4, 2 * threadCount));
|
||||
|
||||
@@ -89,8 +89,8 @@ static const QIcon &icon(IconIndex icon)
|
||||
class PluginItem : public TreeItem
|
||||
{
|
||||
public:
|
||||
PluginItem(PluginSpec *spec, PluginView *view)
|
||||
: m_spec(spec), m_view(view)
|
||||
PluginItem(PluginSpec *spec, PluginData *data)
|
||||
: m_spec(spec), m_data(data)
|
||||
{}
|
||||
|
||||
QVariant data(int column, int role) const override
|
||||
@@ -167,7 +167,7 @@ public:
|
||||
bool setData(int column, const QVariant &data, int role) override
|
||||
{
|
||||
if (column == LoadedColumn && role == Qt::CheckStateRole)
|
||||
return m_view->setPluginsEnabled({m_spec}, data.toBool());
|
||||
return m_data->setPluginsEnabled({m_spec}, data.toBool());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -193,19 +193,19 @@ public:
|
||||
|
||||
public:
|
||||
PluginSpec *m_spec; // Not owned.
|
||||
PluginView *m_view; // Not owned.
|
||||
PluginData *m_data; // Not owned.
|
||||
};
|
||||
|
||||
class CollectionItem : public TreeItem
|
||||
{
|
||||
public:
|
||||
CollectionItem(const QString &name, const PluginSpecs &plugins, PluginView *view)
|
||||
CollectionItem(const QString &name, const PluginSpecs &plugins, PluginData *data)
|
||||
: m_name(name)
|
||||
, m_plugins(plugins)
|
||||
, m_view(view)
|
||||
, m_data(data)
|
||||
{
|
||||
for (PluginSpec *spec : plugins)
|
||||
appendChild(new PluginItem(spec, view));
|
||||
appendChild(new PluginItem(spec, data));
|
||||
}
|
||||
|
||||
QVariant data(int column, int role) const override
|
||||
@@ -241,7 +241,7 @@ public:
|
||||
if (column == LoadedColumn && role == Qt::CheckStateRole) {
|
||||
const PluginSpecs affectedPlugins
|
||||
= Utils::filtered(m_plugins, [](PluginSpec *spec) { return !spec->isRequired(); });
|
||||
if (m_view->setPluginsEnabled(toSet(affectedPlugins), data.toBool())) {
|
||||
if (m_data->setPluginsEnabled(toSet(affectedPlugins), data.toBool())) {
|
||||
update();
|
||||
return true;
|
||||
}
|
||||
@@ -260,19 +260,31 @@ public:
|
||||
public:
|
||||
QString m_name;
|
||||
const PluginSpecs m_plugins;
|
||||
PluginView *m_view; // Not owned.
|
||||
PluginData *m_data; // Not owned.
|
||||
};
|
||||
|
||||
} // Internal
|
||||
|
||||
using namespace ExtensionSystem::Internal;
|
||||
|
||||
PluginData::PluginData(QWidget *parent, PluginView *owner)
|
||||
: m_parent(parent), m_pluginView(owner)
|
||||
{
|
||||
m_model = new TreeModel<TreeItem, CollectionItem, PluginItem>(parent);
|
||||
m_model->setHeader({ Tr::tr("Name"), Tr::tr("Load"), Tr::tr("Version"), Tr::tr("Vendor") });
|
||||
|
||||
m_sortModel = new CategorySortFilterModel(parent);
|
||||
m_sortModel->setSourceModel(m_model);
|
||||
m_sortModel->setSortRole(SortRole);
|
||||
m_sortModel->setFilterKeyColumn(-1/*all*/);
|
||||
}
|
||||
|
||||
/*!
|
||||
Constructs a plugin view with \a parent that displays a list of plugins
|
||||
from a plugin manager.
|
||||
*/
|
||||
PluginView::PluginView(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
: QWidget(parent), m_data(this, this)
|
||||
{
|
||||
m_categoryView = new TreeView(this);
|
||||
m_categoryView->setAlternatingRowColors(true);
|
||||
@@ -282,14 +294,7 @@ PluginView::PluginView(QWidget *parent)
|
||||
m_categoryView->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
m_categoryView->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
|
||||
m_model = new TreeModel<TreeItem, CollectionItem, PluginItem>(this);
|
||||
m_model->setHeader({ Tr::tr("Name"), Tr::tr("Load"), Tr::tr("Version"), Tr::tr("Vendor") });
|
||||
|
||||
m_sortModel = new CategorySortFilterModel(this);
|
||||
m_sortModel->setSourceModel(m_model);
|
||||
m_sortModel->setSortRole(SortRole);
|
||||
m_sortModel->setFilterKeyColumn(-1/*all*/);
|
||||
m_categoryView->setModel(m_sortModel);
|
||||
m_categoryView->setModel(m_data.m_sortModel);
|
||||
|
||||
auto *gridLayout = new QGridLayout(this);
|
||||
gridLayout->setContentsMargins(2, 2, 2, 2);
|
||||
@@ -329,7 +334,7 @@ PluginSpec *PluginView::currentPlugin() const
|
||||
*/
|
||||
void PluginView::setFilter(const QString &filter)
|
||||
{
|
||||
m_sortModel->setFilterRegularExpression(
|
||||
m_data.m_sortModel->setFilterRegularExpression(
|
||||
QRegularExpression(QRegularExpression::escape(filter),
|
||||
QRegularExpression::CaseInsensitiveOption));
|
||||
m_categoryView->expandAll();
|
||||
@@ -337,15 +342,15 @@ void PluginView::setFilter(const QString &filter)
|
||||
|
||||
PluginSpec *PluginView::pluginForIndex(const QModelIndex &index) const
|
||||
{
|
||||
const QModelIndex &sourceIndex = m_sortModel->mapToSource(index);
|
||||
PluginItem *item = m_model->itemForIndexAtLevel<2>(sourceIndex);
|
||||
const QModelIndex &sourceIndex = m_data.m_sortModel->mapToSource(index);
|
||||
PluginItem *item = m_data.m_model->itemForIndexAtLevel<2>(sourceIndex);
|
||||
return item ? item->m_spec: nullptr;
|
||||
}
|
||||
|
||||
void PluginView::updatePlugins()
|
||||
{
|
||||
// Model.
|
||||
m_model->clear();
|
||||
m_data.m_model->clear();
|
||||
|
||||
const QHash<QString, PluginSpecs> pluginCollections
|
||||
= PluginManager::pluginCollections();
|
||||
@@ -353,17 +358,22 @@ void PluginView::updatePlugins()
|
||||
const auto end = pluginCollections.cend();
|
||||
for (auto it = pluginCollections.cbegin(); it != end; ++it) {
|
||||
const QString name = it.key().isEmpty() ? Tr::tr("Utilities") : it.key();
|
||||
collections.push_back(new CollectionItem(name, it.value(), this));
|
||||
collections.push_back(new CollectionItem(name, it.value(), &m_data));
|
||||
}
|
||||
Utils::sort(collections, &CollectionItem::m_name);
|
||||
|
||||
for (CollectionItem *collection : std::as_const(collections))
|
||||
m_model->rootItem()->appendChild(collection);
|
||||
m_data.m_model->rootItem()->appendChild(collection);
|
||||
|
||||
emit m_model->layoutChanged();
|
||||
emit m_data.m_model->layoutChanged();
|
||||
m_categoryView->expandAll();
|
||||
}
|
||||
|
||||
PluginData &PluginView::data()
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
static QString pluginListString(const QSet<PluginSpec *> &plugins)
|
||||
{
|
||||
QStringList names = Utils::transform<QList>(plugins, &PluginSpec::name);
|
||||
@@ -371,7 +381,7 @@ static QString pluginListString(const QSet<PluginSpec *> &plugins)
|
||||
return names.join(QLatin1Char('\n'));
|
||||
}
|
||||
|
||||
bool PluginView::setPluginsEnabled(const QSet<PluginSpec *> &plugins, bool enable)
|
||||
bool PluginData::setPluginsEnabled(const QSet<PluginSpec *> &plugins, bool enable)
|
||||
{
|
||||
QSet<PluginSpec *> additionalPlugins;
|
||||
if (enable) {
|
||||
@@ -383,7 +393,7 @@ bool PluginView::setPluginsEnabled(const QSet<PluginSpec *> &plugins, bool enabl
|
||||
}
|
||||
additionalPlugins.subtract(plugins);
|
||||
if (!additionalPlugins.isEmpty()) {
|
||||
if (QMessageBox::question(this, Tr::tr("Enabling Plugins"),
|
||||
if (QMessageBox::question(m_parent, Tr::tr("Enabling Plugins"),
|
||||
Tr::tr("Enabling\n%1\nwill also enable the following plugins:\n\n%2")
|
||||
.arg(pluginListString(plugins), pluginListString(additionalPlugins)),
|
||||
QMessageBox::Ok | QMessageBox::Cancel,
|
||||
@@ -400,7 +410,7 @@ bool PluginView::setPluginsEnabled(const QSet<PluginSpec *> &plugins, bool enabl
|
||||
}
|
||||
additionalPlugins.subtract(plugins);
|
||||
if (!additionalPlugins.isEmpty()) {
|
||||
if (QMessageBox::question(this, Tr::tr("Disabling Plugins"),
|
||||
if (QMessageBox::question(m_parent, Tr::tr("Disabling Plugins"),
|
||||
Tr::tr("Disabling\n%1\nwill also disable the following plugins:\n\n%2")
|
||||
.arg(pluginListString(plugins), pluginListString(additionalPlugins)),
|
||||
QMessageBox::Ok | QMessageBox::Cancel,
|
||||
@@ -422,13 +432,16 @@ bool PluginView::setPluginsEnabled(const QSet<PluginSpec *> &plugins, bool enabl
|
||||
item->updateColumn(LoadedColumn);
|
||||
item->parent()->updateColumn(LoadedColumn);
|
||||
}
|
||||
emit pluginsChanged(affectedPlugins, enable);
|
||||
|
||||
if (m_pluginView)
|
||||
emit m_pluginView->pluginsChanged(affectedPlugins, enable);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PluginView::cancelChanges()
|
||||
{
|
||||
for (auto element : m_affectedPlugins)
|
||||
for (auto element : m_data.m_affectedPlugins)
|
||||
element.first->setEnabledBySettings(element.second);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,12 +22,32 @@ class TreeView;
|
||||
namespace ExtensionSystem {
|
||||
|
||||
class PluginSpec;
|
||||
class PluginView;
|
||||
|
||||
namespace Internal {
|
||||
class CollectionItem;
|
||||
class PluginItem;
|
||||
} // Internal
|
||||
|
||||
class EXTENSIONSYSTEM_EXPORT PluginData
|
||||
{
|
||||
public:
|
||||
explicit PluginData(QWidget *parent, PluginView *pluginView = nullptr);
|
||||
|
||||
bool setPluginsEnabled(const QSet<PluginSpec *> &plugins, bool enable);
|
||||
|
||||
private:
|
||||
QWidget *m_parent = nullptr;
|
||||
PluginView *m_pluginView = nullptr;
|
||||
Utils::TreeModel<Utils::TreeItem, Internal::CollectionItem, Internal::PluginItem> *m_model;
|
||||
Utils::CategorySortFilterModel *m_sortModel;
|
||||
std::unordered_map<PluginSpec *, bool> m_affectedPlugins;
|
||||
|
||||
friend class Internal::CollectionItem;
|
||||
friend class Internal::PluginItem;
|
||||
friend class PluginView;
|
||||
};
|
||||
|
||||
class EXTENSIONSYSTEM_EXPORT PluginView : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -40,6 +60,8 @@ public:
|
||||
void setFilter(const QString &filter);
|
||||
void cancelChanges();
|
||||
|
||||
PluginData &data();
|
||||
|
||||
signals:
|
||||
void currentPluginChanged(ExtensionSystem::PluginSpec *spec);
|
||||
void pluginActivated(ExtensionSystem::PluginSpec *spec);
|
||||
@@ -48,15 +70,9 @@ signals:
|
||||
private:
|
||||
PluginSpec *pluginForIndex(const QModelIndex &index) const;
|
||||
void updatePlugins();
|
||||
bool setPluginsEnabled(const QSet<PluginSpec *> &plugins, bool enable);
|
||||
|
||||
PluginData m_data;
|
||||
Utils::TreeView *m_categoryView;
|
||||
Utils::TreeModel<Utils::TreeItem, Internal::CollectionItem, Internal::PluginItem> *m_model;
|
||||
Utils::CategorySortFilterModel *m_sortModel;
|
||||
std::unordered_map<PluginSpec *, bool> m_affectedPlugins;
|
||||
|
||||
friend class Internal::CollectionItem;
|
||||
friend class Internal::PluginItem;
|
||||
};
|
||||
|
||||
} // namespace ExtensionSystem
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <QtCore/QTime>
|
||||
#include <QtCore/QTimer>
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
using namespace std::chrono;
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
@@ -1298,11 +1299,11 @@ const void *Loop::valuePtr() const
|
||||
|
||||
using StoragePtr = void *;
|
||||
|
||||
static QString s_activeStorageWarning =
|
||||
static constexpr QLatin1StringView s_activeStorageWarning =
|
||||
"The referenced storage is not reachable in the running tree. "
|
||||
"A nullptr will be returned which might lead to a crash in the calling code. "
|
||||
"It is possible that no storage was added to the tree, "
|
||||
"or the storage is not reachable from where it is referenced.";
|
||||
"or the storage is not reachable from where it is referenced."_L1;
|
||||
|
||||
class StorageThreadData
|
||||
{
|
||||
|
||||
@@ -211,7 +211,7 @@ QIcon Icon::sideBarIcon(const Icon &classic, const Icon &flat)
|
||||
}
|
||||
|
||||
QIcon Icon::modeIcon(const Icon &classic, const Icon &flat,
|
||||
[[__maybe_unused__]] const Icon &flatActive)
|
||||
[[maybe_unused]] const Icon &flatActive)
|
||||
{
|
||||
QIcon result = sideBarIcon(classic, flat);
|
||||
return result;
|
||||
|
||||
@@ -41,6 +41,11 @@ enum class Channel {
|
||||
Error
|
||||
};
|
||||
|
||||
enum class DetachedChannelMode {
|
||||
Forward,
|
||||
Discard
|
||||
};
|
||||
|
||||
enum class TextChannelMode {
|
||||
// Keep | Emit | Emit
|
||||
// raw | text | content
|
||||
|
||||
@@ -1317,12 +1317,18 @@ void Process::closeWriteChannel()
|
||||
d->sendControlSignal(ControlSignal::CloseWriteChannel);
|
||||
}
|
||||
|
||||
bool Process::startDetached(const CommandLine &cmd, const FilePath &workingDirectory, qint64 *pid)
|
||||
bool Process::startDetached(const CommandLine &cmd, const FilePath &workingDirectory,
|
||||
DetachedChannelMode channelMode, qint64 *pid)
|
||||
{
|
||||
return QProcess::startDetached(cmd.executable().toUserOutput(),
|
||||
cmd.splitArguments(),
|
||||
workingDirectory.toUserOutput(),
|
||||
pid);
|
||||
QProcess process;
|
||||
process.setProgram(cmd.executable().toUserOutput());
|
||||
process.setArguments(cmd.splitArguments());
|
||||
process.setWorkingDirectory(workingDirectory.toUserOutput());
|
||||
if (channelMode == DetachedChannelMode::Discard) {
|
||||
process.setStandardOutputFile(QProcess::nullDevice());
|
||||
process.setStandardErrorFile(QProcess::nullDevice());
|
||||
}
|
||||
return process.startDetached(pid);
|
||||
}
|
||||
|
||||
void Process::setLowPriority()
|
||||
|
||||
@@ -137,6 +137,7 @@ public:
|
||||
// Some of them could be aggregated in another public utils class.
|
||||
|
||||
static bool startDetached(const CommandLine &cmd, const FilePath &workingDirectory = {},
|
||||
DetachedChannelMode channelMode = DetachedChannelMode::Forward,
|
||||
qint64 *pid = nullptr);
|
||||
|
||||
// Starts the command and waits for finish.
|
||||
|
||||
@@ -974,8 +974,12 @@ expected_str<void> createAvd(const CreateAvdInfo &info, bool force)
|
||||
|
||||
GuardLocker locker(s_instance->m_avdPathGuard);
|
||||
process.runBlocking();
|
||||
if (process.result() != ProcessResult::FinishedWithSuccess)
|
||||
return Utils::make_unexpected(process.exitMessage());
|
||||
if (process.result() != ProcessResult::FinishedWithSuccess) {
|
||||
const QString stdErr = process.stdErr();
|
||||
const QString errorMessage = stdErr.isEmpty() ? process.exitMessage()
|
||||
: process.exitMessage() + "\n\n" + stdErr;
|
||||
return Utils::make_unexpected(errorMessage);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -55,12 +55,12 @@ static std::optional<QDomElement> documentElement(const FilePath &fileName)
|
||||
{
|
||||
QFile file(fileName.toString());
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
MessageManager::writeDisrupting(Tr::tr("Cannot open: %1").arg(fileName.toUserOutput()));
|
||||
MessageManager::writeDisrupting(Tr::tr("Cannot open \"%1\".").arg(fileName.toUserOutput()));
|
||||
return {};
|
||||
}
|
||||
QDomDocument doc;
|
||||
if (!doc.setContent(file.readAll())) {
|
||||
MessageManager::writeDisrupting(Tr::tr("Cannot parse: %1").arg(fileName.toUserOutput()));
|
||||
MessageManager::writeDisrupting(Tr::tr("Cannot parse \"%1\".").arg(fileName.toUserOutput()));
|
||||
return {};
|
||||
}
|
||||
return doc.documentElement();
|
||||
|
||||
@@ -313,7 +313,7 @@ static GroupItem updateRecipe(const Storage<DialogStorage> &dialogStorage)
|
||||
const QStringList args = {"--update", sdkRootArg()};
|
||||
QuestionProgressDialog *dialog = dialogStorage->m_dialog.get();
|
||||
setupSdkProcess(args, &process, dialog, 0, 1);
|
||||
dialog->appendMessage(Tr::tr("Updating installed packages....") + '\n', NormalMessageFormat);
|
||||
dialog->appendMessage(Tr::tr("Updating installed packages...") + '\n', NormalMessageFormat);
|
||||
dialog->setProgress(0);
|
||||
};
|
||||
const auto onDone = [dialogStorage](DoneWith result) {
|
||||
|
||||
@@ -38,7 +38,8 @@ AppStatisticsMonitorChart::AppStatisticsMonitorChart(
|
||||
m_chartView->setMinimumHeight(200);
|
||||
m_chartView->setMinimumWidth(400);
|
||||
const QBrush brushTitle(creatorColor(Theme::Token_Text_Muted));
|
||||
const QBrush brush(creatorColor(Theme::Token_Background_Default));
|
||||
// const QBrush brush(creatorColor(Theme::Token_Background_Default)); left for the future
|
||||
const QBrush brush(creatorColor(Theme::BackgroundColorNormal));
|
||||
const QPen penBack(creatorColor(Theme::Token_Text_Muted));
|
||||
const QPen penAxis(creatorColor(Theme::Token_Text_Muted));
|
||||
|
||||
@@ -160,8 +161,8 @@ void Chart::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
Q_UNUSED(event);
|
||||
QPainter painter(this);
|
||||
|
||||
painter.fillRect(rect(), creatorColor(Theme::Token_Background_Default));
|
||||
// painter.fillRect(rect(), creatorColor(Theme::Token_Background_Default)); left for the future
|
||||
painter.fillRect(rect(), creatorColor(Theme::BackgroundColorNormal));
|
||||
|
||||
// add the name of the chart in the middle of the widget width and on the top
|
||||
painter.drawText(
|
||||
|
||||
@@ -69,7 +69,7 @@ ProjectTestSettingsWidget::ProjectTestSettingsWidget(Project *project)
|
||||
m_pathFilters = new QTreeWidget;
|
||||
m_pathFilters->setHeaderHidden(true);
|
||||
m_pathFilters->setRootIsDecorated(false);
|
||||
QLabel *filterLabel = new QLabel(Tr::tr("Wildcard expressions for filtering"), this);
|
||||
QLabel *filterLabel = new QLabel(Tr::tr("Wildcard expressions for filtering:"), this);
|
||||
QPushButton *addFilter = new QPushButton(Tr::tr("Add"), this);
|
||||
QPushButton *removeFilter = new QPushButton(Tr::tr("Remove"), this);
|
||||
removeFilter->setEnabled(false);
|
||||
|
||||
@@ -35,7 +35,7 @@ TestSettings::TestSettings()
|
||||
|
||||
useTimeout.setSettingsKey("UseTimeout");
|
||||
useTimeout.setDefaultValue(false);
|
||||
useTimeout.setLabelText(Tr::tr("Timeout"));
|
||||
useTimeout.setLabelText(Tr::tr("Timeout:"));
|
||||
useTimeout.setToolTip(Tr::tr("Use a timeout while executing test cases."));
|
||||
|
||||
timeout.setSettingsKey("Timeout");
|
||||
|
||||
@@ -879,17 +879,17 @@ public:
|
||||
dashboardUrl.setQuery(search.toUrlQuery(QueryMode::FilterQuery));
|
||||
|
||||
QMenu *menu = new QMenu;
|
||||
auto action = new QAction(Tr::tr("Open issue in Dashboard"), menu);
|
||||
auto action = new QAction(Tr::tr("Open Issue in Dashboard"), menu);
|
||||
menu->addAction(action);
|
||||
QObject::connect(action, &QAction::triggered, menu, [issueBaseUrl] {
|
||||
QDesktopServices::openUrl(issueBaseUrl);
|
||||
});
|
||||
action = new QAction(Tr::tr("Open table in Dashboard"), menu);
|
||||
action = new QAction(Tr::tr("Open Table in Dashboard"), menu);
|
||||
QObject::connect(action, &QAction::triggered, menu, [dashboardUrl] {
|
||||
QDesktopServices::openUrl(dashboardUrl);
|
||||
});
|
||||
menu->addAction(action);
|
||||
action = new QAction(Tr::tr("Copy Dashboard link to clipboard"), menu);
|
||||
action = new QAction(Tr::tr("Copy Dashboard Link to Clipboard"), menu);
|
||||
QObject::connect(action, &QAction::triggered, menu, [dashboardUrl] {
|
||||
if (auto clipboard = QGuiApplication::clipboard())
|
||||
clipboard->setText(dashboardUrl.toString());
|
||||
|
||||
@@ -308,16 +308,17 @@ AxivionSettingsWidget::AxivionSettingsWidget()
|
||||
auto addButton = new QPushButton(Tr::tr("Add..."), this);
|
||||
m_edit = new QPushButton(Tr::tr("Edit..."), this);
|
||||
m_remove = new QPushButton(Tr::tr("Remove"), this);
|
||||
Column {
|
||||
Row {
|
||||
Form {
|
||||
Tr::tr("Default dashboard server"), m_dashboardServers, br
|
||||
}, st,
|
||||
Column { addButton, m_edit, st, m_remove },
|
||||
Column{
|
||||
Row{
|
||||
Form{Tr::tr("Default dashboard server:"), m_dashboardServers, br},
|
||||
st,
|
||||
Column{addButton, m_edit, st, m_remove},
|
||||
},
|
||||
Space(10), br,
|
||||
Row { settings().highlightMarks }, st
|
||||
}.attachTo(this);
|
||||
Space(10),
|
||||
br,
|
||||
Row{settings().highlightMarks},
|
||||
st}
|
||||
.attachTo(this);
|
||||
|
||||
connect(addButton, &QPushButton::clicked, this, [this] {
|
||||
// add an empty item unconditionally
|
||||
@@ -357,9 +358,10 @@ void AxivionSettingsWidget::updateEnabledStates()
|
||||
void AxivionSettingsWidget::removeCurrentServerConfig()
|
||||
{
|
||||
const QString config = m_dashboardServers->currentData().value<AxivionServer>().displayString();
|
||||
if (QMessageBox::question(ICore::dialogParent(), Tr::tr("Remove Server Configuration"),
|
||||
Tr::tr("Do you really want to remove the server configuration "
|
||||
"\"%1\"?").arg(config))
|
||||
if (QMessageBox::question(
|
||||
ICore::dialogParent(),
|
||||
Tr::tr("Remove Server Configuration"),
|
||||
Tr::tr("Remove the server configuration \"%1\"?").arg(config))
|
||||
!= QMessageBox::Yes) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ void DeviceDetector::handleDeviceEvent(QdbDeviceTracker::DeviceEventType eventTy
|
||||
DeviceManager * const dm = DeviceManager::instance();
|
||||
|
||||
if (eventType == QdbDeviceTracker::NewDevice) {
|
||||
const QString name = Tr::tr("Qt Debug Bridge device %1").arg(serial);
|
||||
const QString name = Tr::tr("Boot to Qt device %1").arg(serial);
|
||||
QdbDevice::Ptr device = QdbDevice::create();
|
||||
device->setupId(IDevice::AutoDetected, deviceId);
|
||||
device->settings()->displayName.setValue(name);
|
||||
|
||||
@@ -88,9 +88,9 @@ private:
|
||||
}
|
||||
showMessage(errorString, true);
|
||||
if (!stdOut.isEmpty())
|
||||
showMessage(Tr::tr("stdout was: \"%1\"").arg(stdOut));
|
||||
showMessage(Tr::tr("stdout was: \"%1\".").arg(stdOut));
|
||||
if (!stdErr.isEmpty())
|
||||
showMessage(Tr::tr("stderr was: \"%1\"").arg(stdErr));
|
||||
showMessage(Tr::tr("stderr was: \"%1\".").arg(stdErr));
|
||||
} else {
|
||||
showMessage(Tr::tr("Commands on device \"%1\" finished successfully.")
|
||||
.arg(m_deviceName));
|
||||
@@ -107,7 +107,7 @@ private:
|
||||
|
||||
QdbDevice::QdbDevice()
|
||||
{
|
||||
setDisplayType(Tr::tr("Boot2Qt Device"));
|
||||
setDisplayType(Tr::tr("Boot to Qt Device"));
|
||||
setType(Constants::QdbLinuxOsType);
|
||||
|
||||
addDeviceAction({Tr::tr("Reboot Device"), [](const IDevice::Ptr &device, QWidget *) {
|
||||
@@ -218,7 +218,7 @@ public:
|
||||
QdbDeviceWizard(QWidget *parent)
|
||||
: QWizard(parent)
|
||||
{
|
||||
setWindowTitle(Tr::tr("Boot2Qt Network Device Setup"));
|
||||
setWindowTitle(Tr::tr("Boot to Qt Network Device Setup"));
|
||||
settingsPage.setCommitPage(true);
|
||||
|
||||
enum { SettingsPageId };
|
||||
@@ -253,7 +253,7 @@ public:
|
||||
QdbLinuxDeviceFactory()
|
||||
: IDeviceFactory(Constants::QdbLinuxOsType)
|
||||
{
|
||||
setDisplayName(Tr::tr("Boot2Qt Device"));
|
||||
setDisplayName(Tr::tr("Boot to Qt Device"));
|
||||
setCombinedIcon(":/qdb/images/qdbdevicesmall.png", ":/qdb/images/qdbdevice.png");
|
||||
setQuickCreationAllowed(true);
|
||||
setConstructionFunction(&QdbDevice::create);
|
||||
|
||||
@@ -106,7 +106,7 @@ public:
|
||||
{
|
||||
setConfigBaseId(Constants::QdbDeployConfigurationId);
|
||||
addSupportedTargetDeviceType(Constants::QdbLinuxOsType);
|
||||
setDefaultDisplayName(Tr::tr("Deploy to Boot2Qt target"));
|
||||
setDefaultDisplayName(Tr::tr("Deploy to Boot to Qt target"));
|
||||
setUseDeploymentDataView();
|
||||
|
||||
addInitialStep(RemoteLinux::Constants::MakeInstallStepId, [](Target *target) {
|
||||
|
||||
@@ -33,7 +33,7 @@ public:
|
||||
QdbRunConfiguration(Target *target, Id id)
|
||||
: RunConfiguration(target, id)
|
||||
{
|
||||
setDefaultDisplayName(Tr::tr("Run on Boot2Qt Device"));
|
||||
setDefaultDisplayName(Tr::tr("Run on Boot to Qt Device"));
|
||||
|
||||
executable.setDeviceSelector(target, ExecutableAspect::RunDevice);
|
||||
executable.setSettingsKey("QdbRunConfig.RemoteExecutable");
|
||||
@@ -87,7 +87,7 @@ private:
|
||||
Tasks tasks;
|
||||
if (executable().isEmpty()) {
|
||||
tasks << BuildSystemTask(Task::Warning, Tr::tr("The remote executable must be set "
|
||||
"in order to run on a Boot2Qt device."));
|
||||
"to run on a Boot to Qt device."));
|
||||
}
|
||||
return tasks;
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ QString overridingEnvironmentVariable(QdbTool tool)
|
||||
|
||||
void showMessage(const QString &message, bool important)
|
||||
{
|
||||
const QString fullMessage = Tr::tr("Boot2Qt: %1").arg(message);
|
||||
const QString fullMessage = Tr::tr("Boot to Qt: %1").arg(message);
|
||||
if (important)
|
||||
Core::MessageManager::writeFlashing(fullMessage);
|
||||
else
|
||||
|
||||
@@ -358,8 +358,14 @@ void ClangdCompletionItem::apply(TextDocumentManipulator &manipulator,
|
||||
}
|
||||
|
||||
// Avoid inserting characters that are already there
|
||||
// For include file completions, also consider a possibly pre-existing
|
||||
// closing quote or angle bracket.
|
||||
QTextCursor cursor = manipulator.textCursorAt(rangeStart);
|
||||
cursor.movePosition(QTextCursor::EndOfWord);
|
||||
if (kind == CompletionItemKind::File && !textToBeInserted.isEmpty()
|
||||
&& textToBeInserted.right(1) == manipulator.textAt(cursor.position(), 1)) {
|
||||
cursor.setPosition(cursor.position() + 1);
|
||||
}
|
||||
const QString textAfterCursor = manipulator.textAt(currentPos, cursor.position() - currentPos);
|
||||
if (currentPos < cursor.position()
|
||||
&& textToBeInserted != textAfterCursor
|
||||
|
||||
@@ -110,7 +110,7 @@ ClangFormatGlobalConfigWidget::ClangFormatGlobalConfigWidget(ICodeStylePreferenc
|
||||
// clang-format off
|
||||
Group globalSettingsGroupBox {
|
||||
bindTo(&globalSettingsGroupBoxWidget),
|
||||
title(Tr::tr("ClangFormat Settings:")),
|
||||
title(Tr::tr("ClangFormat Settings")),
|
||||
Column {
|
||||
m_useGlobalSettings,
|
||||
Form {
|
||||
|
||||
@@ -1585,7 +1585,7 @@ CMakeBuildConfiguration::CMakeBuildConfiguration(Target *target, Id id)
|
||||
if (info.buildDirectory.isEmpty()) {
|
||||
setBuildDirectory(shadowBuildDirectory(target->project()->projectFilePath(),
|
||||
k,
|
||||
info.typeName,
|
||||
info.displayName,
|
||||
info.buildType));
|
||||
}
|
||||
|
||||
@@ -1960,6 +1960,9 @@ CMakeBuildConfigurationFactory::CMakeBuildConfigurationFactory()
|
||||
k,
|
||||
info.typeName,
|
||||
info.buildType);
|
||||
} else {
|
||||
info.displayName.clear(); // ask for a name
|
||||
info.buildDirectory.clear(); // This depends on the displayName
|
||||
}
|
||||
result << info;
|
||||
}
|
||||
|
||||
@@ -642,6 +642,25 @@ bool CMakeBuildSystem::addTsFiles(Node *context, const FilePaths &filePaths, Fil
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool isGlobbingFunction(const cmListFile &cmakeListFile, const cmListFileFunction &func)
|
||||
{
|
||||
// Check if the filename is part of globbing variable result
|
||||
const auto globFunctions = std::get<0>(
|
||||
Utils::partition(cmakeListFile.Functions, [](const auto &f) {
|
||||
return f.LowerCaseName() == "file" && f.Arguments().size() > 2
|
||||
&& (f.Arguments().front().Value == "GLOB"
|
||||
|| f.Arguments().front().Value == "GLOB_RECURSE");
|
||||
}));
|
||||
|
||||
const auto globVariables = Utils::transform<QSet>(globFunctions, [](const auto &func) {
|
||||
return std::string("${") + func.Arguments()[1].Value + "}";
|
||||
});
|
||||
|
||||
return Utils::anyOf(func.Arguments(), [globVariables](const auto &arg) {
|
||||
return globVariables.contains(arg.Value);
|
||||
});
|
||||
}
|
||||
|
||||
bool CMakeBuildSystem::addSrcFiles(Node *context, const FilePaths &filePaths, FilePaths *notAdded)
|
||||
{
|
||||
if (notAdded)
|
||||
@@ -670,6 +689,11 @@ bool CMakeBuildSystem::addSrcFiles(Node *context, const FilePaths &filePaths, Fi
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool haveGlobbing = isGlobbingFunction(cmakeListFile.value(), function.value());
|
||||
n->setVisibleAfterAddFileAction(!haveGlobbing);
|
||||
if (haveGlobbing && settings(project()).autorunCMake()) {
|
||||
runCMake();
|
||||
} else {
|
||||
const std::string target_name = function->Arguments().front().Value;
|
||||
auto qtAddModule = [target_name](const auto &func) {
|
||||
return (func.LowerCaseName() == "qt_add_qml_module"
|
||||
@@ -691,6 +715,7 @@ bool CMakeBuildSystem::addSrcFiles(Node *context, const FilePaths &filePaths, Fi
|
||||
qCCritical(cmakeBuildSystemLog) << inserted.error();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (notAdded)
|
||||
notAdded->removeIf([filePaths](const FilePath &p) { return filePaths.contains(p); });
|
||||
@@ -770,22 +795,7 @@ CMakeBuildSystem::projectFileArgumentPosition(const QString &targetName, const Q
|
||||
return ProjectFileArgumentPosition{filePathArgument, targetCMakeFile, fileName};
|
||||
} else {
|
||||
// Check if the filename is part of globbing variable result
|
||||
const auto globFunctions = std::get<0>(
|
||||
Utils::partition(cmakeListFile->Functions, [](const auto &f) {
|
||||
return f.LowerCaseName() == "file" && f.Arguments().size() > 2
|
||||
&& (f.Arguments().front().Value == "GLOB"
|
||||
|| f.Arguments().front().Value == "GLOB_RECURSE");
|
||||
}));
|
||||
|
||||
const auto globVariables = Utils::transform<QSet>(globFunctions, [](const auto &func) {
|
||||
return std::string("${") + func.Arguments()[1].Value + "}";
|
||||
});
|
||||
|
||||
const auto haveGlobbing = Utils::anyOf(func->Arguments(),
|
||||
[globVariables](const auto &arg) {
|
||||
return globVariables.contains(arg.Value);
|
||||
});
|
||||
|
||||
const auto haveGlobbing = isGlobbingFunction(cmakeListFile.value(), func.value());
|
||||
if (haveGlobbing) {
|
||||
return ProjectFileArgumentPosition{filePathArgument,
|
||||
targetCMakeFile,
|
||||
@@ -833,6 +843,7 @@ RemovedFilesFromProject CMakeBuildSystem::removeFiles(Node *context,
|
||||
const FilePath projDir = n->filePath().canonicalPath();
|
||||
const QString targetName = n->buildKey();
|
||||
|
||||
bool haveGlobbing = false;
|
||||
for (const auto &file : filePaths) {
|
||||
const QString fileName
|
||||
= file.canonicalPath().relativePathFrom(projDir).cleanPath().toString();
|
||||
@@ -847,6 +858,11 @@ RemovedFilesFromProject CMakeBuildSystem::removeFiles(Node *context,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (filePos.value().fromGlobbing) {
|
||||
haveGlobbing = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
BaseTextEditor *editor = qobject_cast<BaseTextEditor *>(
|
||||
Core::EditorManager::openEditorAt({filePos.value().cmakeFile,
|
||||
static_cast<int>(filePos.value().argumentPosition.Line),
|
||||
@@ -869,7 +885,6 @@ RemovedFilesFromProject CMakeBuildSystem::removeFiles(Node *context,
|
||||
if (filePos->argumentPosition.Delim == cmListFileArgument::Quoted)
|
||||
extraChars = 2;
|
||||
|
||||
if (!filePos.value().fromGlobbing)
|
||||
editor->replace(filePos.value().relativeFileName.length() + extraChars, "");
|
||||
|
||||
editor->editorWidget()->autoIndent();
|
||||
@@ -889,6 +904,9 @@ RemovedFilesFromProject CMakeBuildSystem::removeFiles(Node *context,
|
||||
if (notRemoved && !badFiles.isEmpty())
|
||||
*notRemoved = badFiles;
|
||||
|
||||
if (haveGlobbing && settings(project()).autorunCMake())
|
||||
runCMake();
|
||||
|
||||
return badFiles.isEmpty() ? RemovedFilesFromProject::Ok : RemovedFilesFromProject::Error;
|
||||
}
|
||||
|
||||
@@ -936,10 +954,6 @@ bool CMakeBuildSystem::renameFile(Node *context,
|
||||
const FilePath newRelPath = newFilePath.canonicalPath().relativePathFrom(projDir).cleanPath();
|
||||
const QString newRelPathName = newRelPath.toString();
|
||||
|
||||
// FilePath needs the file to exist on disk, the old file has already been renamed
|
||||
const QString oldRelPathName
|
||||
= newRelPath.parentDir().pathAppended(oldFilePath.fileName()).cleanPath().toString();
|
||||
|
||||
const QString targetName = n->buildKey();
|
||||
const QString key
|
||||
= QStringList{projDir.path(), targetName, oldFilePath.path(), newFilePath.path()}.join(
|
||||
@@ -953,7 +967,9 @@ bool CMakeBuildSystem::renameFile(Node *context,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool haveGlobbing = false;
|
||||
do {
|
||||
if (!fileToRename->fromGlobbing) {
|
||||
BaseTextEditor *editor = qobject_cast<BaseTextEditor *>(
|
||||
Core::EditorManager::openEditorAt(
|
||||
{fileToRename->cmakeFile,
|
||||
@@ -973,7 +989,6 @@ bool CMakeBuildSystem::renameFile(Node *context,
|
||||
if (fileToRename->argumentPosition.Delim == cmListFileArgument::Quoted)
|
||||
editor->setCursorPosition(editor->position() + 1);
|
||||
|
||||
if (!fileToRename->fromGlobbing)
|
||||
editor->replace(fileToRename->relativeFileName.length(), newRelPathName);
|
||||
|
||||
editor->editorWidget()->autoIndent();
|
||||
@@ -982,11 +997,17 @@ bool CMakeBuildSystem::renameFile(Node *context,
|
||||
<< "Changes to" << fileToRename->cmakeFile.path() << "could not be saved.";
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
haveGlobbing = true;
|
||||
}
|
||||
|
||||
// Try the next occurrence. This can happen if set_source_file_properties is used
|
||||
fileToRename = projectFileArgumentPosition(targetName, oldRelPathName);
|
||||
fileToRename = projectFileArgumentPosition(targetName, fileToRename->relativeFileName);
|
||||
} while (fileToRename && !fileToRename->fromGlobbing);
|
||||
|
||||
if (haveGlobbing && settings(project()).autorunCMake())
|
||||
runCMake();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -330,7 +330,8 @@ void CMakeEditorWidget::findLinkAt(const QTextCursor &cursor,
|
||||
|
||||
if (auto project = ProjectTree::currentProject()) {
|
||||
buffer.replace("${CMAKE_SOURCE_DIR}", project->projectDirectory().path());
|
||||
if (auto bs = ProjectTree::currentBuildSystem(); bs && bs->buildConfiguration()) {
|
||||
auto bs = ProjectTree::currentBuildSystem();
|
||||
if (bs && bs->buildConfiguration()) {
|
||||
buffer.replace("${CMAKE_BINARY_DIR}", bs->buildConfiguration()->buildDirectory().path());
|
||||
|
||||
// Get the path suffix from current source dir to project source dir and apply it
|
||||
|
||||
@@ -187,9 +187,16 @@ void CMakeTargetNode::setConfig(const CMakeConfig &config)
|
||||
m_config = config;
|
||||
}
|
||||
|
||||
void CMakeTargetNode::setVisibleAfterAddFileAction(bool visibleAfterAddFileAction)
|
||||
{
|
||||
m_visibleAfterAddFileAction = visibleAfterAddFileAction;
|
||||
}
|
||||
|
||||
std::optional<FilePath> CMakeTargetNode::visibleAfterAddFileAction() const
|
||||
{
|
||||
if (m_visibleAfterAddFileAction)
|
||||
return filePath().pathAppended(Constants::CMAKE_LISTS_TXT);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void CMakeTargetNode::build()
|
||||
|
||||
@@ -57,11 +57,14 @@ public:
|
||||
QVariant data(Utils::Id role) const override;
|
||||
void setConfig(const CMakeConfig &config);
|
||||
|
||||
void setVisibleAfterAddFileAction(bool visibleAfterAddFileAction);
|
||||
|
||||
private:
|
||||
QString m_tooltip;
|
||||
Utils::FilePath m_buildDirectory;
|
||||
Utils::FilePath m_artifact;
|
||||
CMakeConfig m_config;
|
||||
bool m_visibleAfterAddFileAction = true;
|
||||
};
|
||||
|
||||
} // CMakeProjectManager::Internal
|
||||
|
||||
@@ -142,8 +142,9 @@ void CMakeSpecificSettings::readSettings()
|
||||
} else {
|
||||
Store data = storeFromVariant(project->namedSettings(Constants::Settings::GENERAL_ID));
|
||||
if (data.isEmpty()) {
|
||||
CMakeProject *cmakeProject = static_cast<CMakeProject *>(project);
|
||||
if (cmakeProject->presetsData().havePresets && cmakeProject->presetsData().vendor) {
|
||||
CMakeProject *cmakeProject = qobject_cast<CMakeProject *>(project);
|
||||
if (cmakeProject && cmakeProject->presetsData().havePresets
|
||||
&& cmakeProject->presetsData().vendor) {
|
||||
useGlobalSettings = false;
|
||||
data = storeFromMap(cmakeProject->presetsData().vendor.value());
|
||||
fromMap(data);
|
||||
|
||||
@@ -518,7 +518,7 @@ bool PresetsParser::parse(const Utils::FilePath &jsonFile, QString &errorMessage
|
||||
m_presetsData.configurePresets,
|
||||
jsonFile.parentDir())) {
|
||||
errorMessage = ::CMakeProjectManager::Tr::tr(
|
||||
"Invalid \"configurePresets\" section in %1 file")
|
||||
"Invalid \"configurePresets\" section in file \"%1\".")
|
||||
.arg(jsonFile.fileName());
|
||||
return false;
|
||||
}
|
||||
@@ -527,14 +527,15 @@ bool PresetsParser::parse(const Utils::FilePath &jsonFile, QString &errorMessage
|
||||
if (!parseBuildPresets(root.value("buildPresets"),
|
||||
m_presetsData.buildPresets,
|
||||
jsonFile.parentDir())) {
|
||||
errorMessage = ::CMakeProjectManager::Tr::tr("Invalid \"buildPresets\" section in %1 file")
|
||||
errorMessage = ::CMakeProjectManager::Tr::tr(
|
||||
"Invalid \"buildPresets\" section in file \"%1\".")
|
||||
.arg(jsonFile.fileName());
|
||||
return false;
|
||||
}
|
||||
|
||||
// optional
|
||||
if (!parseVendor(root.value("vendor"), m_presetsData.vendor)) {
|
||||
errorMessage = ::CMakeProjectManager::Tr::tr("Invalid \"vendor\" section in %1 file")
|
||||
errorMessage = ::CMakeProjectManager::Tr::tr("Invalid \"vendor\" section in file \"%1\".")
|
||||
.arg(jsonFile.fileName());
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "ieditorfactory.h"
|
||||
#include "ieditorfactory_p.h"
|
||||
#include "editormanager.h"
|
||||
#include "../coreconstants.h"
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/mimeconstants.h>
|
||||
@@ -16,15 +17,14 @@ namespace Core {
|
||||
|
||||
/* Find the one best matching the mimetype passed in.
|
||||
* Recurse over the parent classes of the mimetype to find them. */
|
||||
template<class EditorTypeLike>
|
||||
static void mimeTypeFactoryLookup(const Utils::MimeType &mimeType,
|
||||
const QList<EditorTypeLike *> &allFactories,
|
||||
QList<EditorTypeLike *> *list)
|
||||
const QList<IEditorFactory *> &allFactories,
|
||||
QList<IEditorFactory *> *list)
|
||||
{
|
||||
QSet<EditorTypeLike *> matches;
|
||||
QSet<IEditorFactory *> matches;
|
||||
Utils::visitMimeParents(mimeType, [&](const Utils::MimeType &mt) -> bool {
|
||||
// check for matching factories
|
||||
for (EditorTypeLike *factory : allFactories) {
|
||||
for (IEditorFactory *factory : allFactories) {
|
||||
if (!matches.contains(factory)) {
|
||||
const QStringList mimeTypes = factory->mimeTypes();
|
||||
for (const QString &mimeName : mimeTypes) {
|
||||
@@ -37,6 +37,14 @@ static void mimeTypeFactoryLookup(const Utils::MimeType &mimeType,
|
||||
}
|
||||
return true; // continue
|
||||
});
|
||||
// Always offer the plain text editor as a fallback for the case that the mime type
|
||||
// is not detected correctly.
|
||||
if (auto plainTextEditorFactory = Utils::findOrDefault(
|
||||
allFactories,
|
||||
Utils::equal(&IEditorFactory::id, Utils::Id(Constants::K_DEFAULT_TEXT_EDITOR_ID)))) {
|
||||
if (!matches.contains(plainTextEditorFactory))
|
||||
list->append(plainTextEditorFactory);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
@@ -161,17 +161,39 @@ void OpenEditorsWindow::setVisible(bool visible)
|
||||
bool OpenEditorsWindow::eventFilter(QObject *obj, QEvent *e)
|
||||
{
|
||||
if (obj == m_editorView) {
|
||||
if (e->type() == QEvent::KeyPress) {
|
||||
if (e->type() == QEvent::ShortcutOverride) {
|
||||
auto ke = static_cast<QKeyEvent*>(e);
|
||||
if (ke->key() == Qt::Key_Escape) {
|
||||
setVisible(false);
|
||||
switch (ke->key()) {
|
||||
case Qt::Key_Up:
|
||||
case Qt::Key_P:
|
||||
e->accept();
|
||||
return true;
|
||||
case Qt::Key_Down:
|
||||
case Qt::Key_N:
|
||||
e->accept();
|
||||
return true;
|
||||
}
|
||||
if (ke->key() == Qt::Key_Return
|
||||
|| ke->key() == Qt::Key_Enter) {
|
||||
}
|
||||
if (e->type() == QEvent::KeyPress) {
|
||||
auto ke = static_cast<QKeyEvent*>(e);
|
||||
switch (ke->key()) {
|
||||
case Qt::Key_Up:
|
||||
case Qt::Key_P:
|
||||
selectNextEditor();
|
||||
return true;
|
||||
case Qt::Key_Down:
|
||||
case Qt::Key_N:
|
||||
selectPreviousEditor();
|
||||
return true;
|
||||
case Qt::Key_Escape:
|
||||
setVisible(false);
|
||||
return true;
|
||||
case Qt::Key_Return:
|
||||
case Qt::Key_Enter:
|
||||
selectEditor(m_editorView->currentItem());
|
||||
return true;
|
||||
}
|
||||
|
||||
} else if (e->type() == QEvent::KeyRelease) {
|
||||
auto ke = static_cast<QKeyEvent*>(e);
|
||||
if (ke->modifiers() == 0
|
||||
|
||||
@@ -74,7 +74,7 @@ public:
|
||||
QElapsedTimer lastMessage;
|
||||
QHash<unsigned int, QPair<int, int>> taskPositions;
|
||||
//: default file name suggested for saving text from output views
|
||||
QString outputFileNameHint{Tr::tr("output.txt")};
|
||||
QString outputFileNameHint{::Core::Tr::tr("output.txt")};
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
@@ -47,7 +47,7 @@ class HeaderGuardExpander : public MacroExpander
|
||||
public:
|
||||
HeaderGuardExpander(const FilePath &filePath) : m_filePath(filePath)
|
||||
{
|
||||
setDisplayName(Tr::tr("Header file variables"));
|
||||
setDisplayName(Tr::tr("Header File Variables"));
|
||||
registerFileVariables("Header", Tr::tr("Header file"), [this] {
|
||||
return m_filePath;
|
||||
});
|
||||
|
||||
@@ -82,8 +82,6 @@ void CppHighlighter::highlightBlock(const QString &text)
|
||||
return;
|
||||
}
|
||||
|
||||
const int firstNonSpace = tokens.first().utf16charsBegin();
|
||||
|
||||
// Keep "semantic parentheses".
|
||||
Parentheses parentheses;
|
||||
if (TextBlockUserData *userData = TextDocumentLayout::textUserData(currentBlock())) {
|
||||
@@ -122,9 +120,16 @@ void CppHighlighter::highlightBlock(const QString &text)
|
||||
if (tk.is(T_LBRACE)) {
|
||||
++braceDepth;
|
||||
|
||||
// if a folding block opens at the beginning of a line, treat the entire line
|
||||
// as if it were inside the folding block
|
||||
if (tk.utf16charsBegin() == firstNonSpace) {
|
||||
// if a folding block opens at the beginning of a line, treat the line before
|
||||
// as if it were inside the folding block except if it is a comment or the line does
|
||||
// end with ;
|
||||
const int firstNonSpace = tokens.first().utf16charsBegin();
|
||||
const QString prevBlockText = currentBlock().previous().isValid()
|
||||
? currentBlock().previous().text().trimmed()
|
||||
: QString();
|
||||
if (!prevBlockText.isEmpty() && !prevBlockText.startsWith("//")
|
||||
&& !prevBlockText.endsWith("*/") && !prevBlockText.endsWith(";")
|
||||
&& tk.utf16charsBegin() == firstNonSpace) {
|
||||
++foldingIndent;
|
||||
TextDocumentLayout::userData(currentBlock())->setFoldingStartIncluded(true);
|
||||
}
|
||||
@@ -654,6 +659,32 @@ void CppHighlighterTest::testParentheses()
|
||||
QCOMPARE(TextDocumentLayout::parentheses(block).count(), expectedParenCount);
|
||||
}
|
||||
|
||||
void CppHighlighterTest::testFoldingIndent_data()
|
||||
{
|
||||
QTest::addColumn<int>("line");
|
||||
QTest::addColumn<int>("expectedFoldingIndent");
|
||||
QTest::addColumn<int>("expectedFoldingIndentNextLine");
|
||||
|
||||
QTest::newRow("braces after one line comment") << 52 << 0 << 1;
|
||||
QTest::newRow("braces after multiline comment") << 59 << 0 << 1;
|
||||
QTest::newRow("braces after completed line") << 67 << 1 << 2;
|
||||
}
|
||||
|
||||
void CppHighlighterTest::testFoldingIndent()
|
||||
{
|
||||
QFETCH(int, line);
|
||||
QFETCH(int, expectedFoldingIndent);
|
||||
QFETCH(int, expectedFoldingIndentNextLine);
|
||||
|
||||
QTextBlock block = m_doc.findBlockByNumber(line - 1);
|
||||
QVERIFY(block.isValid());
|
||||
QCOMPARE(TextDocumentLayout::foldingIndent(block), expectedFoldingIndent);
|
||||
|
||||
QTextBlock nextBlock = m_doc.findBlockByNumber(line);
|
||||
QVERIFY(nextBlock.isValid());
|
||||
QCOMPARE(TextDocumentLayout::foldingIndent(nextBlock), expectedFoldingIndentNextLine);
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
#endif // WITH_TESTS
|
||||
|
||||
|
||||
@@ -54,6 +54,8 @@ private slots:
|
||||
void test();
|
||||
void testParentheses_data();
|
||||
void testParentheses();
|
||||
void testFoldingIndent_data();
|
||||
void testFoldingIndent();
|
||||
|
||||
private:
|
||||
QTextDocument m_doc;
|
||||
|
||||
@@ -553,7 +553,7 @@ using FindUnusedActionsEnabledSwitcherPtr = std::shared_ptr<FindUnusedActionsEna
|
||||
|
||||
static void checkNextFunctionForUnused(
|
||||
const QPointer<SearchResult> &search,
|
||||
const std::shared_ptr<QFutureInterface<bool>> &findRefsFuture,
|
||||
const std::shared_ptr<QFutureInterface<void>> &findRefsFuture,
|
||||
const FindUnusedActionsEnabledSwitcherPtr &actionsSwitcher)
|
||||
{
|
||||
if (!search || findRefsFuture->isCanceled())
|
||||
@@ -650,14 +650,14 @@ void CppModelManager::findUnusedFunctions(const FilePath &folder)
|
||||
Utils::transform<QVariantList>(links, [](const Link &l) { return QVariant::fromValue(l);
|
||||
}));
|
||||
search->setUserData(remainingAndActiveLinks);
|
||||
const auto findRefsFuture = std::make_shared<QFutureInterface<bool>>();
|
||||
const auto findRefsFuture = std::make_shared<QFutureInterface<void>>();
|
||||
FutureProgress *const progress = ProgressManager::addTask(findRefsFuture->future(),
|
||||
Tr::tr("Finding Unused Functions"),
|
||||
"CppEditor.FindUnusedFunctions");
|
||||
connect(progress,
|
||||
&FutureProgress::canceled,
|
||||
search,
|
||||
[search, future = std::weak_ptr<QFutureInterface<bool>>(findRefsFuture)] {
|
||||
[search, future = std::weak_ptr<QFutureInterface<void>>(findRefsFuture)] {
|
||||
search->finishSearch(true);
|
||||
if (const auto f = future.lock()) {
|
||||
f->cancel();
|
||||
|
||||
@@ -47,3 +47,24 @@ static void parenTest()
|
||||
|
||||
const char* s7 = R"(
|
||||
))";
|
||||
|
||||
// comment
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void parenTest2()
|
||||
{
|
||||
|
||||
parenTest();
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,14 +115,6 @@ LldbDapEngine::LldbDapEngine()
|
||||
setDebuggerType("DAP");
|
||||
}
|
||||
|
||||
QJsonArray LldbDapEngine::environment() const
|
||||
{
|
||||
QJsonArray envArray;
|
||||
for (const QString &value : runParameters().inferior.environment.toDictionary().toStringList())
|
||||
envArray.append(value);
|
||||
return envArray;
|
||||
}
|
||||
|
||||
QJsonArray LldbDapEngine::sourceMap() const
|
||||
{
|
||||
QJsonArray sourcePathMapping;
|
||||
@@ -160,11 +152,12 @@ void LldbDapEngine::handleDapInitialize()
|
||||
const QJsonArray commands = preRunCommands();
|
||||
|
||||
if (!isLocalAttachEngine()) {
|
||||
const QJsonArray env = environment();
|
||||
const QJsonArray env = QJsonArray::fromStringList(rp.inferior.environment.toStringList());
|
||||
const QJsonArray args = QJsonArray::fromStringList(rp.inferior.command.splitArguments());
|
||||
|
||||
QJsonObject launchJson{
|
||||
{"noDebug", false},
|
||||
{"program", rp.inferior.command.executable().path()},
|
||||
{"args", rp.inferior.command.arguments()},
|
||||
{"cwd", rp.inferior.workingDirectory.path()},
|
||||
{"env", env},
|
||||
{"__restart", ""},
|
||||
@@ -173,6 +166,8 @@ void LldbDapEngine::handleDapInitialize()
|
||||
launchJson.insert("sourceMap", map);
|
||||
if (!commands.isEmpty())
|
||||
launchJson.insert("preRunCommands", commands);
|
||||
if (!args.isEmpty())
|
||||
launchJson.insert("args", args);
|
||||
|
||||
m_dapClient->postRequest("launch", launchJson);
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ private:
|
||||
bool acceptsBreakpoint(const BreakpointParameters &bp) const override;
|
||||
const QLoggingCategory &logCategory() override;
|
||||
|
||||
QJsonArray environment() const;
|
||||
QJsonArray sourceMap() const;
|
||||
QJsonArray preRunCommands() const;
|
||||
};
|
||||
|
||||
@@ -30,7 +30,9 @@
|
||||
#include <coreplugin/messagebox.h>
|
||||
|
||||
#include <projectexplorer/devicesupport/idevice.h>
|
||||
#include <projectexplorer/project.h>
|
||||
#include <projectexplorer/projectexplorer.h>
|
||||
#include <projectexplorer/projectmanager.h>
|
||||
#include <projectexplorer/taskhub.h>
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
@@ -2074,8 +2076,10 @@ QString GdbEngine::breakpointLocation(const BreakpointParameters &data)
|
||||
return addressSpec(data.address);
|
||||
|
||||
BreakpointPathUsage usage = data.pathUsage;
|
||||
if (usage == BreakpointPathUsageEngineDefault)
|
||||
usage = BreakpointUseShortPath;
|
||||
if (usage == BreakpointPathUsageEngineDefault) {
|
||||
ProjectExplorer::Project *project = ProjectManager::projectForFile(data.fileName);
|
||||
usage = project ? BreakpointUseFullPath : BreakpointUseShortPath;
|
||||
}
|
||||
|
||||
const QString fileName = usage == BreakpointUseFullPath
|
||||
? data.fileName.path() : breakLocation(data.fileName);
|
||||
@@ -2088,8 +2092,10 @@ QString GdbEngine::breakpointLocation(const BreakpointParameters &data)
|
||||
QString GdbEngine::breakpointLocation2(const BreakpointParameters &data)
|
||||
{
|
||||
BreakpointPathUsage usage = data.pathUsage;
|
||||
if (usage == BreakpointPathUsageEngineDefault)
|
||||
usage = BreakpointUseShortPath;
|
||||
if (usage == BreakpointPathUsageEngineDefault) {
|
||||
ProjectExplorer::Project *project = ProjectManager::projectForFile(data.fileName);
|
||||
usage = project ? BreakpointUseFullPath : BreakpointUseShortPath;
|
||||
}
|
||||
|
||||
const QString fileName = usage == BreakpointUseFullPath
|
||||
? data.fileName.path() : breakLocation(data.fileName);
|
||||
|
||||
@@ -166,7 +166,7 @@ GdbSettings::GdbSettings()
|
||||
|
||||
useDebugInfoD.setSettingsKey("UseDebugInfoD");
|
||||
useDebugInfoD.setLabelText(Tr::tr("Use debug info daemon"));
|
||||
useDebugInfoD.setOptionText(TriState::DefaultValue, tr("Use system settings"));
|
||||
useDebugInfoD.setOptionText(TriState::DefaultValue, Tr::tr("Use system settings"));
|
||||
useDebugInfoD.setToolTip(Tr::tr("Lets GDB attempt to automatically retrieve "
|
||||
"debug information for system packages."));
|
||||
|
||||
|
||||
@@ -2,10 +2,14 @@
|
||||
<qresource prefix="/extensionmanager">
|
||||
<file>images/download.png</file>
|
||||
<file>images/download@2x.png</file>
|
||||
<file>images/extensionbig.png</file>
|
||||
<file>images/extensionbig@2x.png</file>
|
||||
<file>images/extensionsmall.png</file>
|
||||
<file>images/extensionsmall@2x.png</file>
|
||||
<file>images/mode_extensionmanager_mask.png</file>
|
||||
<file>images/mode_extensionmanager_mask@2x.png</file>
|
||||
<file>images/packbig.png</file>
|
||||
<file>images/packbig@2x.png</file>
|
||||
<file>images/packsmall.png</file>
|
||||
<file>images/packsmall@2x.png</file>
|
||||
</qresource>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<RCC>
|
||||
<qresource prefix="/extensionmanager">
|
||||
<file>testdata/augmentedplugindata.json</file>
|
||||
<file>testdata/defaultpacks.json</file>
|
||||
<file>testdata/thirdpartyplugins.json</file>
|
||||
<file>testdata/varieddata.json</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@@ -50,7 +50,7 @@ public:
|
||||
using namespace Layouting;
|
||||
auto widget = Column {
|
||||
new StyledBar,
|
||||
new ExtensionManagerWidget,
|
||||
createExtensionManagerWidget(),
|
||||
noMargin, spacing(0),
|
||||
}.emerge();
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include <extensionsystem/pluginmanager.h>
|
||||
#include <extensionsystem/pluginspec.h>
|
||||
#include <extensionsystem/pluginview.h>
|
||||
|
||||
#include <solutions/tasking/networkquery.h>
|
||||
#include <solutions/tasking/tasktree.h>
|
||||
@@ -33,16 +34,56 @@
|
||||
#include <utils/utilsicons.h>
|
||||
|
||||
#include <QAction>
|
||||
#include <QApplication>
|
||||
#include <QBuffer>
|
||||
#include <QCheckBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QImageReader>
|
||||
#include <QMessageBox>
|
||||
#include <QTextBrowser>
|
||||
#include <QMovie>
|
||||
#include <QPainter>
|
||||
#include <QProgressDialog>
|
||||
#include <QScrollArea>
|
||||
#include <QSignalMapper>
|
||||
|
||||
using namespace Core;
|
||||
using namespace Utils;
|
||||
using namespace StyleHelper;
|
||||
using namespace WelcomePageHelpers;
|
||||
|
||||
namespace ExtensionManager::Internal {
|
||||
|
||||
constexpr TextFormat h5TF
|
||||
{Theme::Token_Text_Default, UiElement::UiElementH5};
|
||||
constexpr TextFormat h6TF
|
||||
{h5TF.themeColor, UiElement::UiElementH6};
|
||||
constexpr TextFormat h6CapitalTF
|
||||
{Theme::Token_Text_Muted, UiElement::UiElementH6Capital};
|
||||
constexpr TextFormat contentTF
|
||||
{Theme::Token_Text_Default, UiElement::UiElementBody2};
|
||||
|
||||
static QLabel *sectionTitle(const TextFormat &tf, const QString &title)
|
||||
{
|
||||
QLabel *label = tfLabel(tf, true);
|
||||
label->setText(title);
|
||||
return label;
|
||||
};
|
||||
|
||||
static QWidget *toScrollableColumn(QWidget *widget)
|
||||
{
|
||||
widget->setContentsMargins(SpacingTokens::ExVPaddingGapXl, SpacingTokens::ExVPaddingGapXl,
|
||||
SpacingTokens::ExVPaddingGapXl, SpacingTokens::ExVPaddingGapXl);
|
||||
widget->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Minimum);
|
||||
|
||||
auto scrollArea = new QScrollArea;
|
||||
scrollArea->setWidget(widget);
|
||||
scrollArea->setWidgetResizable(true);
|
||||
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
scrollArea->setFrameStyle(QFrame::NoFrame);
|
||||
|
||||
return scrollArea;
|
||||
};
|
||||
|
||||
class CollapsingWidget : public QWidget
|
||||
{
|
||||
public:
|
||||
@@ -68,6 +109,169 @@ private:
|
||||
int m_width = 100;
|
||||
};
|
||||
|
||||
class HeadingWidget : public QWidget
|
||||
{
|
||||
static constexpr QSize iconBgS{68, 68};
|
||||
static constexpr int dividerH = 16;
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit HeadingWidget(QWidget *parent = nullptr)
|
||||
: QWidget(parent)
|
||||
{
|
||||
m_icon = new QLabel;
|
||||
m_icon->setFixedSize(iconBgS);
|
||||
|
||||
static const TextFormat titleTF
|
||||
{Theme::Token_Text_Default, UiElementH4};
|
||||
static const TextFormat vendorTF
|
||||
{Theme::Token_Text_Accent, UiElementLabelMedium};
|
||||
static const TextFormat dlTF
|
||||
{Theme::Token_Text_Muted, vendorTF.uiElement};
|
||||
static const TextFormat detailsTF
|
||||
{Theme::Token_Text_Default, UiElementBody2};
|
||||
|
||||
m_title = tfLabel(titleTF);
|
||||
m_vendor = new Button({}, Button::SmallLink);
|
||||
m_vendor->setContentsMargins({});
|
||||
m_divider = new QLabel;
|
||||
m_divider->setFixedSize(1, dividerH);
|
||||
WelcomePageHelpers::setBackgroundColor(m_divider, dlTF.themeColor);
|
||||
m_dlIcon = new QLabel;
|
||||
const QPixmap dlIcon = Icon({{":/extensionmanager/images/download.png", dlTF.themeColor}},
|
||||
Icon::Tint).pixmap();
|
||||
m_dlIcon->setPixmap(dlIcon);
|
||||
m_dlCount = tfLabel(dlTF);
|
||||
m_dlCount->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
|
||||
m_details = tfLabel(detailsTF);
|
||||
installButton = new Button(Tr::tr("Install..."), Button::MediumPrimary);
|
||||
installButton->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
|
||||
installButton->hide();
|
||||
|
||||
using namespace Layouting;
|
||||
Row {
|
||||
m_icon,
|
||||
Column {
|
||||
m_title,
|
||||
st,
|
||||
Row {
|
||||
m_vendor,
|
||||
Widget {
|
||||
bindTo(&m_dlCountItems),
|
||||
Row {
|
||||
Space(SpacingTokens::HGapXs),
|
||||
m_divider,
|
||||
Space(SpacingTokens::HGapXs),
|
||||
m_dlIcon,
|
||||
Space(SpacingTokens::HGapXxs),
|
||||
m_dlCount,
|
||||
noMargin, spacing(0),
|
||||
},
|
||||
},
|
||||
},
|
||||
st,
|
||||
m_details,
|
||||
spacing(0),
|
||||
},
|
||||
Column {
|
||||
installButton,
|
||||
st,
|
||||
},
|
||||
noMargin, spacing(SpacingTokens::ExPaddingGapL),
|
||||
}.attachTo(this);
|
||||
|
||||
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Maximum);
|
||||
m_dlCountItems->setVisible(false);
|
||||
|
||||
connect(installButton, &QAbstractButton::pressed,
|
||||
this, &HeadingWidget::pluginInstallationRequested);
|
||||
connect(m_vendor, &QAbstractButton::pressed, this, [this]() {
|
||||
emit vendorClicked(m_currentVendor);
|
||||
});
|
||||
|
||||
update({});
|
||||
}
|
||||
|
||||
void update(const QModelIndex ¤t)
|
||||
{
|
||||
if (!current.isValid())
|
||||
return;
|
||||
|
||||
m_icon->setPixmap(icon(current));
|
||||
|
||||
const QString name = current.data(RoleName).toString();
|
||||
m_title->setText(name);
|
||||
|
||||
m_currentVendor = current.data(RoleVendor).toString();
|
||||
m_vendor->setText(m_currentVendor);
|
||||
|
||||
const int dlCount = current.data(RoleDownloadCount).toInt();
|
||||
const bool showDlCount = dlCount > 0;
|
||||
if (showDlCount)
|
||||
m_dlCount->setText(QString::number(dlCount));
|
||||
m_dlCountItems->setVisible(showDlCount);
|
||||
|
||||
const auto pluginData = current.data(RolePlugins).value<PluginsData>();
|
||||
if (current.data(RoleItemType).toInt() == ItemTypePack) {
|
||||
const int pluginsCount = pluginData.count();
|
||||
const QString details = Tr::tr("Pack contains %n plugins.", nullptr, pluginsCount);
|
||||
m_details->setText(details);
|
||||
} else {
|
||||
m_details->setText({});
|
||||
}
|
||||
|
||||
const ItemType itemType = current.data(RoleItemType).value<ItemType>();
|
||||
const bool isPack = itemType == ItemTypePack;
|
||||
const bool isRemotePlugin = !(isPack || pluginSpecForName(name));
|
||||
installButton->setVisible(isRemotePlugin && !pluginData.empty());
|
||||
if (installButton->isVisible())
|
||||
installButton->setToolTip(pluginData.constFirst().second);
|
||||
}
|
||||
|
||||
signals:
|
||||
void pluginInstallationRequested();
|
||||
void vendorClicked(const QString &vendor);
|
||||
|
||||
private:
|
||||
static QPixmap icon(const QModelIndex &index)
|
||||
{
|
||||
const qreal dpr = qApp->devicePixelRatio();
|
||||
QPixmap pixmap(iconBgS * dpr);
|
||||
pixmap.fill(Qt::transparent);
|
||||
pixmap.setDevicePixelRatio(dpr);
|
||||
const QRect bgR(QPoint(), pixmap.deviceIndependentSize().toSize());
|
||||
|
||||
QPainter p(&pixmap);
|
||||
QLinearGradient gradient(bgR.topRight(), bgR.bottomLeft());
|
||||
gradient.setStops(iconGradientStops(index));
|
||||
constexpr int iconRectRounding = 4;
|
||||
WelcomePageHelpers::drawCardBackground(&p, bgR, gradient, Qt::NoPen, iconRectRounding);
|
||||
|
||||
// Icon
|
||||
constexpr Theme::Color color = Theme::Token_Basic_White;
|
||||
static const QIcon pack = Icon({{":/extensionmanager/images/packbig.png", color}},
|
||||
Icon::Tint).icon();
|
||||
static const QIcon extension = Icon({{":/extensionmanager/images/extensionbig.png",
|
||||
color}}, Icon::Tint).icon();
|
||||
const ItemType itemType = index.data(RoleItemType).value<ItemType>();
|
||||
(itemType == ItemTypePack ? pack : extension).paint(&p, bgR);
|
||||
|
||||
return pixmap;
|
||||
}
|
||||
|
||||
QLabel *m_icon;
|
||||
QLabel *m_title;
|
||||
Button *m_vendor;
|
||||
QLabel *m_divider;
|
||||
QLabel *m_dlIcon;
|
||||
QLabel *m_dlCount;
|
||||
QWidget *m_dlCountItems;
|
||||
QLabel *m_details;
|
||||
QAbstractButton *installButton;
|
||||
QString m_currentVendor;
|
||||
};
|
||||
|
||||
class PluginStatusWidget : public QWidget
|
||||
{
|
||||
public:
|
||||
@@ -75,22 +279,34 @@ public:
|
||||
: QWidget(parent)
|
||||
{
|
||||
m_label = new InfoLabel;
|
||||
m_checkBox = new QCheckBox(Tr::tr("Load on Start"));
|
||||
m_checkBox = new QCheckBox(Tr::tr("Load on start"));
|
||||
m_restartButton = new Button(Tr::tr("Restart Now"), Button::MediumPrimary);
|
||||
m_restartButton->setVisible(false);
|
||||
m_pluginView.hide();
|
||||
|
||||
using namespace Layouting;
|
||||
Column {
|
||||
m_label,
|
||||
m_checkBox,
|
||||
m_restartButton,
|
||||
}.attachTo(this);
|
||||
|
||||
connect(m_checkBox, &QCheckBox::clicked, this, [this](bool checked) {
|
||||
ExtensionSystem::PluginSpec *spec = ExtensionsModel::pluginSpecForName(m_pluginName);
|
||||
ExtensionSystem::PluginSpec *spec = pluginSpecForName(m_pluginName);
|
||||
if (spec == nullptr)
|
||||
return;
|
||||
spec->setEnabledBySettings(checked);
|
||||
const bool doIt = m_pluginView.data().setPluginsEnabled({spec}, checked);
|
||||
if (doIt) {
|
||||
m_restartButton->show();
|
||||
ExtensionSystem::PluginManager::writeSettings();
|
||||
} else {
|
||||
m_checkBox->setChecked(!checked);
|
||||
}
|
||||
});
|
||||
|
||||
connect(m_restartButton, &QAbstractButton::clicked,
|
||||
ICore::instance(), &ICore::restart, Qt::QueuedConnection);
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
@@ -103,7 +319,7 @@ public:
|
||||
private:
|
||||
void update()
|
||||
{
|
||||
const ExtensionSystem::PluginSpec *spec = ExtensionsModel::pluginSpecForName(m_pluginName);
|
||||
const ExtensionSystem::PluginSpec *spec = pluginSpecForName(m_pluginName);
|
||||
setVisible(spec != nullptr);
|
||||
if (spec == nullptr)
|
||||
return;
|
||||
@@ -125,290 +341,322 @@ private:
|
||||
|
||||
InfoLabel *m_label;
|
||||
QCheckBox *m_checkBox;
|
||||
QAbstractButton *m_restartButton;
|
||||
QString m_pluginName;
|
||||
ExtensionSystem::PluginView m_pluginView{this};
|
||||
};
|
||||
|
||||
class ExtensionManagerWidgetPrivate
|
||||
class TagList : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QString currentItemName;
|
||||
ExtensionsBrowser *leftColumn;
|
||||
CollapsingWidget *secondaryDescriptionWidget;
|
||||
QTextBrowser *primaryDescription;
|
||||
QTextBrowser *secondaryDescription;
|
||||
PluginStatusWidget *pluginStatus;
|
||||
QAbstractButton *installButton;
|
||||
PluginsData currentItemPlugins;
|
||||
Tasking::TaskTreeRunner taskTreeRunner;
|
||||
};
|
||||
explicit TagList(QWidget *parent = nullptr)
|
||||
: QWidget(parent)
|
||||
{
|
||||
QHBoxLayout *layout = new QHBoxLayout(this);
|
||||
setLayout(layout);
|
||||
layout->setContentsMargins({});
|
||||
m_signalMapper = new QSignalMapper(this);
|
||||
connect(m_signalMapper, &QSignalMapper::mappedString, this, &TagList::tagSelected);
|
||||
}
|
||||
|
||||
ExtensionManagerWidget::ExtensionManagerWidget(QWidget *parent)
|
||||
: ResizeSignallingWidget(parent)
|
||||
, d(new ExtensionManagerWidgetPrivate)
|
||||
{
|
||||
d->leftColumn = new ExtensionsBrowser;
|
||||
void setTags(const QStringList &tags)
|
||||
{
|
||||
if (m_container) {
|
||||
delete m_container;
|
||||
m_container = nullptr;
|
||||
}
|
||||
|
||||
auto descriptionColumns = new QWidget;
|
||||
|
||||
d->secondaryDescriptionWidget = new CollapsingWidget;
|
||||
|
||||
d->primaryDescription = new QTextBrowser;
|
||||
d->primaryDescription->setOpenExternalLinks(true);
|
||||
d->primaryDescription->setFrameStyle(QFrame::NoFrame);
|
||||
|
||||
d->secondaryDescription = new QTextBrowser;
|
||||
d->secondaryDescription->setFrameStyle(QFrame::NoFrame);
|
||||
|
||||
d->pluginStatus = new PluginStatusWidget;
|
||||
|
||||
d->installButton = new Button(Tr::tr("Install..."), Button::MediumPrimary);
|
||||
d->installButton->hide();
|
||||
if (!tags.empty()) {
|
||||
m_container = new QWidget(this);
|
||||
layout()->addWidget(m_container);
|
||||
|
||||
using namespace Layouting;
|
||||
Flow flow {};
|
||||
flow.setNoMargins();
|
||||
flow.setSpacing(SpacingTokens::HGapXs);
|
||||
|
||||
for (const QString &tag : tags) {
|
||||
QAbstractButton *tagButton = new Button(tag, Button::Tag);
|
||||
connect(tagButton, &QAbstractButton::clicked,
|
||||
m_signalMapper, qOverload<>(&QSignalMapper::map));
|
||||
m_signalMapper->setMapping(tagButton, tag);
|
||||
flow.addItem(tagButton);
|
||||
}
|
||||
|
||||
flow.attachTo(m_container);
|
||||
}
|
||||
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
signals:
|
||||
void tagSelected(const QString &tag);
|
||||
|
||||
private:
|
||||
QWidget *m_container = nullptr;
|
||||
QSignalMapper *m_signalMapper;
|
||||
};
|
||||
|
||||
class ExtensionManagerWidget final : public Core::ResizeSignallingWidget
|
||||
{
|
||||
public:
|
||||
ExtensionManagerWidget();
|
||||
|
||||
private:
|
||||
void updateView(const QModelIndex ¤t);
|
||||
void fetchAndInstallPlugin(const QUrl &url);
|
||||
void fetchAndDisplayImage(const QUrl &url);
|
||||
|
||||
QString m_currentItemName;
|
||||
ExtensionsBrowser *m_extensionBrowser;
|
||||
CollapsingWidget *m_secondaryDescriptionWidget;
|
||||
HeadingWidget *m_headingWidget;
|
||||
QWidget *m_primaryContent;
|
||||
QWidget *m_secondaryContent;
|
||||
QLabel *m_description;
|
||||
QLabel *m_linksTitle;
|
||||
QLabel *m_links;
|
||||
QLabel *m_imageTitle;
|
||||
QLabel *m_image;
|
||||
QBuffer m_imageDataBuffer;
|
||||
QMovie m_imageMovie;
|
||||
QLabel *m_tagsTitle;
|
||||
TagList *m_tags;
|
||||
QLabel *m_platformsTitle;
|
||||
QLabel *m_platforms;
|
||||
QLabel *m_dependenciesTitle;
|
||||
QLabel *m_dependencies;
|
||||
QLabel *m_packExtensionsTitle;
|
||||
QLabel *m_packExtensions;
|
||||
PluginStatusWidget *m_pluginStatus;
|
||||
PluginsData m_currentItemPlugins;
|
||||
Tasking::TaskTreeRunner m_dlTaskTreeRunner;
|
||||
Tasking::TaskTreeRunner m_imgTaskTreeRunner;
|
||||
};
|
||||
|
||||
ExtensionManagerWidget::ExtensionManagerWidget()
|
||||
{
|
||||
m_extensionBrowser = new ExtensionsBrowser;
|
||||
auto descriptionColumns = new QWidget;
|
||||
m_secondaryDescriptionWidget = new CollapsingWidget;
|
||||
|
||||
m_headingWidget = new HeadingWidget;
|
||||
m_description = tfLabel(contentTF, false);
|
||||
m_description->setWordWrap(true);
|
||||
m_linksTitle = sectionTitle(h6CapitalTF, Tr::tr("More information"));
|
||||
m_links = tfLabel(contentTF, false);
|
||||
m_links->setOpenExternalLinks(true);
|
||||
m_imageTitle = sectionTitle(h6CapitalTF, {});
|
||||
m_image = new QLabel;
|
||||
m_imageMovie.setDevice(&m_imageDataBuffer);
|
||||
|
||||
using namespace Layouting;
|
||||
auto primary = new QWidget;
|
||||
const auto spL = spacing(SpacingTokens::VPaddingL);
|
||||
Column {
|
||||
m_description,
|
||||
Column { m_linksTitle, m_links, spL },
|
||||
Column { m_imageTitle, m_image, spL },
|
||||
st,
|
||||
noMargin, spacing(SpacingTokens::ExVPaddingGapXl),
|
||||
}.attachTo(primary);
|
||||
m_primaryContent = toScrollableColumn(primary);
|
||||
|
||||
m_tagsTitle = sectionTitle(h6TF, Tr::tr("Tags"));
|
||||
m_tags = new TagList;
|
||||
m_platformsTitle = sectionTitle(h6TF, Tr::tr("Platforms"));
|
||||
m_platforms = tfLabel(contentTF, false);
|
||||
m_dependenciesTitle = sectionTitle(h6TF, Tr::tr("Dependencies"));
|
||||
m_dependencies = tfLabel(contentTF, false);
|
||||
m_packExtensionsTitle = sectionTitle(h6TF, Tr::tr("Extensions in pack"));
|
||||
m_packExtensions = tfLabel(contentTF, false);
|
||||
m_pluginStatus = new PluginStatusWidget;
|
||||
|
||||
auto secondary = new QWidget;
|
||||
const auto spXxs = spacing(SpacingTokens::VPaddingXxs);
|
||||
Column {
|
||||
sectionTitle(h6CapitalTF, Tr::tr("Extension details")),
|
||||
Column {
|
||||
Column { m_tagsTitle, m_tags, spXxs },
|
||||
Column { m_platformsTitle, m_platforms, spXxs },
|
||||
Column { m_dependenciesTitle, m_dependencies, spXxs },
|
||||
Column { m_packExtensionsTitle, m_packExtensions, spXxs },
|
||||
spacing(SpacingTokens::VPaddingL),
|
||||
},
|
||||
st,
|
||||
noMargin, spacing(SpacingTokens::ExVPaddingGapXl),
|
||||
}.attachTo(secondary);
|
||||
m_secondaryContent = toScrollableColumn(secondary);
|
||||
|
||||
Row {
|
||||
WelcomePageHelpers::createRule(Qt::Vertical),
|
||||
Column {
|
||||
d->secondaryDescription,
|
||||
d->pluginStatus,
|
||||
d->installButton,
|
||||
m_secondaryContent,
|
||||
m_pluginStatus,
|
||||
},
|
||||
noMargin, spacing(0),
|
||||
}.attachTo(d->secondaryDescriptionWidget);
|
||||
}.attachTo(m_secondaryDescriptionWidget);
|
||||
|
||||
Row {
|
||||
WelcomePageHelpers::createRule(Qt::Vertical),
|
||||
Row {
|
||||
d->primaryDescription,
|
||||
noMargin,
|
||||
Column {
|
||||
Column {
|
||||
m_headingWidget,
|
||||
customMargins(SpacingTokens::ExVPaddingGapXl, SpacingTokens::ExVPaddingGapXl,
|
||||
SpacingTokens::ExVPaddingGapXl, SpacingTokens::ExVPaddingGapXl),
|
||||
},
|
||||
d->secondaryDescriptionWidget,
|
||||
m_primaryContent,
|
||||
},
|
||||
},
|
||||
m_secondaryDescriptionWidget,
|
||||
noMargin, spacing(0),
|
||||
}.attachTo(descriptionColumns);
|
||||
|
||||
Row {
|
||||
Space(StyleHelper::SpacingTokens::ExVPaddingGapXl),
|
||||
d->leftColumn,
|
||||
Space(SpacingTokens::ExVPaddingGapXl),
|
||||
m_extensionBrowser,
|
||||
descriptionColumns,
|
||||
noMargin, spacing(0),
|
||||
}.attachTo(this);
|
||||
|
||||
WelcomePageHelpers::setBackgroundColor(this, Theme::Token_Background_Default);
|
||||
|
||||
connect(d->leftColumn, &ExtensionsBrowser::itemSelected,
|
||||
connect(m_extensionBrowser, &ExtensionsBrowser::itemSelected,
|
||||
this, &ExtensionManagerWidget::updateView);
|
||||
connect(this, &ResizeSignallingWidget::resized, this, [this](const QSize &size) {
|
||||
const int intendedLeftColumnWidth = size.width() - 580;
|
||||
d->leftColumn->adjustToWidth(intendedLeftColumnWidth);
|
||||
const int intendedBrowserColumnWidth = size.width() - 580;
|
||||
m_extensionBrowser->adjustToWidth(intendedBrowserColumnWidth);
|
||||
const bool secondaryDescriptionVisible = size.width() > 970;
|
||||
const int secondaryDescriptionWidth = secondaryDescriptionVisible ? 264 : 0;
|
||||
d->secondaryDescriptionWidget->setWidth(secondaryDescriptionWidth);
|
||||
m_secondaryDescriptionWidget->setWidth(secondaryDescriptionWidth);
|
||||
});
|
||||
connect(d->installButton, &QAbstractButton::pressed, this, [this]() {
|
||||
fetchAndInstallPlugin(QUrl::fromUserInput(d->currentItemPlugins.constFirst().second));
|
||||
connect(m_headingWidget, &HeadingWidget::pluginInstallationRequested, this, [this](){
|
||||
fetchAndInstallPlugin(QUrl::fromUserInput(m_currentItemPlugins.constFirst().second));
|
||||
});
|
||||
updateView({});
|
||||
}
|
||||
connect(m_tags, &TagList::tagSelected, m_extensionBrowser, &ExtensionsBrowser::setFilter);
|
||||
connect(m_headingWidget, &HeadingWidget::vendorClicked,
|
||||
m_extensionBrowser, &ExtensionsBrowser::setFilter);
|
||||
|
||||
ExtensionManagerWidget::~ExtensionManagerWidget()
|
||||
{
|
||||
delete d;
|
||||
updateView({});
|
||||
}
|
||||
|
||||
void ExtensionManagerWidget::updateView(const QModelIndex ¤t)
|
||||
{
|
||||
const QString h5Css =
|
||||
StyleHelper::fontToCssProperties(StyleHelper::uiFont(StyleHelper::UiElementH5))
|
||||
+ "; margin-top: 0px;";
|
||||
const QString h6Css =
|
||||
StyleHelper::fontToCssProperties(StyleHelper::uiFont(StyleHelper::UiElementH6))
|
||||
+ "; margin-top: 28px;";
|
||||
const QString h6CapitalCss =
|
||||
StyleHelper::fontToCssProperties(StyleHelper::uiFont(StyleHelper::UiElementH6Capital))
|
||||
+ QString::fromLatin1("; margin-top: 0px; color: %1;")
|
||||
.arg(creatorColor(Theme::Token_Text_Muted).name());
|
||||
const QString bodyStyle = QString::fromLatin1("color: %1; background-color: %2; "
|
||||
"margin-left: %3px; margin-right: %3px;")
|
||||
.arg(creatorColor(Theme::Token_Text_Default).name())
|
||||
.arg(creatorColor(Theme::Token_Background_Muted).name())
|
||||
.arg(StyleHelper::SpacingTokens::ExVPaddingGapXl);
|
||||
const QString htmlStart = QString(R"(
|
||||
<html>
|
||||
<body style="%1"><br/>
|
||||
)").arg(bodyStyle);
|
||||
const QString htmlEnd = QString(R"(
|
||||
</body></html>
|
||||
)");
|
||||
m_headingWidget->update(current);
|
||||
|
||||
if (!current.isValid()) {
|
||||
const QString emptyHtml = htmlStart + htmlEnd;
|
||||
d->primaryDescription->setText(emptyHtml);
|
||||
d->secondaryDescription->setText(emptyHtml);
|
||||
const bool showContent = current.isValid();
|
||||
m_primaryContent->setVisible(showContent);
|
||||
m_secondaryContent->setVisible(showContent);
|
||||
m_headingWidget->setVisible(showContent);
|
||||
m_pluginStatus->setVisible(showContent);
|
||||
if (!showContent)
|
||||
return;
|
||||
}
|
||||
|
||||
d->currentItemName = current.data().toString();
|
||||
m_currentItemName = current.data().toString();
|
||||
const bool isPack = current.data(RoleItemType) == ItemTypePack;
|
||||
d->pluginStatus->setPluginName(isPack ? QString() : d->currentItemName);
|
||||
const bool isRemotePlugin = !(isPack || ExtensionsModel::pluginSpecForName(d->currentItemName));
|
||||
d->currentItemPlugins = current.data(RolePlugins).value<PluginsData>();
|
||||
d->installButton->setVisible(isRemotePlugin && !d->currentItemPlugins.empty());
|
||||
if (!d->currentItemPlugins.empty())
|
||||
d->installButton->setToolTip(d->currentItemPlugins.constFirst().second);
|
||||
m_pluginStatus->setPluginName(isPack ? QString() : m_currentItemName);
|
||||
m_currentItemPlugins = current.data(RolePlugins).value<PluginsData>();
|
||||
|
||||
{
|
||||
QString description = htmlStart;
|
||||
auto toContentParagraph = [](const QString &text) {
|
||||
const QString pHtml = QString::fromLatin1("<p style=\"margin-top:0;margin-bottom:0;"
|
||||
"line-height:%1px\">%2</p>")
|
||||
.arg(contentTF.lineHeight()).arg(text);
|
||||
return pHtml;
|
||||
};
|
||||
|
||||
QString descriptionHtml;
|
||||
{
|
||||
const TextData textData = current.data(RoleDescriptionText).value<TextData>();
|
||||
const bool hasDescription = !textData.isEmpty();
|
||||
if (hasDescription) {
|
||||
const QString headerCssTemplate =
|
||||
";margin-top:%1;margin-bottom:%2;padding-top:0;padding-bottom:0;";
|
||||
const QString h4Css = fontToCssProperties(uiFont(UiElementH4))
|
||||
+ headerCssTemplate.arg(0).arg(SpacingTokens::VGapL);
|
||||
const QString h5Css = fontToCssProperties(uiFont(UiElementH5))
|
||||
+ headerCssTemplate.arg(SpacingTokens::ExVPaddingGapXl)
|
||||
.arg(SpacingTokens::VGapL);
|
||||
QString descriptionHtml;
|
||||
for (const TextData::Type &text : textData) {
|
||||
if (text.second.isEmpty())
|
||||
continue;
|
||||
const QString paragraph =
|
||||
QString::fromLatin1("<div style=\"%1\">%2</div><p>%3</p>")
|
||||
.arg(descriptionHtml.isEmpty() ? h5Css : h6Css)
|
||||
QString::fromLatin1("<div style=\"%1\">%2</div>%3")
|
||||
.arg(descriptionHtml.isEmpty() ? h4Css : h5Css)
|
||||
.arg(text.first)
|
||||
.arg(text.second.join("<br/>"));
|
||||
.arg(toContentParagraph(text.second.join("<br/>")));
|
||||
descriptionHtml.append(paragraph);
|
||||
}
|
||||
descriptionHtml.prepend(QString::fromLatin1("<body style=\"color:%1;\">")
|
||||
.arg(creatorColor(Theme::Token_Text_Default).name()));
|
||||
descriptionHtml.append("</body>");
|
||||
m_description->setText(descriptionHtml);
|
||||
}
|
||||
description.append(descriptionHtml);
|
||||
m_description->setVisible(hasDescription);
|
||||
|
||||
description.append(QString::fromLatin1("<div style=\"%1\">%2</div>")
|
||||
.arg(h6Css)
|
||||
.arg(Tr::tr("More information")));
|
||||
const LinksData linksData = current.data(RoleDescriptionLinks).value<LinksData>();
|
||||
if (!linksData.isEmpty()) {
|
||||
const bool hasLinks = !linksData.isEmpty();
|
||||
if (hasLinks) {
|
||||
QString linksHtml;
|
||||
const QStringList links = transform(linksData, [](const LinksData::Type &link) {
|
||||
const QString anchor = link.first.isEmpty() ? link.second : link.first;
|
||||
return QString::fromLatin1("<a href=\"%1\">%2 ></a>")
|
||||
.arg(link.second).arg(anchor);
|
||||
return QString::fromLatin1(R"(<a href="%1" style="color:%2">%3 ></a>)")
|
||||
.arg(link.second)
|
||||
.arg(creatorColor(Theme::Token_Text_Accent).name())
|
||||
.arg(anchor);
|
||||
});
|
||||
linksHtml = links.join("<br/>");
|
||||
description.append(QString::fromLatin1("<p>%1</p>").arg(linksHtml));
|
||||
m_links->setText(toContentParagraph(linksHtml));
|
||||
}
|
||||
m_linksTitle->setVisible(hasLinks);
|
||||
m_links->setVisible(hasLinks);
|
||||
|
||||
m_imgTaskTreeRunner.reset();
|
||||
m_imageMovie.stop();
|
||||
m_imageDataBuffer.close();
|
||||
m_image->clear();
|
||||
const ImagesData imagesData = current.data(RoleDescriptionImages).value<ImagesData>();
|
||||
if (!imagesData.isEmpty()) {
|
||||
const QString examplesBoxCss =
|
||||
QString::fromLatin1("height: 168px; background-color: %1; ")
|
||||
.arg(creatorColor(Theme::Token_Background_Default).name());
|
||||
description.append(QString(R"(
|
||||
<br/>
|
||||
<div style="%1">%2</div>
|
||||
<p style="%3">
|
||||
<br/><br/><br/><br/><br/>
|
||||
TODO: Load imagea asynchronously, and show them in a QLabel.
|
||||
Also Use QMovie for animated images.
|
||||
<br/><br/><br/><br/><br/>
|
||||
</p>
|
||||
)").arg(h6CapitalCss)
|
||||
.arg(Tr::tr("Examples"))
|
||||
.arg(examplesBoxCss));
|
||||
const bool hasImages = !imagesData.isEmpty();
|
||||
if (hasImages) {
|
||||
const ImagesData::Type &image = imagesData.constFirst(); // Only show one image
|
||||
m_imageTitle->setText(image.first);
|
||||
fetchAndDisplayImage(image.second);
|
||||
}
|
||||
|
||||
// Library details vanished from the Figma designs. The data is available, though.
|
||||
const bool showDetails = false;
|
||||
if (showDetails) {
|
||||
const QString captionStrongCss = StyleHelper::fontToCssProperties(
|
||||
StyleHelper::uiFont(StyleHelper::UiElementCaptionStrong));
|
||||
const QLocale locale;
|
||||
const uint size = current.data(RoleSize).toUInt();
|
||||
const QString sizeFmt = locale.formattedDataSize(size);
|
||||
const FilePath location = FilePath::fromVariant(current.data(RoleLocation));
|
||||
const QString version = current.data(RoleVersion).toString();
|
||||
description.append(QString(R"(
|
||||
<div style="%1">%2</div>
|
||||
<p>
|
||||
<table>
|
||||
<tr><td style="%3">%4</td><td>%5</td></tr>
|
||||
<tr><td style="%3">%6</td><td>%7</td></tr>
|
||||
)").arg(h6Css)
|
||||
.arg(Tr::tr("Extension library details"))
|
||||
.arg(captionStrongCss)
|
||||
.arg(Tr::tr("Size"))
|
||||
.arg(sizeFmt)
|
||||
.arg(Tr::tr("Version"))
|
||||
.arg(version));
|
||||
if (!location.isEmpty()) {
|
||||
const QString locationFmt =
|
||||
HostOsInfo::isWindowsHost() ? location.toUserOutput()
|
||||
: location.withTildeHomePath();
|
||||
description.append(QString(R"(
|
||||
<tr><td style="%3">%1</td><td>%2</td></tr>
|
||||
)").arg(Tr::tr("Location"))
|
||||
.arg(locationFmt));
|
||||
}
|
||||
description.append(QString(R"(
|
||||
</table>
|
||||
</p>
|
||||
)"));
|
||||
}
|
||||
|
||||
description.append(htmlEnd);
|
||||
d->primaryDescription->setText(description);
|
||||
m_imageTitle->setVisible(hasImages);
|
||||
m_image->setVisible(hasImages);
|
||||
}
|
||||
|
||||
{
|
||||
QString description = htmlStart;
|
||||
|
||||
description.append(QString(R"(
|
||||
<p style="%1">%2</p>
|
||||
)").arg(h6CapitalCss)
|
||||
.arg(Tr::tr("Extension details")));
|
||||
|
||||
const QStringList tags = current.data(RoleTags).toStringList();
|
||||
if (!tags.isEmpty()) {
|
||||
const QString tagTemplate = QString(R"(
|
||||
<td style="border: 1px solid %1; padding: 3px; ">%2</td>
|
||||
)").arg(creatorColor(Theme::Token_Stroke_Subtle).name());
|
||||
const QStringList tagsFmt = transform(tags, [&tagTemplate](const QString &tag) {
|
||||
return tagTemplate.arg(tag);
|
||||
});
|
||||
description.append(QString(R"(
|
||||
<div style="%1">%2</div>
|
||||
<p>%3</p>
|
||||
)").arg(h6Css)
|
||||
.arg(Tr::tr("Related tags"))
|
||||
.arg(tagsFmt.join(" ")));
|
||||
}
|
||||
m_tags->setTags(tags);
|
||||
const bool hasTags = !tags.isEmpty();
|
||||
m_tagsTitle->setVisible(hasTags);
|
||||
m_tags->setVisible(hasTags);
|
||||
|
||||
const QStringList platforms = current.data(RolePlatforms).toStringList();
|
||||
if (!platforms.isEmpty()) {
|
||||
description.append(QString(R"(
|
||||
<div style="%1">%2</div>
|
||||
<p>%3</p>
|
||||
)").arg(h6Css)
|
||||
.arg(Tr::tr("Platforms"))
|
||||
.arg(platforms.join("<br/>")));
|
||||
}
|
||||
const bool hasPlatforms = !platforms.isEmpty();
|
||||
if (hasPlatforms)
|
||||
m_platforms->setText(toContentParagraph(platforms.join("<br/>")));
|
||||
m_platformsTitle->setVisible(hasPlatforms);
|
||||
m_platforms->setVisible(hasPlatforms);
|
||||
|
||||
const QStringList dependencies = current.data(RoleDependencies).toStringList();
|
||||
if (!dependencies.isEmpty()) {
|
||||
const QString dependenciesFmt = dependencies.join("<br/>");
|
||||
description.append(QString(R"(
|
||||
<div style="%1">%2</div>
|
||||
<p>%3</p>
|
||||
)").arg(h6Css)
|
||||
.arg(Tr::tr("Dependencies"))
|
||||
.arg(dependenciesFmt));
|
||||
}
|
||||
const bool hasDependencies = !dependencies.isEmpty();
|
||||
if (hasDependencies)
|
||||
m_dependencies->setText(toContentParagraph(dependencies.join("<br/>")));
|
||||
m_dependenciesTitle->setVisible(hasDependencies);
|
||||
m_dependencies->setVisible(hasDependencies);
|
||||
|
||||
if (isPack) {
|
||||
const PluginsData plugins = current.data(RolePlugins).value<PluginsData>();
|
||||
const bool hasExtensions = isPack && !plugins.isEmpty();
|
||||
if (hasExtensions) {
|
||||
const QStringList extensions = transform(plugins, &QPair<QString, QString>::first);
|
||||
const QString extensionsFmt = extensions.join("<br/>");
|
||||
description.append(QString(R"(
|
||||
<div style="%1">%2</div>
|
||||
<p>%3</p>
|
||||
)").arg(h6Css)
|
||||
.arg(Tr::tr("Extensions in pack"))
|
||||
.arg(extensionsFmt));
|
||||
m_packExtensions->setText(toContentParagraph(extensions.join("<br/>")));
|
||||
}
|
||||
|
||||
description.append(htmlEnd);
|
||||
d->secondaryDescription->setText(description);
|
||||
m_packExtensionsTitle->setVisible(hasExtensions);
|
||||
m_packExtensions->setVisible(hasExtensions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -419,9 +667,9 @@ void ExtensionManagerWidget::fetchAndInstallPlugin(const QUrl &url)
|
||||
struct StorageStruct
|
||||
{
|
||||
StorageStruct() {
|
||||
progressDialog.reset(new QProgressDialog(Tr::tr("Downloading Plugin..."),
|
||||
Tr::tr("Cancel"), 0, 0,
|
||||
ICore::dialogParent()));
|
||||
progressDialog.reset(new QProgressDialog(
|
||||
Tr::tr("Downloading..."), Tr::tr("Cancel"), 0, 0, ICore::dialogParent()));
|
||||
progressDialog->setWindowTitle(Tr::tr("Download Extension"));
|
||||
progressDialog->setWindowModality(Qt::ApplicationModal);
|
||||
progressDialog->setFixedSize(progressDialog->sizeHint());
|
||||
progressDialog->setAutoClose(false);
|
||||
@@ -446,7 +694,7 @@ void ExtensionManagerWidget::fetchAndInstallPlugin(const QUrl &url)
|
||||
QMessageBox::warning(
|
||||
ICore::dialogParent(),
|
||||
Tr::tr("Download Error"),
|
||||
Tr::tr("Could not download Plugin") + "\n\n" + storage->url.toString() + "\n\n"
|
||||
Tr::tr("Cannot download extension") + "\n\n" + storage->url.toString() + "\n\n"
|
||||
+ Tr::tr("Code: %1.").arg(query.reply()->error()));
|
||||
}
|
||||
};
|
||||
@@ -469,7 +717,61 @@ void ExtensionManagerWidget::fetchAndInstallPlugin(const QUrl &url)
|
||||
onGroupDone(onPluginInstallation),
|
||||
};
|
||||
|
||||
d->taskTreeRunner.start(group);
|
||||
m_dlTaskTreeRunner.start(group);
|
||||
}
|
||||
|
||||
void ExtensionManagerWidget::fetchAndDisplayImage(const QUrl &url)
|
||||
{
|
||||
using namespace Tasking;
|
||||
|
||||
struct StorageStruct
|
||||
{
|
||||
QByteArray imageData;
|
||||
QUrl url;
|
||||
};
|
||||
Storage<StorageStruct> storage;
|
||||
|
||||
const auto onFetchSetup = [url, storage](NetworkQuery &query) {
|
||||
storage->url = url;
|
||||
query.setRequest(QNetworkRequest(url));
|
||||
query.setNetworkAccessManager(NetworkAccessManager::instance());
|
||||
};
|
||||
const auto onFetchDone = [storage](const NetworkQuery &query, DoneWith result) {
|
||||
if (result == DoneWith::Success)
|
||||
storage->imageData = query.reply()->readAll();
|
||||
};
|
||||
|
||||
const auto onShowImage = [storage, this]() {
|
||||
if (storage->imageData.isEmpty())
|
||||
return;
|
||||
m_imageDataBuffer.setData(storage->imageData);
|
||||
if (!m_imageDataBuffer.open(QIODevice::ReadOnly))
|
||||
return;
|
||||
QImageReader reader(&m_imageDataBuffer);
|
||||
const bool animated = reader.supportsAnimation();
|
||||
if (animated) {
|
||||
m_image->setMovie(&m_imageMovie);
|
||||
m_imageMovie.start();
|
||||
} else {
|
||||
const QPixmap pixmap = QPixmap::fromImage(reader.read());
|
||||
m_image->setPixmap(pixmap);
|
||||
}
|
||||
};
|
||||
|
||||
Group group{
|
||||
storage,
|
||||
NetworkQueryTask{onFetchSetup, onFetchDone},
|
||||
onGroupDone(onShowImage),
|
||||
};
|
||||
|
||||
m_imgTaskTreeRunner.start(group);
|
||||
}
|
||||
|
||||
QWidget *createExtensionManagerWidget()
|
||||
{
|
||||
return new ExtensionManagerWidget;
|
||||
}
|
||||
|
||||
} // ExtensionManager::Internal
|
||||
|
||||
#include "extensionmanagerwidget.moc"
|
||||
|
||||
@@ -1,21 +1,10 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <coreplugin/welcomepagehelper.h>
|
||||
#include <QWidget>
|
||||
|
||||
namespace ExtensionManager::Internal {
|
||||
|
||||
class ExtensionManagerWidget final : public Core::ResizeSignallingWidget
|
||||
{
|
||||
public:
|
||||
explicit ExtensionManagerWidget(QWidget *parent = nullptr);
|
||||
~ExtensionManagerWidget();
|
||||
|
||||
private:
|
||||
void updateView(const QModelIndex ¤t);
|
||||
void fetchAndInstallPlugin(const QUrl &url);
|
||||
|
||||
class ExtensionManagerWidgetPrivate *d = nullptr;
|
||||
};
|
||||
QWidget *createExtensionManagerWidget();
|
||||
|
||||
} // ExtensionManager::Internal
|
||||
|
||||
@@ -22,10 +22,12 @@
|
||||
#include <extensionsystem/pluginview.h>
|
||||
#include <extensionsystem/pluginmanager.h>
|
||||
|
||||
#include <solutions/spinner/spinner.h>
|
||||
#include <solutions/tasking/networkquery.h>
|
||||
#include <solutions/tasking/tasktree.h>
|
||||
#include <solutions/tasking/tasktreerunner.h>
|
||||
|
||||
#include <utils/elidinglabel.h>
|
||||
#include <utils/fancylineedit.h>
|
||||
#include <utils/icon.h>
|
||||
#include <utils/layoutbuilder.h>
|
||||
@@ -51,9 +53,9 @@ namespace ExtensionManager::Internal {
|
||||
|
||||
Q_LOGGING_CATEGORY(browserLog, "qtc.extensionmanager.browser", QtWarningMsg)
|
||||
|
||||
constexpr int gapSize = ExVPaddingGapXl;
|
||||
constexpr int gapSize = HGapL;
|
||||
constexpr int itemWidth = 330;
|
||||
constexpr int cellWidth = itemWidth + HPaddingL;
|
||||
constexpr int cellWidth = itemWidth + gapSize;
|
||||
|
||||
class ExtensionItemDelegate : public QItemDelegate
|
||||
{
|
||||
@@ -79,25 +81,25 @@ public:
|
||||
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index)
|
||||
const override
|
||||
{
|
||||
// +---------------+-------+---------------+----------------------------------------------------------------------+---------------+-----------+
|
||||
// +---------------+-------+---------------+----------------------------------------------------------------------+---------------+---------+
|
||||
// | | | | (ExPaddingGapL) | | |
|
||||
// | | | +-------------------------------------------------------------+--------+ | |
|
||||
// | | | | <itemName> |<status>| | |
|
||||
// | | | +-------------------------------------------------------------+--------+ | |
|
||||
// | | | | (VGapXxs) | | |
|
||||
// | | | +--------+--------+--------------+--------+--------+---------+---------+ | |
|
||||
// |(ExPaddingGapL)|<icon> |(ExPaddingGapL)|<vendor>|(HGapXs)|<divider>(h16)|(HGapXs)|<dlIcon>|(HGapXxs)|<dlCount>|(ExPaddingGapL)|(HPaddingL)|
|
||||
// |(ExPaddingGapL)|<icon> |(ExPaddingGapL)|<vendor>|(HGapXs)|<divider>(h16)|(HGapXs)|<dlIcon>|(HGapXxs)|<dlCount>|(ExPaddingGapL)|(gapSize)|
|
||||
// | |(50x50)| +--------+--------+--------------+--------+--------+---------+---------+ | |
|
||||
// | | | | (VGapXxs) | | |
|
||||
// | | | +----------------------------------------------------------------------+ | |
|
||||
// | | | | <tags> | | |
|
||||
// | | | +----------------------------------------------------------------------+ | |
|
||||
// | | | | (ExPaddingGapL) | | |
|
||||
// +---------------+-------+---------------+----------------------------------------------------------------------+---------------+-----------+
|
||||
// | (ExVPaddingGapXl) |
|
||||
// +------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
// +---------------+-------+---------------+----------------------------------------------------------------------+---------------+---------+
|
||||
// | (gapSize) |
|
||||
// +----------------------------------------------------------------------------------------------------------------------------------------+
|
||||
|
||||
const QRect bgRGlobal = option.rect.adjusted(0, 0, -HPaddingL, -gapSize);
|
||||
const QRect bgRGlobal = option.rect.adjusted(0, 0, -gapSize, -gapSize);
|
||||
const QRect bgR = bgRGlobal.translated(-option.rect.topLeft());
|
||||
|
||||
const int middleColumnW = bgR.width() - ExPaddingGapL - iconBgS.width() - ExPaddingGapL
|
||||
@@ -141,10 +143,7 @@ public:
|
||||
}
|
||||
{
|
||||
QLinearGradient gradient(iconBgR.topRight(), iconBgR.bottomLeft());
|
||||
const QColor startColor = creatorColor(Utils::Theme::Token_Gradient01_Start);
|
||||
const QColor endColor = creatorColor(Utils::Theme::Token_Gradient01_End);
|
||||
gradient.setColorAt(0, startColor);
|
||||
gradient.setColorAt(1, endColor);
|
||||
gradient.setStops(iconGradientStops(index));
|
||||
constexpr int iconRectRounding = 4;
|
||||
drawCardBackground(painter, iconBgR, gradient, Qt::NoPen, iconRectRounding);
|
||||
|
||||
@@ -248,17 +247,44 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class SortFilterProxyModel : public QSortFilterProxyModel
|
||||
{
|
||||
public:
|
||||
SortFilterProxyModel(QObject *parent = nullptr);
|
||||
|
||||
protected:
|
||||
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
|
||||
};
|
||||
|
||||
SortFilterProxyModel::SortFilterProxyModel(QObject *parent)
|
||||
: QSortFilterProxyModel(parent)
|
||||
{
|
||||
}
|
||||
|
||||
bool SortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
|
||||
{
|
||||
const ItemType leftType = left.data(RoleItemType).value<ItemType>();
|
||||
const ItemType rightType = right.data(RoleItemType).value<ItemType>();
|
||||
if (leftType != rightType)
|
||||
return leftType < rightType;
|
||||
|
||||
const QString leftName = left.data(RoleName).toString();
|
||||
const QString rightName = right.data(RoleName).toString();
|
||||
return leftName < rightName;
|
||||
}
|
||||
|
||||
class ExtensionsBrowserPrivate
|
||||
{
|
||||
public:
|
||||
bool dataFetched = false;
|
||||
ExtensionsModel *model;
|
||||
QLineEdit *searchBox;
|
||||
QAbstractButton *updateButton;
|
||||
QListView *extensionsView;
|
||||
QItemSelectionModel *selectionModel = nullptr;
|
||||
QSortFilterProxyModel *filterProxyModel;
|
||||
SortFilterProxyModel *filterProxyModel;
|
||||
int columnsCount = 2;
|
||||
Tasking::TaskTreeRunner taskTreeRunner;
|
||||
SpinnerSolution::Spinner *m_spinner;
|
||||
};
|
||||
|
||||
ExtensionsBrowser::ExtensionsBrowser(QWidget *parent)
|
||||
@@ -267,16 +293,17 @@ ExtensionsBrowser::ExtensionsBrowser(QWidget *parent)
|
||||
{
|
||||
setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
|
||||
|
||||
auto manageLabel = new QLabel(Tr::tr("Manage Extensions"));
|
||||
manageLabel->setFont(uiFont(UiElementH1));
|
||||
static const TextFormat titleTF
|
||||
{Theme::Token_Text_Default, UiElementH2};
|
||||
QLabel *titleLabel = tfLabel(titleTF);
|
||||
titleLabel->setText(Tr::tr("Manage Extensions"));
|
||||
|
||||
d->searchBox = new SearchBox;
|
||||
d->searchBox->setFixedWidth(itemWidth);
|
||||
d->updateButton = new Button(Tr::tr("Install..."), Button::MediumPrimary);
|
||||
d->searchBox->setPlaceholderText(Tr::tr("Search"));
|
||||
|
||||
d->model = new ExtensionsModel(this);
|
||||
|
||||
d->filterProxyModel = new QSortFilterProxyModel(this);
|
||||
d->filterProxyModel = new SortFilterProxyModel(this);
|
||||
d->filterProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||
d->filterProxyModel->setFilterRole(RoleSearchText);
|
||||
d->filterProxyModel->setSortRole(RoleItemType);
|
||||
@@ -294,11 +321,16 @@ ExtensionsBrowser::ExtensionsBrowser(QWidget *parent)
|
||||
|
||||
using namespace Layouting;
|
||||
Column {
|
||||
Space(15),
|
||||
manageLabel,
|
||||
Space(15),
|
||||
Row { d->searchBox, st, d->updateButton, Space(extraListViewWidth() + gapSize) },
|
||||
Space(gapSize),
|
||||
Column {
|
||||
titleLabel,
|
||||
customMargins(0, VPaddingM, 0, VPaddingM),
|
||||
},
|
||||
Row {
|
||||
d->searchBox,
|
||||
spacing(gapSize),
|
||||
customMargins(0, VPaddingM, extraListViewWidth() + gapSize, VPaddingM),
|
||||
},
|
||||
Space(ExPaddingGapL),
|
||||
d->extensionsView,
|
||||
noMargin, spacing(0),
|
||||
}.attachTo(this);
|
||||
@@ -308,6 +340,8 @@ ExtensionsBrowser::ExtensionsBrowser(QWidget *parent)
|
||||
WelcomePageHelpers::setBackgroundColor(d->extensionsView->viewport(),
|
||||
Theme::Token_Background_Default);
|
||||
|
||||
d->m_spinner = new SpinnerSolution::Spinner(SpinnerSolution::SpinnerSize::Large, this);
|
||||
|
||||
auto updateModel = [this] {
|
||||
d->filterProxyModel->sort(0);
|
||||
|
||||
@@ -320,12 +354,7 @@ ExtensionsBrowser::ExtensionsBrowser(QWidget *parent)
|
||||
}
|
||||
};
|
||||
|
||||
connect(d->updateButton, &QAbstractButton::pressed, this, []() {
|
||||
executePluginInstallWizard();
|
||||
});
|
||||
connect(PluginManager::instance(), &PluginManager::pluginsChanged, this, updateModel);
|
||||
connect(PluginManager::instance(), &PluginManager::initializationDone,
|
||||
this, &ExtensionsBrowser::fetchExtensions);
|
||||
connect(d->searchBox, &QLineEdit::textChanged,
|
||||
d->filterProxyModel, &QSortFilterProxyModel::setFilterWildcard);
|
||||
}
|
||||
@@ -335,11 +364,15 @@ ExtensionsBrowser::~ExtensionsBrowser()
|
||||
delete d;
|
||||
}
|
||||
|
||||
void ExtensionsBrowser::setFilter(const QString &filter)
|
||||
{
|
||||
d->searchBox->setText(filter);
|
||||
}
|
||||
|
||||
void ExtensionsBrowser::adjustToWidth(const int width)
|
||||
{
|
||||
const int widthForItems = width - extraListViewWidth();
|
||||
d->columnsCount = qMax(1, qFloor(widthForItems / cellWidth));
|
||||
d->updateButton->setVisible(d->columnsCount > 1);
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
@@ -352,40 +385,56 @@ QSize ExtensionsBrowser::sizeHint() const
|
||||
int ExtensionsBrowser::extraListViewWidth() const
|
||||
{
|
||||
// TODO: Investigate "transient" scrollbar, just for this list view.
|
||||
constexpr int extraPadding = qMax(0, ExVPaddingGapXl - gapSize);
|
||||
return d->extensionsView->style()->pixelMetric(QStyle::PM_ScrollBarExtent)
|
||||
+ extraPadding
|
||||
+ 1; // Needed
|
||||
}
|
||||
|
||||
void ExtensionsBrowser::showEvent(QShowEvent *event)
|
||||
{
|
||||
if (!d->dataFetched) {
|
||||
d->dataFetched = true;
|
||||
fetchExtensions();
|
||||
}
|
||||
QWidget::showEvent(event);
|
||||
}
|
||||
|
||||
void ExtensionsBrowser::fetchExtensions()
|
||||
{
|
||||
// d->model->setExtensionsJson(testData("thirdpartyplugins")); return;
|
||||
#ifdef WITH_TESTS
|
||||
// Uncomment for testing with local json data.
|
||||
// Available: "augmentedplugindata", "defaultpacks", "varieddata", "thirdpartyplugins"
|
||||
// d->model->setExtensionsJson(testData("defaultpacks")); return;
|
||||
#endif // WITH_TESTS
|
||||
|
||||
using namespace Tasking;
|
||||
|
||||
const auto onQuerySetup = [](NetworkQuery &query) {
|
||||
const auto onQuerySetup = [this](NetworkQuery &query) {
|
||||
const QString host = "https://qc-extensions.qt.io";
|
||||
const QString url = "%1/api/v1/search?request=";
|
||||
const QString requestTemplate
|
||||
= R"({"version":"%1","host_os":"%2","host_os_version":"%3","host_architecture":"%4","page_size":200})";
|
||||
const QString request = url.arg(host)
|
||||
+ requestTemplate
|
||||
.arg("2.2") // .arg(QCoreApplication::applicationVersion())
|
||||
.arg("macOS") // .arg(QSysInfo::productType())
|
||||
.arg("12") // .arg(QSysInfo::productVersion())
|
||||
.arg("arm64"); // .arg(QSysInfo::currentCpuArchitecture());
|
||||
|
||||
const QString request = url.arg(host) + requestTemplate
|
||||
.arg(QCoreApplication::applicationVersion())
|
||||
.arg(QSysInfo::productType())
|
||||
.arg(QSysInfo::productVersion())
|
||||
.arg(QSysInfo::currentCpuArchitecture());
|
||||
query.setRequest(QNetworkRequest(QUrl::fromUserInput(request)));
|
||||
query.setNetworkAccessManager(NetworkAccessManager::instance());
|
||||
qCDebug(browserLog).noquote() << "Sending request:" << request;
|
||||
d->m_spinner->show();
|
||||
};
|
||||
const auto onQueryDone = [this](const NetworkQuery &query, DoneWith result) {
|
||||
if (result != DoneWith::Success) {
|
||||
#ifdef WITH_TESTS
|
||||
d->model->setExtensionsJson(testData("defaultpacks"));
|
||||
#endif // WITH_TESTS
|
||||
return;
|
||||
}
|
||||
const QByteArray response = query.reply()->readAll();
|
||||
qCDebug(browserLog).noquote() << "Got result" << result;
|
||||
if (result == DoneWith::Success) {
|
||||
d->model->setExtensionsJson(response);
|
||||
} else {
|
||||
qCDebug(browserLog).noquote() << response;
|
||||
d->model->setExtensionsJson({});
|
||||
}
|
||||
d->m_spinner->hide();
|
||||
};
|
||||
|
||||
Group group {
|
||||
@@ -395,4 +444,35 @@ void ExtensionsBrowser::fetchExtensions()
|
||||
d->taskTreeRunner.start(group);
|
||||
}
|
||||
|
||||
QLabel *tfLabel(const TextFormat &tf, bool singleLine)
|
||||
{
|
||||
QLabel *label = singleLine ? new Utils::ElidingLabel : new QLabel;
|
||||
if (singleLine)
|
||||
label->setFixedHeight(tf.lineHeight());
|
||||
label->setFont(tf.font());
|
||||
label->setAlignment(Qt::Alignment(tf.drawTextFlags));
|
||||
|
||||
QPalette pal = label->palette();
|
||||
pal.setColor(QPalette::WindowText, tf.color());
|
||||
label->setPalette(pal);
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
QGradientStops iconGradientStops(const QModelIndex &index)
|
||||
{
|
||||
const PluginSpec *ps = pluginSpecForName(index.data(RoleName).toString());
|
||||
const bool greenGradient = ps != nullptr && ps->isEffectivelyEnabled();
|
||||
|
||||
const QColor startColor = creatorColor(greenGradient ? Theme::Token_Gradient01_Start
|
||||
: Theme::Token_Gradient02_Start);
|
||||
const QColor endColor = creatorColor(greenGradient ? Theme::Token_Gradient01_End
|
||||
: Theme::Token_Gradient02_End);
|
||||
const QGradientStops gradient = {
|
||||
{0, startColor},
|
||||
{1, endColor},
|
||||
};
|
||||
return gradient;
|
||||
}
|
||||
|
||||
} // ExtensionManager::Internal
|
||||
|
||||
@@ -5,6 +5,12 @@
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QLabel)
|
||||
|
||||
namespace Core::WelcomePageHelpers {
|
||||
class TextFormat;
|
||||
}
|
||||
|
||||
namespace ExtensionManager::Internal {
|
||||
|
||||
class ExtensionsBrowser final : public QWidget
|
||||
@@ -15,11 +21,15 @@ public:
|
||||
ExtensionsBrowser(QWidget *parent = nullptr);
|
||||
~ExtensionsBrowser();
|
||||
|
||||
void setFilter(const QString &filter);
|
||||
|
||||
void adjustToWidth(const int width);
|
||||
QSize sizeHint() const override;
|
||||
|
||||
int extraListViewWidth() const; // Space for scrollbar, etc.
|
||||
|
||||
void showEvent(QShowEvent *event) override;
|
||||
|
||||
signals:
|
||||
void itemSelected(const QModelIndex ¤t, const QModelIndex &previous);
|
||||
|
||||
@@ -29,4 +39,7 @@ private:
|
||||
class ExtensionsBrowserPrivate *d = nullptr;
|
||||
};
|
||||
|
||||
QLabel *tfLabel(const Core::WelcomePageHelpers::TextFormat &tf, bool singleLine = true);
|
||||
QGradientStops iconGradientStops(const QModelIndex &index);
|
||||
|
||||
} // ExtensionManager::Internal
|
||||
|
||||
@@ -36,8 +36,8 @@ using Dependencies = QList<Dependency>;
|
||||
|
||||
struct Plugin
|
||||
{
|
||||
Dependencies dependencies;
|
||||
QString copyright;
|
||||
Dependencies dependencies;
|
||||
bool isInternal = false;
|
||||
QString name;
|
||||
QString packageUrl;
|
||||
@@ -69,23 +69,29 @@ struct Extension {
|
||||
};
|
||||
using Extensions = QList<Extension>;
|
||||
|
||||
static const Dependencies dependenciesFromJson(const QJsonObject &obj)
|
||||
{
|
||||
const QJsonArray dependenciesArray = obj.value("Dependencies").toArray();
|
||||
Dependencies dependencies;
|
||||
for (const QJsonValueConstRef &dependencyVal : dependenciesArray) {
|
||||
const QJsonObject dependencyObj = dependencyVal.toObject();
|
||||
const QJsonObject metaDataObj = dependencyObj.value("meta_data").toObject();
|
||||
dependencies.append({
|
||||
.name = metaDataObj.value("Name").toString(),
|
||||
.version = metaDataObj.value("Version").toString(),
|
||||
});
|
||||
}
|
||||
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
static Plugin pluginFromJson(const QJsonObject &obj)
|
||||
{
|
||||
const QJsonObject metaDataObj = obj.value("meta_data").toObject();
|
||||
|
||||
const QJsonArray dependenciesArray = metaDataObj.value("Dependencies").toArray();
|
||||
Dependencies dependencies;
|
||||
for (const QJsonValueConstRef &dependencyVal : dependenciesArray) {
|
||||
const QJsonObject dependencyObj = dependencyVal.toObject();
|
||||
dependencies.append(Dependency{
|
||||
.name = dependencyObj.value("Name").toString(),
|
||||
.version = dependencyObj.value("Version").toString(),
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
.dependencies = dependencies,
|
||||
.copyright = metaDataObj.value("Copyright").toString(),
|
||||
.dependencies = dependenciesFromJson(metaDataObj),
|
||||
.isInternal = obj.value("is_internal").toBool(false),
|
||||
.name = metaDataObj.value("Name").toString(),
|
||||
.packageUrl = obj.value("url").toString(),
|
||||
@@ -192,30 +198,80 @@ static Extensions parseExtensionsRepoReply(const QByteArray &jsonData)
|
||||
return parsedExtensions;
|
||||
}
|
||||
|
||||
static Extension extensionFromPluginSpec(const PluginSpec *pluginSpec)
|
||||
{
|
||||
const Dependencies dependencies = transform(pluginSpec->dependencies(),
|
||||
[](const PluginDependency &pd) -> Dependency {
|
||||
return {
|
||||
.name = pd.name,
|
||||
.version = pd.version,
|
||||
};
|
||||
});
|
||||
const Plugin plugin = {
|
||||
.copyright = pluginSpec->copyright(),
|
||||
.dependencies = dependencies,
|
||||
.name = pluginSpec->name(),
|
||||
.packageUrl = {},
|
||||
.vendor = pluginSpec->vendor(),
|
||||
.version = pluginSpec->version(),
|
||||
};
|
||||
|
||||
const QStringList lines = pluginSpec->description().split('\n', Qt::SkipEmptyParts)
|
||||
+ pluginSpec->longDescription().split('\n', Qt::SkipEmptyParts);
|
||||
const TextData text = {{ pluginSpec->name(), lines }};
|
||||
LinksData links;
|
||||
if (const QString url = pluginSpec->url(); !url.isEmpty())
|
||||
links.append({{}, url});
|
||||
const Description description = {
|
||||
.images = {},
|
||||
.links = links,
|
||||
.text = text,
|
||||
};
|
||||
|
||||
const QString platformsPattern = pluginSpec->platformSpecification().pattern();
|
||||
const QStringList platforms = platformsPattern.isEmpty()
|
||||
? QStringList({"macOS", "Windows", "Linux"})
|
||||
: QStringList(platformsPattern);
|
||||
|
||||
const Extension extension = {
|
||||
.copyright = pluginSpec->copyright(),
|
||||
.description = description,
|
||||
.id = {},
|
||||
.license = pluginSpec->license(),
|
||||
.name = pluginSpec->name(),
|
||||
.platforms = platforms,
|
||||
.plugins = {plugin},
|
||||
.tags = {},
|
||||
.type = ItemTypeExtension,
|
||||
.vendor = pluginSpec->vendor(),
|
||||
.version = pluginSpec->version(),
|
||||
};
|
||||
return extension;
|
||||
}
|
||||
|
||||
class ExtensionsModelPrivate
|
||||
{
|
||||
public:
|
||||
void setExtensions(const Extensions &extensions);
|
||||
void removeLocalExtensions();
|
||||
void addUnlistedLocalExtensions();
|
||||
|
||||
Extensions allExtensions; // Original, complete extensions entries
|
||||
Extensions absentExtensions; // All packs + plugin extensions that are not (yet) installed
|
||||
Extensions extensions;
|
||||
};
|
||||
|
||||
void ExtensionsModelPrivate::setExtensions(const Extensions &extensions)
|
||||
{
|
||||
allExtensions = extensions;
|
||||
removeLocalExtensions();
|
||||
this->extensions = extensions;
|
||||
qCDebug(modelLog) << "Number of extensions from json:" << this->extensions.count();
|
||||
addUnlistedLocalExtensions();
|
||||
qCDebug(modelLog) << "Number of extensions with added local ones:" << this->extensions.count();
|
||||
}
|
||||
|
||||
void ExtensionsModelPrivate::removeLocalExtensions()
|
||||
void ExtensionsModelPrivate::addUnlistedLocalExtensions()
|
||||
{
|
||||
const QStringList installedPlugins = transform(PluginManager::plugins(), &PluginSpec::name);
|
||||
absentExtensions.clear();
|
||||
for (const Extension &extension : allExtensions) {
|
||||
if (extension.type == ItemTypePack || !installedPlugins.contains(extension.name))
|
||||
absentExtensions.append(extension);
|
||||
}
|
||||
const QStringList listedModelExtensions = transform(extensions, &Extension::name);
|
||||
for (const PluginSpec *plugin : PluginManager::plugins())
|
||||
if (!listedModelExtensions.contains(plugin->name()))
|
||||
extensions.append(extensionFromPluginSpec(plugin));
|
||||
}
|
||||
|
||||
ExtensionsModel::ExtensionsModel(QObject *parent)
|
||||
@@ -231,66 +287,7 @@ ExtensionsModel::~ExtensionsModel()
|
||||
|
||||
int ExtensionsModel::rowCount([[maybe_unused]] const QModelIndex &parent) const
|
||||
{
|
||||
const int remoteExtnsionsCount = d->absentExtensions.count();
|
||||
const int installedPluginsCount = PluginManager::plugins().count();
|
||||
return remoteExtnsionsCount + installedPluginsCount;
|
||||
}
|
||||
|
||||
static QVariant dataFromPluginSpec(const PluginSpec *pluginSpec, int role)
|
||||
{
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
case RoleName:
|
||||
return pluginSpec->name();
|
||||
case RoleCopyright:
|
||||
return pluginSpec->copyright();
|
||||
case RoleDependencies: {
|
||||
QStringList dependencies = transform(pluginSpec->dependencies(),
|
||||
&PluginDependency::toString);
|
||||
dependencies.sort();
|
||||
return dependencies;
|
||||
}
|
||||
case RoleDescriptionImages:
|
||||
break;
|
||||
case RoleDescriptionLinks: {
|
||||
const QString url = pluginSpec->url();
|
||||
if (!url.isEmpty()) {
|
||||
const LinksData links = {{{}, url}};
|
||||
return QVariant::fromValue(links);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RoleDescriptionText: {
|
||||
QStringList lines = pluginSpec->description().split('\n', Qt::SkipEmptyParts);
|
||||
lines.append(pluginSpec->longDescription().split('\n', Qt::SkipEmptyParts));
|
||||
const TextData text = {{ pluginSpec->name(), lines }};
|
||||
return QVariant::fromValue(text);
|
||||
}
|
||||
case RoleItemType:
|
||||
return ItemTypeExtension;
|
||||
case RoleLicense:
|
||||
return pluginSpec->license();
|
||||
case RoleLocation:
|
||||
return pluginSpec->filePath().toVariant();
|
||||
case RolePlatforms: {
|
||||
const QString pattern = pluginSpec->platformSpecification().pattern();
|
||||
const QStringList platforms = pattern.isEmpty()
|
||||
? QStringList({"macOS", "Windows", "Linux"})
|
||||
: QStringList(pattern);
|
||||
return platforms;
|
||||
}
|
||||
case RoleSize:
|
||||
return pluginSpec->filePath().fileSize();
|
||||
case RoleTags:
|
||||
break;
|
||||
case RoleVendor:
|
||||
return pluginSpec->vendor();
|
||||
case RoleVersion:
|
||||
return pluginSpec->version();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return {};
|
||||
return d->extensions.count();
|
||||
}
|
||||
|
||||
static QStringList dependenciesFromExtension(const Extension &extension)
|
||||
@@ -371,27 +368,18 @@ QVariant ExtensionsModel::data(const QModelIndex &index, int role) const
|
||||
if (role == RoleSearchText)
|
||||
return searchText(index);
|
||||
|
||||
const bool itemIsLocalPlugin = index.row() >= d->absentExtensions.count();
|
||||
if (itemIsLocalPlugin) {
|
||||
const PluginSpecs &pluginSpecs = PluginManager::plugins();
|
||||
const int pluginIndex = index.row() - d->absentExtensions.count();
|
||||
QTC_ASSERT(pluginIndex >= 0 && pluginIndex <= pluginSpecs.size(), return {});
|
||||
const PluginSpec *plugin = pluginSpecs.at(pluginIndex);
|
||||
return dataFromPluginSpec(plugin, role);
|
||||
} else {
|
||||
const Extension &extension = d->absentExtensions.at(index.row());
|
||||
const Extension &extension = d->extensions.at(index.row());
|
||||
const QVariant extensionData = dataFromExtension(extension, role);
|
||||
|
||||
// If data is unavailable, retrieve it from the first contained plugin
|
||||
if (extensionData.isNull() && !extension.plugins.isEmpty()) {
|
||||
const PluginSpec *pluginSpec = ExtensionsModel::pluginSpecForName(
|
||||
extension.plugins.constFirst().name);
|
||||
if (pluginSpec)
|
||||
return dataFromPluginSpec(pluginSpec, role);
|
||||
const QString firstPluginName = extension.plugins.constFirst().name;
|
||||
const Extension firstPluginExtension =
|
||||
findOrDefault(d->extensions, Utils::equal(&Extension::name, firstPluginName));
|
||||
if (firstPluginExtension.name.isEmpty())
|
||||
return {};
|
||||
return dataFromExtension(firstPluginExtension, role);
|
||||
}
|
||||
return extensionData;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void ExtensionsModel::setExtensionsJson(const QByteArray &json)
|
||||
@@ -402,7 +390,7 @@ void ExtensionsModel::setExtensionsJson(const QByteArray &json)
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
PluginSpec *ExtensionsModel::pluginSpecForName(const QString &pluginName)
|
||||
PluginSpec *pluginSpecForName(const QString &pluginName)
|
||||
{
|
||||
return findOrDefault(PluginManager::plugins(), equal(&PluginSpec::name, pluginName));
|
||||
}
|
||||
|
||||
@@ -54,12 +54,13 @@ public:
|
||||
QVariant data(const QModelIndex &index, int role) const;
|
||||
|
||||
void setExtensionsJson(const QByteArray &json);
|
||||
static ExtensionSystem::PluginSpec *pluginSpecForName(const QString &pluginName);
|
||||
|
||||
private:
|
||||
class ExtensionsModelPrivate *d = nullptr;
|
||||
};
|
||||
|
||||
ExtensionSystem::PluginSpec *pluginSpecForName(const QString &pluginName);
|
||||
|
||||
#ifdef WITH_TESTS
|
||||
QObject *createExtensionsModelTest();
|
||||
#endif
|
||||
|
||||
BIN
src/plugins/extensionmanager/images/extensionbig.png
Normal file
|
After Width: | Height: | Size: 509 B |
BIN
src/plugins/extensionmanager/images/extensionbig@2x.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
src/plugins/extensionmanager/images/packbig.png
Normal file
|
After Width: | Height: | Size: 455 B |
BIN
src/plugins/extensionmanager/images/packbig@2x.png
Normal file
|
After Width: | Height: | Size: 785 B |
71
src/plugins/extensionmanager/testdata/augmentedplugindata.json
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"name": "ScreenRecorder",
|
||||
"description": {
|
||||
"paragraphs": [
|
||||
{
|
||||
"header": "Screen Recorder plugin",
|
||||
"text": [
|
||||
"With FFmpeg, you can record your screens and save the recordings as animated images or videos.",
|
||||
"To record screens:",
|
||||
"",
|
||||
"- Select Tools > Screen Recording.",
|
||||
"- Select to select the screen to record from and to set the recorded screen area.",
|
||||
"- Select to start recording.",
|
||||
"- Select when you are done recording.",
|
||||
"- Select Crop and Trim to edit the recording.",
|
||||
"- Select Export to save the recording as an animated image or a video."
|
||||
]
|
||||
},
|
||||
{
|
||||
"header": "Set the screen and area to record",
|
||||
"text": [
|
||||
"Set the screen and the area to record in the Screen Recording Options dialog.",
|
||||
"To select a screen and area:",
|
||||
"",
|
||||
"- In Display, select the display to record.",
|
||||
"- In Recorded screen area, drag the guides to set the x and y coordinates of the starting point for the recording area, as well as the width and height of the area.",
|
||||
"- Select OK to return to the Record Screen dialog."
|
||||
]
|
||||
}
|
||||
],
|
||||
"images": [
|
||||
{
|
||||
"image_label": "Create animated imges like this",
|
||||
"url": "https://bugreports.qt.io/secure/attachment/156058/156058_DragAndCopyOnLinux.gif"
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"link_text": "Documentation",
|
||||
"url": "https://doc.qt.io/qtcreator/creator-how-to-record-screens.html"
|
||||
},
|
||||
{
|
||||
"link_text": "Homepage",
|
||||
"url": "https://www.qt.io/"
|
||||
}
|
||||
]
|
||||
},
|
||||
"is_pack": false,
|
||||
"plugins": [
|
||||
{
|
||||
"meta_data": {
|
||||
"Name": "ScreenRecorder",
|
||||
"Dependencies": [
|
||||
{
|
||||
"meta_data": {
|
||||
"Name": "Core",
|
||||
"Version": "13.0.2"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Version": "13.0.2"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [ "Utility", "Docs" ],
|
||||
"vendor": "The Qt Company Ltd"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -121,41 +121,6 @@
|
||||
"plugins": [
|
||||
{ "meta_data": { "Name": "Designer" } }
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"name": "SpellChecker",
|
||||
"tags": [ "Editor" ],
|
||||
"platforms": [ "macOS", "Windows", "Linux" ],
|
||||
"license": "os",
|
||||
"is_pack": false,
|
||||
"description": {
|
||||
"paragraphs": [
|
||||
{
|
||||
"text": [
|
||||
"Spellcheck comments in source files."
|
||||
],
|
||||
"header": "Get started"
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"url": "https://github.com/CJCombrink/SpellChecker-Plugin",
|
||||
"link_text": "GitHub page"
|
||||
}
|
||||
]
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
"meta_data": {
|
||||
"Name": "SpellChecker",
|
||||
"Copyright": "(C) 2015 - 2024 Carel Combrink"
|
||||
},
|
||||
"url": "https://github.com/CJCombrink/SpellChecker-Plugin/releases/download/v3.6.0/SpellChecker-Plugin_QtC13.0.0_macos_x64.tar.gz"
|
||||
}
|
||||
],
|
||||
"vendor": "Carel Combrink",
|
||||
"copyright": "(C) 2015 - 2024 Carel Combrink"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
76
src/plugins/extensionmanager/testdata/varieddata.json
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"name": "Few tags",
|
||||
"tags": [ "Tag one", "Tag two"]
|
||||
},
|
||||
|
||||
{
|
||||
"name": "Many tags",
|
||||
"tags": [ "Tag_01", "Tag_02", "Tag_03", "Tag_04", "Tag_05", "Tag_06", "Tag_07", "Tag_08", "Tag_09", "Tag_10", "Tag_11", "Tag_12", "Tag_13", "Tag_14", "Tag_15", "Tag_16", "Tag_17", "Tag_18", "Tag_19", "Tag_20", "Tag_21", "Tag_22", "Tag_23", "Tag_24", "Tag_25", "Tag_26", "Tag_27", "Tag_28", "Tag_29", "Tag_30", "And_a_very_long_tag_without_spaces", "Ok, a last long tag without spaces, but that sgould be enough"],
|
||||
"description": {
|
||||
"paragraphs": [
|
||||
{
|
||||
"text": [
|
||||
"... and a few long ones"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "One static image",
|
||||
"description": {
|
||||
"paragraphs": [
|
||||
{
|
||||
"text": [
|
||||
"png"
|
||||
]
|
||||
}
|
||||
],
|
||||
"images": [
|
||||
{
|
||||
"image_label": "Screenshot",
|
||||
"url": "https://bugreports.qt.io/secure/attachment/147354/VirtualNodesShownAsNotExisting.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "One animated image",
|
||||
"description": {
|
||||
"paragraphs": [
|
||||
{
|
||||
"text": [
|
||||
"gif (animated)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"images": [
|
||||
{
|
||||
"image_label": "Screencast",
|
||||
"url": "https://bugreports.qt.io/secure/attachment/156058/156058_DragAndCopyOnLinux.gif"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "Vendor, no download count",
|
||||
"vendor": "Vendor name"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "No vendor, but download count",
|
||||
"download_count": 12345
|
||||
},
|
||||
|
||||
{
|
||||
"name": "Vendor and download count",
|
||||
"vendor": "Vendor name",
|
||||
"download_count": 12345
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -76,7 +76,12 @@ protected:
|
||||
}
|
||||
|
||||
private:
|
||||
bool canFetchMore() const override { return m_client && !m_fetchedChildren; }
|
||||
bool canFetchMore() const override
|
||||
{
|
||||
if (m_client && !m_fetchedChildren)
|
||||
const_cast<HierarchyItem*>(this)->fetchMore();
|
||||
return false;
|
||||
}
|
||||
|
||||
void fetchMore() override
|
||||
{
|
||||
@@ -96,8 +101,6 @@ private:
|
||||
appendChild(new HierarchyItem(getSourceItem(item), m_client));
|
||||
}
|
||||
}
|
||||
if (!hasChildren())
|
||||
update();
|
||||
});
|
||||
m_client->sendMessage(request);
|
||||
}
|
||||
@@ -267,6 +270,7 @@ public:
|
||||
m_view->setModel(&m_model);
|
||||
m_view->setActivationMode(SingleClickActivation);
|
||||
m_view->setItemDelegate(&m_delegate);
|
||||
m_view->setUniformRowHeights(true);
|
||||
|
||||
theWidget->setLayout(new QVBoxLayout);
|
||||
theWidget->layout()->addWidget(m_view);
|
||||
@@ -432,7 +436,7 @@ public:
|
||||
Icons::RELOAD_TOOLBAR.icon();
|
||||
auto button = new QToolButton;
|
||||
button->setIcon(Icons::RELOAD_TOOLBAR.icon());
|
||||
button->setToolTip(LanguageClient::Tr::tr(
|
||||
button->setToolTip(::LanguageClient::Tr::tr(
|
||||
"Reloads the call hierarchy for the symbol under cursor position."));
|
||||
connect(button, &QToolButton::clicked, this, [h] { h->updateHierarchyAtCursorPosition(); });
|
||||
return {h, {button}};
|
||||
|
||||
@@ -1789,6 +1789,11 @@ const DynamicCapabilities &Client::dynamicCapabilities() const
|
||||
return d->m_dynamicCapabilities;
|
||||
}
|
||||
|
||||
DynamicCapabilities &Client::dynamicCapabilities()
|
||||
{
|
||||
return d->m_dynamicCapabilities;
|
||||
}
|
||||
|
||||
DocumentSymbolCache *Client::documentSymbolCache()
|
||||
{
|
||||
return &d->m_documentSymbolCache;
|
||||
|
||||
@@ -97,6 +97,7 @@ public:
|
||||
QString serverName() const;
|
||||
QString serverVersion() const;
|
||||
const DynamicCapabilities &dynamicCapabilities() const;
|
||||
DynamicCapabilities &dynamicCapabilities();
|
||||
void registerCapabilities(const QList<LanguageServerProtocol::Registration> ®istrations);
|
||||
void unregisterCapabilities(const QList<LanguageServerProtocol::Unregistration> &unregistrations);
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "languageclient_global.h"
|
||||
|
||||
#include <languageserverprotocol/client.h>
|
||||
|
||||
namespace LanguageClient {
|
||||
@@ -21,7 +23,7 @@ public:
|
||||
|
||||
void disable()
|
||||
{
|
||||
m_enabled = true;
|
||||
m_enabled = false;
|
||||
m_id.clear();
|
||||
m_options = QJsonValue();
|
||||
}
|
||||
@@ -37,7 +39,7 @@ private:
|
||||
|
||||
};
|
||||
|
||||
class DynamicCapabilities
|
||||
class LANGUAGECLIENT_EXPORT DynamicCapabilities
|
||||
{
|
||||
public:
|
||||
DynamicCapabilities() = default;
|
||||
|
||||
@@ -387,6 +387,7 @@ void LanguageClientManager::enableClientSettings(const QString &settingsId, bool
|
||||
QList<Client *> LanguageClientManager::clientsForSetting(const BaseSettings *setting)
|
||||
{
|
||||
QTC_ASSERT(managerInstance, return {});
|
||||
QTC_ASSERT(setting, return {});
|
||||
auto instance = managerInstance;
|
||||
return instance->m_clientsForSetting.value(setting->m_id);
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ public:
|
||||
std::optional<sol::protected_function> m_startFailedCallback;
|
||||
QMap<QString, sol::protected_function> m_messageCallbacks;
|
||||
|
||||
QList<Client *> m_clients;
|
||||
LuaClientSettings *m_settings{nullptr};
|
||||
|
||||
public:
|
||||
static BaseSettings::StartBehavior startBehaviorFromString(const QString &str)
|
||||
@@ -190,6 +190,8 @@ public:
|
||||
throw sol::error("Unknown start behavior: " + str.toStdString());
|
||||
}
|
||||
|
||||
void setSettings(LuaClientSettings *settings) { m_settings = settings; }
|
||||
|
||||
LuaClientWrapper(const sol::table &options)
|
||||
{
|
||||
m_cmdLineCallback = addValue<CommandLine>(
|
||||
@@ -267,8 +269,6 @@ public:
|
||||
auto luaClient = qobject_cast<LuaClient *>(c);
|
||||
if (luaClient && luaClient->m_settingsId == m_settingsTypeId && m_onInstanceStart) {
|
||||
QTC_CHECK(::Lua::LuaEngine::void_safe_call(*m_onInstanceStart, c));
|
||||
|
||||
m_clients.push_back(c);
|
||||
updateMessageCallbacks();
|
||||
}
|
||||
});
|
||||
@@ -286,22 +286,11 @@ public:
|
||||
if (!luaClient || luaClient->m_settingsId != m_settingsTypeId)
|
||||
return;
|
||||
|
||||
if (m_clients.contains(c))
|
||||
m_clients.removeOne(c);
|
||||
|
||||
if (unexpected && m_startFailedCallback) {
|
||||
QTC_CHECK_EXPECTED(::Lua::LuaEngine::void_safe_call(*m_startFailedCallback));
|
||||
}
|
||||
}
|
||||
|
||||
~LuaClientWrapper()
|
||||
{
|
||||
for (auto client : m_clients)
|
||||
LanguageClientManager::shutdownClient(client);
|
||||
|
||||
// TODO: Unregister Client settings from LanguageClientManager
|
||||
}
|
||||
|
||||
TransportType transportType() { return m_transportType; }
|
||||
|
||||
void applySettings()
|
||||
@@ -340,7 +329,9 @@ public:
|
||||
|
||||
void updateMessageCallbacks()
|
||||
{
|
||||
for (Client *c : m_clients) {
|
||||
for (Client *c : LanguageClientManager::clientsForSetting(m_settings)) {
|
||||
if (!c)
|
||||
continue;
|
||||
for (const auto &[msg, func] : m_messageCallbacks.asKeyValueRange()) {
|
||||
c->registerCustomMethod(
|
||||
msg,
|
||||
@@ -367,9 +358,11 @@ public:
|
||||
if (!messageValue.isObject())
|
||||
throw sol::error("Message is not an object");
|
||||
const LanguageServerProtocol::JsonRpcMessage jsonrpcmessage(messageValue.toObject());
|
||||
for (Client *c : m_clients)
|
||||
for (Client *c : LanguageClientManager::clientsForSetting(m_settings)) {
|
||||
if (c)
|
||||
c->sendMessage(jsonrpcmessage);
|
||||
}
|
||||
}
|
||||
|
||||
void updateOptions()
|
||||
{
|
||||
@@ -536,6 +529,7 @@ static void registerLuaApi()
|
||||
[](const sol::table &options) -> std::shared_ptr<LuaClientWrapper> {
|
||||
auto luaClient = std::make_shared<LuaClientWrapper>(options);
|
||||
auto client = new LuaClientSettings(luaClient);
|
||||
luaClient->setSettings(client);
|
||||
|
||||
// The order is important!
|
||||
// First restore the settings ...
|
||||
|
||||
@@ -59,7 +59,7 @@ void addFetchModule()
|
||||
LuaOptionsPage(Module *module)
|
||||
{
|
||||
setId("BB.Lua.Fetch");
|
||||
setDisplayName(Tr::tr("Network access"));
|
||||
setDisplayName(Tr::tr("Network Access"));
|
||||
setCategory("ZY.Lua");
|
||||
setDisplayCategory("Lua");
|
||||
setCategoryIconPath(":/lua/images/settingscategory_lua.png");
|
||||
@@ -176,8 +176,8 @@ void addFetchModule()
|
||||
// so we have to use a QMessageBox instead of the info bar
|
||||
auto msgBox = new QMessageBox(
|
||||
QMessageBox::Question,
|
||||
Tr::tr("Allow Internet access"),
|
||||
Tr::tr("The plugin \"%1\" would like to fetch from the following url:\n%2")
|
||||
Tr::tr("Allow Internet Access"),
|
||||
Tr::tr("Allow the extension \"%1\" to fetch from the following URL:\n%2")
|
||||
.arg(pluginName)
|
||||
.arg(url),
|
||||
QMessageBox::Yes | QMessageBox::No,
|
||||
@@ -205,14 +205,13 @@ void addFetchModule()
|
||||
|
||||
Utils::InfoBarEntry entry{
|
||||
Utils::Id::fromString("Fetch" + pluginName),
|
||||
Tr::tr("The plugin \"%1\" would like to fetch data from the internet. Do "
|
||||
"you want to allow this?")
|
||||
Tr::tr("Allow the extension \"%1\" to fetch data from the internet?")
|
||||
.arg(pluginName)};
|
||||
entry.setDetailsWidgetCreator([pluginName, url] {
|
||||
const QString markdown = Tr::tr("The plugin \"**%1**\" would like to fetch "
|
||||
"from the following url:\n\n")
|
||||
.arg(pluginName)
|
||||
+ QString("* [%3](%3)").arg(url);
|
||||
const QString markdown = Tr::tr("Allow the extension \"%1\" to fetch data"
|
||||
"from the following URL:\n\n")
|
||||
.arg("**" + pluginName + "**")
|
||||
+ QString("* [%1](%1)").arg(url);
|
||||
|
||||
QLabel *list = new QLabel();
|
||||
list->setTextFormat(Qt::TextFormat::MarkdownText);
|
||||
@@ -225,7 +224,7 @@ void addFetchModule()
|
||||
Core::ICore::infoBar()->removeInfo(Utils::Id::fromString("Fetch" + pluginName));
|
||||
fetch();
|
||||
});
|
||||
entry.addCustomButton(Tr::tr("Allow once"), [pluginName, fetch]() {
|
||||
entry.addCustomButton(Tr::tr("Allow Once"), [pluginName, fetch]() {
|
||||
Core::ICore::infoBar()->removeInfo(Utils::Id::fromString("Fetch" + pluginName));
|
||||
fetch();
|
||||
});
|
||||
@@ -311,8 +310,8 @@ void addFetchModule()
|
||||
};
|
||||
|
||||
checkPermission(url, actualFetch, [callback, pluginName]() {
|
||||
callback(Tr::tr("Fetching is not allowed for the plugin \"%1\" (You can edit "
|
||||
"permissions in Preferences => Lua)")
|
||||
callback(Tr::tr("Fetching is not allowed for the extension \"%1\". (You can edit "
|
||||
"permissions in Preferences > Lua.)")
|
||||
.arg(pluginName));
|
||||
});
|
||||
};
|
||||
|
||||
@@ -50,7 +50,7 @@ expected_str<QJsonDocument> getPackageInfo(const FilePath &appDataPath)
|
||||
return make_unexpected(error.errorString());
|
||||
|
||||
if (!doc.isObject())
|
||||
return make_unexpected(Tr::tr("Package info is not an object"));
|
||||
return make_unexpected(Tr::tr("Package info is not an object."));
|
||||
|
||||
return doc;
|
||||
}
|
||||
@@ -66,7 +66,7 @@ expected_str<QJsonObject> getInstalledPackageInfo(const FilePath &appDataPath, c
|
||||
if (root.contains(name)) {
|
||||
QJsonValue v = root[name];
|
||||
if (!v.isObject())
|
||||
return make_unexpected(Tr::tr("Installed package info is not an object"));
|
||||
return make_unexpected(Tr::tr("Installed package info is not an object."));
|
||||
return v.toObject();
|
||||
}
|
||||
|
||||
@@ -86,12 +86,12 @@ expected_str<QJsonDocument> getOrCreatePackageInfo(const FilePath &appDataPath)
|
||||
expected_str<void> savePackageInfo(const FilePath &appDataPath, const QJsonDocument &doc)
|
||||
{
|
||||
if (!appDataPath.ensureWritableDir())
|
||||
return make_unexpected(Tr::tr("Could not create app data directory"));
|
||||
return make_unexpected(Tr::tr("Cannot create app data directory."));
|
||||
|
||||
const FilePath packageInfoPath = appDataPath / "package.json";
|
||||
return packageInfoPath.writeFileContents(doc.toJson())
|
||||
.transform_error([](const QString &error) {
|
||||
return Tr::tr("Could not write to package info: %1").arg(error);
|
||||
return Tr::tr("Cannot write to package info: %1").arg(error);
|
||||
})
|
||||
.transform([](qint64) { return; });
|
||||
}
|
||||
@@ -147,7 +147,7 @@ static Group installRecipe(
|
||||
const auto size = reply->size();
|
||||
const auto written = storage->write(reply->readAll());
|
||||
if (written != size)
|
||||
return emitResult(Tr::tr("Could not write to temporary file"));
|
||||
return emitResult(Tr::tr("Cannot write to temporary file."));
|
||||
storage->close();
|
||||
return DoneResult::Success;
|
||||
};
|
||||
@@ -169,7 +169,7 @@ static Group installRecipe(
|
||||
|
||||
const auto onUnarchiverDone = [appDataPath, installOptionsIt, emitResult](DoneWith result) {
|
||||
if (result == DoneWith::Error)
|
||||
return emitResult(Tr::tr("Unarchiving failed"));
|
||||
return emitResult(Tr::tr("Unarchiving failed."));
|
||||
if (result == DoneWith::Cancel)
|
||||
return DoneResult::Error;
|
||||
|
||||
@@ -212,7 +212,7 @@ static Group installRecipe(
|
||||
}
|
||||
|
||||
if (!storage->open(QIODevice::WriteOnly)) {
|
||||
emitResult(Tr::tr("Could not open temporary file"));
|
||||
emitResult(Tr::tr("Cannot open temporary file."));
|
||||
return SetupResult::StopWithError;
|
||||
}
|
||||
return SetupResult::Continue;
|
||||
@@ -326,16 +326,17 @@ void addInstallModule()
|
||||
if (QApplication::activeModalWidget()) {
|
||||
auto msgBox = new QMessageBox(
|
||||
QMessageBox::Question,
|
||||
Tr::tr("Install package"),
|
||||
Tr::tr("Install Package"),
|
||||
msg,
|
||||
QMessageBox::Yes | QMessageBox::No,
|
||||
Core::ICore::dialogParent());
|
||||
|
||||
const QString details
|
||||
= Tr::tr("The plugin \"%1\" would like to install the following "
|
||||
= Tr::tr("The extension \"%1\" wants to install the following "
|
||||
"package(s):\n\n")
|
||||
.arg(pluginSpec->name)
|
||||
+ Utils::transform(installOptionsList, [](const InstallOptions &options) {
|
||||
//: %1 = package name, %2 = version, %3 = URL
|
||||
return QString("* %1 - %2 (from: %3)")
|
||||
.arg(options.name, options.version, options.url.toString());
|
||||
}).join("\n");
|
||||
@@ -363,11 +364,12 @@ void addInstallModule()
|
||||
entry.setCancelButtonInfo(denied);
|
||||
|
||||
const QString details
|
||||
= Tr::tr("The plugin \"**%1**\" would like to install the following "
|
||||
= Tr::tr("The extension \"%1\" wants to install the following "
|
||||
"package(s):\n\n")
|
||||
.arg(pluginSpec->name)
|
||||
.arg("**" + pluginSpec->name + "**") // markdown bold
|
||||
+ Utils::transform(installOptionsList, [](const InstallOptions &options) {
|
||||
return QString("* %1 - %2 (from: [%3](%3))")
|
||||
//: Markdown list item: %1 = package name, %2 = version, %3 = URL
|
||||
return Tr::tr("* %1 - %2 (from: [%3](%3))")
|
||||
.arg(options.name, options.version, options.url.toString());
|
||||
}).join("\n");
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ std::unique_ptr<Utils::LuaState> LuaEngine::runScript(
|
||||
sol::error err = result;
|
||||
qWarning() << "Failed to run script" << name << ":" << QString::fromUtf8(err.what());
|
||||
Core::MessageManager::writeFlashing(
|
||||
tr("Failed to run script %1: %2").arg(name, QString::fromUtf8(err.what())));
|
||||
Tr::tr("Failed to run script %1: %2").arg(name, QString::fromUtf8(err.what())));
|
||||
}
|
||||
|
||||
return opaque;
|
||||
@@ -168,7 +168,7 @@ expected_str<void> LuaEngine::connectHooks(
|
||||
QString hookName = QStringList{path, k.as<QString>()}.join(".");
|
||||
auto it = d->m_hooks.find(hookName);
|
||||
if (it == d->m_hooks.end())
|
||||
return make_unexpected(QString("No hook named '%1' found").arg(hookName));
|
||||
return make_unexpected(Tr::tr("No hook with the name \"%1\" found.").arg(hookName));
|
||||
else
|
||||
it.value()(v.as<sol::function>());
|
||||
}
|
||||
@@ -269,7 +269,7 @@ expected_str<sol::protected_function> LuaEngine::prepareSetup(
|
||||
|
||||
auto pluginTable = result.get<sol::optional<sol::table>>();
|
||||
if (!pluginTable)
|
||||
return make_unexpected(Tr::tr("Script did not return a table"));
|
||||
return make_unexpected(Tr::tr("Script did not return a table."));
|
||||
|
||||
auto hookTable = pluginTable->get<sol::optional<sol::table>>("hooks");
|
||||
|
||||
@@ -282,7 +282,7 @@ expected_str<sol::protected_function> LuaEngine::prepareSetup(
|
||||
auto setupFunction = pluginTable->get_or<sol::function>("setup", {});
|
||||
|
||||
if (!setupFunction)
|
||||
return make_unexpected(Tr::tr("Plugin info table did not contain a setup function"));
|
||||
return make_unexpected(Tr::tr("Extension info table did not contain a setup function."));
|
||||
|
||||
return setupFunction;
|
||||
}
|
||||
|
||||
@@ -108,19 +108,19 @@ bool LuaPluginSpec::initializePlugin()
|
||||
= LuaEngine::instance().prepareSetup(*activeLuaState, *this);
|
||||
|
||||
if (!setupResult) {
|
||||
setError(Lua::Tr::tr("Failed to prepare plugin setup: %1").arg(setupResult.error()));
|
||||
setError(Lua::Tr::tr("Cannot prepare extension setup: %1").arg(setupResult.error()));
|
||||
return false;
|
||||
}
|
||||
|
||||
auto result = setupResult->call();
|
||||
|
||||
if (result.get_type() == sol::type::boolean && result.get<bool>() == false) {
|
||||
setError(Lua::Tr::tr("Plugin setup function returned false"));
|
||||
setError(Lua::Tr::tr("Extension setup function returned false."));
|
||||
return false;
|
||||
} else if (result.get_type() == sol::type::string) {
|
||||
std::string error = result.get<sol::error>().what();
|
||||
if (!error.empty()) {
|
||||
setError(Lua::Tr::tr("Plugin setup function returned error: %1")
|
||||
setError(Lua::Tr::tr("Extension setup function returned error: %1")
|
||||
.arg(QString::fromStdString(error)));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -67,12 +67,12 @@ DeployMcuProcessStep::DeployMcuProcessStep(ProjectExplorer::BuildStepList *bc, I
|
||||
, m_tmpDir()
|
||||
{
|
||||
if (!buildSystem()) {
|
||||
showError(QmlProjectManager::Tr::tr("Failed to find valid build system"));
|
||||
showError(QmlProjectManager::Tr::tr("Cannot find a valid build system."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_tmpDir.isValid()) {
|
||||
showError(QmlProjectManager::Tr::tr("Failed to create valid build directory"));
|
||||
showError(QmlProjectManager::Tr::tr("Cannot create a valid build directory."));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -193,7 +193,7 @@ void MCUBuildStepFactory::updateDeployStep(ProjectExplorer::Target *target, bool
|
||||
stepList->appendStep(DeployMcuProcessStep::id);
|
||||
} else {
|
||||
DeployMcuProcessStep::showError(
|
||||
QmlProjectManager::Tr::tr("Failed to find valid Qt for MCUs kit"));
|
||||
QmlProjectManager::Tr::tr("Cannot find a valid Qt for MCUs kit."));
|
||||
}
|
||||
} else {
|
||||
if (!step)
|
||||
|
||||
@@ -320,7 +320,7 @@ void McuSupportOptionsWidget::apply()
|
||||
|
||||
QMessageBox warningPopup(QMessageBox::Icon::Warning,
|
||||
Tr::tr("Warning"),
|
||||
Tr::tr("Unable to apply changes in Devices > MCU."),
|
||||
Tr::tr("Cannot apply changes in Devices > MCU."),
|
||||
QMessageBox::Ok,
|
||||
this);
|
||||
|
||||
|
||||
@@ -448,8 +448,10 @@ void ElementTasks::openLinkedFile(const qmt::MElement *element)
|
||||
(void) Core::EditorManager::openEditor(filepath);
|
||||
}
|
||||
} else {
|
||||
QMessageBox::critical(Core::ICore::dialogParent(), Tr::tr("Opening File"),
|
||||
Tr::tr("File %1 does not exist.").arg(filepath.toUserOutput()));
|
||||
QMessageBox::critical(
|
||||
Core::ICore::dialogParent(),
|
||||
Tr::tr("Opening File"),
|
||||
Tr::tr("File \"%1\" does not exist.").arg(filepath.toUserOutput()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,8 +257,10 @@ void ExtPropertiesMView::onImagePathChanged(const QString &path)
|
||||
assignModelElement<qmt::DObject, QImage>(m_diagramElements, SelectionSingle, image,
|
||||
&qmt::DObject::image, &qmt::DObject::setImage);
|
||||
} else {
|
||||
QMessageBox::critical(Core::ICore::dialogParent(), Tr::tr("Selecting Image"),
|
||||
Tr::tr("Unable to read image file %1").arg(path));
|
||||
QMessageBox::critical(
|
||||
Core::ICore::dialogParent(),
|
||||
Tr::tr("Selecting Image"),
|
||||
Tr::tr("Unable to read image file \"%1\".").arg(path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,7 +140,6 @@ add_qtc_plugin(ProjectExplorer
|
||||
projectmanager.cpp projectmanager.h
|
||||
projectmodels.cpp projectmodels.h
|
||||
projectnodes.cpp projectnodes.h
|
||||
projectnodeshelper.h
|
||||
projectpanelfactory.cpp projectpanelfactory.h
|
||||
projectsettingswidget.cpp projectsettingswidget.h
|
||||
projecttree.cpp projecttree.h
|
||||
|
||||
@@ -57,7 +57,7 @@ BuildPropertiesSettings::BuildPropertiesSettings()
|
||||
buildDirectoryTemplate.setToolTip(
|
||||
Tr::tr("Template used to construct the default build directory.<br><br>"
|
||||
"The default value can be set using the environment variable "
|
||||
"<tt>%1</tt>")
|
||||
"<tt>%1</tt>.")
|
||||
.arg(Constants::QTC_DEFAULT_BUILD_DIRECTORY_TEMPLATE));
|
||||
buildDirectoryTemplate.setUseResetButton();
|
||||
|
||||
|
||||
@@ -75,12 +75,11 @@ QStringList SshParameters::connectionOptions(const FilePath &binary) const
|
||||
return args;
|
||||
}
|
||||
|
||||
bool SshParameters::setupSshEnvironment(Process *process)
|
||||
void SshParameters::setupSshEnvironment(Process *process)
|
||||
{
|
||||
Environment env = process->controlEnvironment();
|
||||
if (!env.hasChanges())
|
||||
env = Environment::systemEnvironment();
|
||||
const bool hasDisplay = env.hasKey("DISPLAY") && (env.value("DISPLAY") != QString(":0"));
|
||||
if (SshSettings::askpassFilePath().exists()) {
|
||||
env.set("SSH_ASKPASS", SshSettings::askpassFilePath().toUserOutput());
|
||||
env.set("SSH_ASKPASS_REQUIRE", "force");
|
||||
@@ -93,7 +92,6 @@ bool SshParameters::setupSshEnvironment(Process *process)
|
||||
|
||||
// Otherwise, ssh will ignore SSH_ASKPASS and read from /dev/tty directly.
|
||||
process->setDisableUnixTerminal();
|
||||
return hasDisplay;
|
||||
}
|
||||
|
||||
bool operator==(const SshParameters &p1, const SshParameters &p2)
|
||||
|
||||
@@ -46,7 +46,7 @@ public:
|
||||
AuthenticationType authenticationType = AuthenticationTypeAll;
|
||||
SshHostKeyCheckingMode hostKeyCheckingMode = SshHostKeyCheckingAllowNoMatch;
|
||||
|
||||
static bool setupSshEnvironment(Utils::Process *process);
|
||||
static void setupSshEnvironment(Utils::Process *process);
|
||||
|
||||
friend PROJECTEXPLORER_EXPORT bool operator==(const SshParameters &p1, const SshParameters &p2);
|
||||
friend bool operator!=(const SshParameters &p1, const SshParameters &p2) { return !(p1 == p2); }
|
||||
|
||||
@@ -115,7 +115,6 @@ QtcPlugin {
|
||||
"projectmanager.cpp", "projectmanager.h",
|
||||
"projectmodels.cpp", "projectmodels.h",
|
||||
"projectnodes.cpp", "projectnodes.h",
|
||||
"projectnodeshelper.h",
|
||||
"projectpanelfactory.cpp", "projectpanelfactory.h",
|
||||
"projectsettingswidget.cpp", "projectsettingswidget.h",
|
||||
"projecttree.cpp",
|
||||
|
||||
@@ -310,10 +310,11 @@ ProjectExplorerSettingsWidget::ProjectExplorerSettingsWidget()
|
||||
{
|
||||
m_reaperTimeoutSpinBox = new QSpinBox;
|
||||
m_reaperTimeoutSpinBox->setMinimum(1);
|
||||
//: Suffix for "seconds"
|
||||
m_reaperTimeoutSpinBox->setSuffix(Tr::tr("s"));
|
||||
m_reaperTimeoutSpinBox->setToolTip(
|
||||
Tr::tr("The amount of seconds to wait between a \"soft kill\" and a \"hard kill\" of a "
|
||||
"running application"));
|
||||
"running application."));
|
||||
|
||||
m_currentDirectoryRadioButton = new QRadioButton(Tr::tr("Current directory"));
|
||||
m_directoryRadioButton = new QRadioButton(Tr::tr("Directory"));
|
||||
|
||||
@@ -284,8 +284,9 @@ bool FlatModel::setData(const QModelIndex &index, const QVariant &value, int rol
|
||||
QTC_ASSERT(node, return false);
|
||||
|
||||
std::vector<std::tuple<Node *, FilePath, FilePath>> toRename;
|
||||
const Utils::FilePath orgFilePath = node->filePath();
|
||||
const Utils::FilePath newFilePath = orgFilePath.parentDir().pathAppended(value.toString());
|
||||
const FilePath orgFilePath = node->filePath();
|
||||
const FilePath newFilePath = orgFilePath.parentDir().pathAppended(value.toString());
|
||||
const FilePath valuePath = FilePath::fromString(value.toString());
|
||||
const QFileInfo orgFileInfo = orgFilePath.toFileInfo();
|
||||
toRename.emplace_back(std::make_tuple(node, orgFilePath, newFilePath));
|
||||
|
||||
@@ -309,12 +310,13 @@ bool FlatModel::setData(const QModelIndex &index, const QVariant &value, int rol
|
||||
case QMessageBox::Yes:
|
||||
for (Node * const n : candidateNodes) {
|
||||
QString targetFilePath = orgFileInfo.absolutePath() + '/'
|
||||
+ newFilePath.completeBaseName();
|
||||
+ valuePath.parentDir().path() + '/'
|
||||
+ valuePath.completeBaseName();
|
||||
const QString suffix = n->filePath().suffix();
|
||||
if (!suffix.isEmpty())
|
||||
targetFilePath.append('.').append(suffix);
|
||||
toRename.emplace_back(std::make_tuple(n, n->filePath(),
|
||||
FilePath::fromString(targetFilePath)));
|
||||
FilePath::fromString(targetFilePath).cleanPath()));
|
||||
}
|
||||
break;
|
||||
case QMessageBox::Cancel:
|
||||
|
||||
@@ -1,141 +0,0 @@
|
||||
// Copyright (C) 2021 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "projectnodes.h"
|
||||
|
||||
#include <coreplugin/iversioncontrol.h>
|
||||
#include <coreplugin/vcsmanager.h>
|
||||
|
||||
#include <solutions/tasking/tasktreerunner.h>
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/async.h>
|
||||
#include <utils/filepath.h>
|
||||
#include <utils/hostosinfo.h>
|
||||
|
||||
#include <QPromise>
|
||||
|
||||
namespace ProjectExplorer {
|
||||
namespace Internal {
|
||||
|
||||
struct DirectoryScanResult
|
||||
{
|
||||
QList<FileNode *> nodes;
|
||||
Utils::FilePaths subDirectories;
|
||||
};
|
||||
|
||||
static DirectoryScanResult scanForFiles(
|
||||
const QFuture<void> &future,
|
||||
const Utils::FilePath &directory,
|
||||
QDir::Filters filter,
|
||||
const std::function<FileNode *(const Utils::FilePath &)> &factory,
|
||||
const QList<Core::IVersionControl *> &versionControls)
|
||||
{
|
||||
DirectoryScanResult result;
|
||||
|
||||
const Utils::FilePaths entries = directory.dirEntries(filter);
|
||||
for (const Utils::FilePath &entry : entries) {
|
||||
if (future.isCanceled())
|
||||
return result;
|
||||
|
||||
if (Utils::anyOf(versionControls, [entry](const Core::IVersionControl *vc) {
|
||||
return vc->isVcsFileOrDirectory(entry);
|
||||
})) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entry.isDir())
|
||||
result.subDirectories.append(entry);
|
||||
else if (FileNode *node = factory(entry))
|
||||
result.nodes.append(node);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename Result>
|
||||
QList<FileNode *> scanForFilesRecursively(
|
||||
QPromise<Result> &promise,
|
||||
int progressRange,
|
||||
const Utils::FilePath &directory,
|
||||
QDir::Filters filter,
|
||||
const std::function<FileNode *(const Utils::FilePath &)> &factory,
|
||||
const QList<Core::IVersionControl *> &versionControls)
|
||||
{
|
||||
const QFuture<void> future(promise.future());
|
||||
|
||||
QSet<Utils::FilePath> visited;
|
||||
const DirectoryScanResult result
|
||||
= scanForFiles(future, directory, filter, factory, versionControls);
|
||||
QList<FileNode *> fileNodes = result.nodes;
|
||||
const int progressIncrement = int(
|
||||
progressRange / static_cast<double>(fileNodes.count() + result.subDirectories.count()));
|
||||
promise.setProgressValue(int(fileNodes.count() * progressIncrement));
|
||||
QList<QPair<Utils::FilePath, int>> subDirectories;
|
||||
auto addSubDirectories = [&](const Utils::FilePaths &subdirs, int progressIncrement) {
|
||||
for (const Utils::FilePath &subdir : subdirs) {
|
||||
if (Utils::insert(visited, subdir.canonicalPath()))
|
||||
subDirectories.append(qMakePair(subdir, progressIncrement));
|
||||
else
|
||||
promise.setProgressValue(promise.future().progressValue() + progressIncrement);
|
||||
}
|
||||
};
|
||||
addSubDirectories(result.subDirectories, progressIncrement);
|
||||
|
||||
while (!subDirectories.isEmpty()) {
|
||||
using namespace Tasking;
|
||||
const LoopList iterator(subDirectories);
|
||||
subDirectories.clear();
|
||||
|
||||
auto onSetup = [&, iterator](Utils::Async<DirectoryScanResult> &task) {
|
||||
task.setConcurrentCallData(
|
||||
scanForFiles, future, iterator->first, filter, factory, versionControls);
|
||||
};
|
||||
|
||||
auto onDone = [&, iterator](const Utils::Async<DirectoryScanResult> &task) {
|
||||
const int progressRange = iterator->second;
|
||||
const DirectoryScanResult result = task.result();
|
||||
fileNodes.append(result.nodes);
|
||||
const qsizetype subDirCount = result.subDirectories.count();
|
||||
if (subDirCount == 0) {
|
||||
promise.setProgressValue(promise.future().progressValue() + progressRange);
|
||||
} else {
|
||||
const qsizetype fileCount = result.nodes.count();
|
||||
const int increment = int(
|
||||
progressRange / static_cast<double>(fileCount + subDirCount));
|
||||
promise.setProgressValue(
|
||||
promise.future().progressValue() + increment * fileCount);
|
||||
addSubDirectories(result.subDirectories, increment);
|
||||
}
|
||||
};
|
||||
|
||||
const Group group{
|
||||
Utils::HostOsInfo::isLinuxHost() ? parallelLimit(2) : parallelIdealThreadCountLimit,
|
||||
iterator,
|
||||
Utils::AsyncTask<DirectoryScanResult>(onSetup, onDone)
|
||||
};
|
||||
TaskTree::runBlocking(group);
|
||||
}
|
||||
return fileNodes;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
template<typename Result>
|
||||
QList<FileNode *> scanForFiles(
|
||||
QPromise<Result> &promise,
|
||||
const Utils::FilePath &directory,
|
||||
QDir::Filters filter,
|
||||
const std::function<FileNode *(const Utils::FilePath &)> &factory)
|
||||
{
|
||||
promise.setProgressRange(0, 1000000);
|
||||
return Internal::scanForFilesRecursively(promise,
|
||||
1000000,
|
||||
directory,
|
||||
filter,
|
||||
factory,
|
||||
Core::VcsManager::versionControls());
|
||||
}
|
||||
|
||||
} // namespace ProjectExplorer
|
||||
@@ -3,15 +3,17 @@
|
||||
|
||||
#include "treescanner.h"
|
||||
|
||||
#include "projectnodeshelper.h"
|
||||
#include "projecttree.h"
|
||||
|
||||
#include <coreplugin/iversioncontrol.h>
|
||||
#include <coreplugin/vcsmanager.h>
|
||||
|
||||
#include <utils/async.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <solutions/tasking/tasktreerunner.h>
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/async.h>
|
||||
#include <utils/hostosinfo.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
@@ -146,14 +148,113 @@ static std::unique_ptr<FolderNode> createFolderNode(const Utils::FilePath &direc
|
||||
return fileSystemNode;
|
||||
}
|
||||
|
||||
struct DirectoryScanResult
|
||||
{
|
||||
QList<FileNode *> nodes;
|
||||
Utils::FilePaths subDirectories;
|
||||
};
|
||||
|
||||
static DirectoryScanResult scanForFilesImpl(
|
||||
const QFuture<void> &future,
|
||||
const Utils::FilePath &directory,
|
||||
QDir::Filters filter,
|
||||
const std::function<FileNode *(const Utils::FilePath &)> &factory,
|
||||
const QList<Core::IVersionControl *> &versionControls)
|
||||
{
|
||||
DirectoryScanResult result;
|
||||
|
||||
const Utils::FilePaths entries = directory.dirEntries(filter);
|
||||
for (const Utils::FilePath &entry : entries) {
|
||||
if (future.isCanceled())
|
||||
return result;
|
||||
|
||||
if (Utils::anyOf(versionControls, [entry](const Core::IVersionControl *vc) {
|
||||
return vc->isVcsFileOrDirectory(entry);
|
||||
})) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entry.isDir())
|
||||
result.subDirectories.append(entry);
|
||||
else if (FileNode *node = factory(entry))
|
||||
result.nodes.append(node);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static QList<FileNode *> scanForFilesHelper(
|
||||
TreeScanner::Promise &promise,
|
||||
const Utils::FilePath &directory,
|
||||
QDir::Filters filter,
|
||||
const std::function<FileNode *(const Utils::FilePath &)> &factory)
|
||||
{
|
||||
const QFuture<void> future(promise.future());
|
||||
|
||||
const int progressRange = 1000000;
|
||||
const QList<Core::IVersionControl *> &versionControls = Core::VcsManager::versionControls();
|
||||
promise.setProgressRange(0, progressRange);
|
||||
|
||||
QSet<Utils::FilePath> visited;
|
||||
const DirectoryScanResult result = scanForFilesImpl(future, directory, filter, factory, versionControls);
|
||||
QList<FileNode *> fileNodes = result.nodes;
|
||||
const int progressIncrement = int(
|
||||
progressRange / static_cast<double>(fileNodes.count() + result.subDirectories.count()));
|
||||
promise.setProgressValue(int(fileNodes.count() * progressIncrement));
|
||||
QList<QPair<Utils::FilePath, int>> subDirectories;
|
||||
auto addSubDirectories = [&](const Utils::FilePaths &subdirs, int progressIncrement) {
|
||||
for (const Utils::FilePath &subdir : subdirs) {
|
||||
if (Utils::insert(visited, subdir.canonicalPath()))
|
||||
subDirectories.append(qMakePair(subdir, progressIncrement));
|
||||
else
|
||||
promise.setProgressValue(future.progressValue() + progressIncrement);
|
||||
}
|
||||
};
|
||||
addSubDirectories(result.subDirectories, progressIncrement);
|
||||
|
||||
while (!subDirectories.isEmpty()) {
|
||||
using namespace Tasking;
|
||||
const LoopList iterator(subDirectories);
|
||||
subDirectories.clear();
|
||||
|
||||
auto onSetup = [&, iterator](Utils::Async<DirectoryScanResult> &task) {
|
||||
task.setConcurrentCallData(
|
||||
scanForFilesImpl, future, iterator->first, filter, factory, versionControls);
|
||||
};
|
||||
|
||||
auto onDone = [&, iterator](const Utils::Async<DirectoryScanResult> &task) {
|
||||
const int progressRange = iterator->second;
|
||||
const DirectoryScanResult result = task.result();
|
||||
fileNodes.append(result.nodes);
|
||||
const qsizetype subDirCount = result.subDirectories.count();
|
||||
if (subDirCount == 0) {
|
||||
promise.setProgressValue(future.progressValue() + progressRange);
|
||||
} else {
|
||||
const qsizetype fileCount = result.nodes.count();
|
||||
const int increment = int(
|
||||
progressRange / static_cast<double>(fileCount + subDirCount));
|
||||
promise.setProgressValue(future.progressValue() + increment * fileCount);
|
||||
addSubDirectories(result.subDirectories, increment);
|
||||
}
|
||||
};
|
||||
|
||||
const Group group{
|
||||
Utils::HostOsInfo::isLinuxHost() ? parallelLimit(2) : parallelIdealThreadCountLimit,
|
||||
iterator,
|
||||
Utils::AsyncTask<DirectoryScanResult>(onSetup, onDone)
|
||||
};
|
||||
TaskTree::runBlocking(group);
|
||||
}
|
||||
return fileNodes;
|
||||
}
|
||||
|
||||
void TreeScanner::scanForFiles(
|
||||
Promise &promise,
|
||||
const Utils::FilePath &directory,
|
||||
const FileFilter &filter,
|
||||
const QDir::Filters &dirFilter,
|
||||
QDir::Filters dirFilter,
|
||||
const FileTypeFactory &factory)
|
||||
{
|
||||
QList<FileNode *> nodes = ProjectExplorer::scanForFiles(
|
||||
QList<FileNode *> nodes = scanForFilesHelper(
|
||||
promise, directory, dirFilter, [&filter, &factory](const Utils::FilePath &fn) -> FileNode * {
|
||||
const Utils::MimeType mimeType = Utils::mimeTypesForFileName(fn.path()).value(0);
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ private:
|
||||
static void scanForFiles(Promise &fi,
|
||||
const Utils::FilePath &directory,
|
||||
const FileFilter &filter,
|
||||
const QDir::Filters &dirFilter,
|
||||
QDir::Filters dirFilter,
|
||||
const FileTypeFactory &factory);
|
||||
|
||||
private:
|
||||
|
||||
@@ -66,21 +66,16 @@ void PipInstallTask::run()
|
||||
emit finished(false);
|
||||
return;
|
||||
}
|
||||
QString operation = Tr::tr("Install");
|
||||
QString operant;
|
||||
QStringList arguments = {"-m", "pip", "install"};
|
||||
if (!m_requirementsFile.isEmpty()) {
|
||||
operant = Tr::tr("Requirements");
|
||||
arguments << "-r" << m_requirementsFile.toString();
|
||||
} else {
|
||||
|
||||
for (const PipPackage &package : m_packages) {
|
||||
QString pipPackage = package.packageName;
|
||||
if (!package.version.isEmpty())
|
||||
pipPackage += "==" + package.version;
|
||||
arguments << pipPackage;
|
||||
}
|
||||
operant = m_packages.count() == 1 ? m_packages.first().displayName : Tr::tr("Packages");
|
||||
}
|
||||
|
||||
if (!m_targetPath.isEmpty()) {
|
||||
@@ -90,17 +85,27 @@ void PipInstallTask::run()
|
||||
arguments << "--user"; // add --user to global pythons, but skip it for venv pythons
|
||||
}
|
||||
|
||||
if (m_upgrade) {
|
||||
if (m_upgrade)
|
||||
arguments << "--upgrade";
|
||||
operation = Tr::tr("Update");
|
||||
|
||||
QString operation;
|
||||
if (!m_requirementsFile.isEmpty()) {
|
||||
operation = m_upgrade ? Tr::tr("Update Requirements") : Tr::tr("Install Requirements");
|
||||
} else if (m_packages.count() == 1) {
|
||||
//: %1 = package name
|
||||
operation = m_upgrade ? Tr::tr("Update %1")
|
||||
//: %1 = package name
|
||||
: Tr::tr("Install %1");
|
||||
operation = operation.arg(m_packages.first().displayName);
|
||||
} else {
|
||||
operation = m_upgrade ? Tr::tr("Update Packages") : Tr::tr("Install Packages");
|
||||
}
|
||||
|
||||
m_process.setCommand({m_python, arguments});
|
||||
m_process.setTerminalMode(m_silent ? TerminalMode::Off : TerminalMode::Run);
|
||||
m_process.start();
|
||||
|
||||
const QString taskTitle = Tr::tr("%1 %2").arg(operation).arg(operant);
|
||||
Core::ProgressManager::addTask(m_future.future(), taskTitle, pipInstallTaskId);
|
||||
Core::ProgressManager::addTask(m_future.future(), operation, pipInstallTaskId);
|
||||
Core::MessageManager::writeSilently(
|
||||
Tr::tr("Running \"%1\" to install %2.")
|
||||
.arg(m_process.commandLine().toUserOutput(), packagesDisplayName()));
|
||||
|
||||
@@ -728,9 +728,10 @@ void QmlJSEditorDocumentPrivate::setSourcesWithCapabilities(
|
||||
setSemanticWarningSource(QmllsStatus::Source::Qmlls);
|
||||
else
|
||||
setSemanticWarningSource(QmllsStatus::Source::EmbeddedCodeModel);
|
||||
if (cap.semanticTokensProvider())
|
||||
setSemanticHighlightSource(QmllsStatus::Source::Qmlls);
|
||||
else
|
||||
// TODO: uncomment when qmlls semantic tokens reach a stable state
|
||||
// if (cap.semanticTokensProvider())
|
||||
// setSemanticHighlightSource(QmllsStatus::Source::Qmlls);
|
||||
// else
|
||||
setSemanticHighlightSource(QmllsStatus::Source::EmbeddedCodeModel);
|
||||
}
|
||||
|
||||
|
||||