aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEike Ziller <eike.ziller@qt.io>2021-10-08 16:00:32 +0200
committerEike Ziller <eike.ziller@qt.io>2021-10-08 16:00:32 +0200
commit2ae0d4d0b1548d2d1ab61582a946cf3766856ca5 (patch)
tree98f0a7678810a5d06056743b9b965ae69a4f9b68
parent9cb0bd94f0473077269078cc0da34d5e1fb600a0 (diff)
parent7bb21fcfff2b0fe6c6248ff85cdafae452ef7c8c (diff)
Merge remote-tracking branch 'origin/6.0'
-rw-r--r--.github/workflows/build_cmake.yml49
-rw-r--r--dist/changes-6.0.0.md20
-rw-r--r--doc/qtcreator/images/extraimages/images/9MqUCP6JLCQ.jpgbin0 -> 9426 bytes
-rw-r--r--doc/qtcreator/images/extraimages/qtdesignstudio-extraimages.qdocconf3
-rw-r--r--doc/qtcreator/src/qtquick/library/qtquick-preset-components.qdoc1
-rw-r--r--doc/qtcreator/src/qtquick/qtquick-library.qdoc1
-rw-r--r--doc/qtdesignstudio/images/icons/attractor-16px.pngbin0 -> 288 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/emit-burst-16px.pngbin0 -> 381 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/emitter-16px.pngbin0 -> 318 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/gravity-16px.pngbin0 -> 328 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/item-ellipse-16px.pngbin0 -> 272 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/item-polygon-16px.pngbin0 -> 320 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/model-blend-particle-16px.pngbin0 -> 384 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/model-particle-16px.pngbin0 -> 448 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/model-shape-16px.pngbin0 -> 367 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/particle-shape-16px.pngbin0 -> 331 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/particle-system-16px.pngbin0 -> 334 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/point-rotator-16px.pngbin0 -> 421 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/sprite-particle-16px.pngbin0 -> 393 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/sprite-sequence-16px.pngbin0 -> 299 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/target-direction-16px.pngbin0 -> 253 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/trail-emitter-16px.pngbin0 -> 359 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/vector-direction-16px.pngbin0 -> 323 bytes
-rw-r--r--doc/qtdesignstudio/images/icons/wander-16px.pngbin0 -> 334 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-particle-system-navigator.pngbin0 -> 8916 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-particles-fire-assets.pngbin0 -> 15634 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-particles-fire-components.pngbin0 -> 12228 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-particles-fire-emitter1.pngbin0 -> 8730 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-particles-fire-properties-particle-emitter.pngbin0 -> 23329 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-particles-fire-properties-particle-system.pngbin0 -> 10138 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-particles-fire-properties-particle.pngbin0 -> 25105 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-particles-fire-properties-sprite-particle.pngbin0 -> 10379 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-particles-fire-properties-vector-direction.pngbin0 -> 8829 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-particles-fire.gifbin0 -> 324526 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-particles.pngbin0 -> 11906 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-properties-model-blend-particle.pngbin0 -> 9349 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-properties-model-particle.pngbin0 -> 4465 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-properties-particle-affector.pngbin0 -> 6883 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-properties-particle-attractor.pngbin0 -> 13833 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-properties-particle-emit-burst.pngbin0 -> 3899 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-properties-particle-emitter.pngbin0 -> 22954 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-properties-particle-gravity.pngbin0 -> 5398 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-properties-particle-model-shape.pngbin0 -> 4077 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-properties-particle-point-rotator.pngbin0 -> 7729 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-properties-particle-rotation.pngbin0 -> 12568 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-properties-particle-shape.pngbin0 -> 5883 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-properties-particle-sprite-sequence.pngbin0 -> 10154 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-properties-particle-system.pngbin0 -> 9798 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-properties-particle-target-direction.pngbin0 -> 11590 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-properties-particle-vector-direction.pngbin0 -> 8433 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-properties-particle-wander-global.pngbin0 -> 7758 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-properties-particle-wander-unique.pngbin0 -> 8321 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-properties-particle-wander.pngbin0 -> 3328 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-properties-particle.pngbin0 -> 24143 bytes
-rw-r--r--doc/qtdesignstudio/images/studio-3d-properties-sprite-particle.pngbin0 -> 10045 bytes
-rw-r--r--doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc8
-rw-r--r--doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-lights.qdoc2
-rw-r--r--doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-particles.qdoc1088
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp26
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp14
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.h4
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.cpp41
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.h2
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.cpp4
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.cpp3
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.cpp7
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.h2
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/LoaderSpecifics.qml106
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/RepeaterSpecifics.qml77
-rw-r--r--share/qtcreator/templates/wizards/projects/qtquickuiprototype/wizard.json16
-rw-r--r--src/libs/qmljs/qmljscheck.cpp1
-rw-r--r--src/libs/utils/environment.cpp11
-rw-r--r--src/libs/utils/pathlisteditor.cpp1
-rw-r--r--src/libs/utils/pathlisteditor.h3
-rw-r--r--src/plugins/android/androidavdmanager.cpp1
-rw-r--r--src/plugins/android/androidbuildapkstep.cpp5
-rw-r--r--src/plugins/android/androidconfigurations.cpp14
-rw-r--r--src/plugins/android/androidconstants.h19
-rw-r--r--src/plugins/android/androiddeployqtstep.cpp6
-rw-r--r--src/plugins/android/androidmanager.cpp2
-rw-r--r--src/plugins/android/androidpackageinstallationstep.cpp2
-rw-r--r--src/plugins/android/androidpotentialkit.cpp2
-rw-r--r--src/plugins/android/androidqtversion.cpp4
-rw-r--r--src/plugins/android/androidrunconfiguration.cpp6
-rw-r--r--src/plugins/android/androidrunnerworker.cpp2
-rw-r--r--src/plugins/clangcodemodel/clangdclient.cpp1127
-rw-r--r--src/plugins/clangformat/CMakeLists.txt1
-rw-r--r--src/plugins/clangformat/clangformat.pro2
-rw-r--r--src/plugins/clangformat/clangformat.qbs2
-rw-r--r--src/plugins/clangformat/clangformatconfigwidget.cpp97
-rw-r--r--src/plugins/clangformat/clangformatconfigwidget.h6
-rw-r--r--src/plugins/clangformat/clangformatfile.cpp116
-rw-r--r--src/plugins/clangformat/clangformatfile.h60
-rw-r--r--src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp2
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp23
-rw-r--r--src/plugins/cmakeprojectmanager/cmaketool.cpp2
-rw-r--r--src/plugins/cmakeprojectmanager/fileapidataextractor.cpp32
-rw-r--r--src/plugins/coreplugin/dialogs/newdialogwidget.cpp1
-rw-r--r--src/plugins/coreplugin/locator/filesystemfilter.cpp5
-rw-r--r--src/plugins/coreplugin/manhattanstyle.cpp13
-rw-r--r--src/plugins/cppcheck/cppcheckoptions.cpp20
-rw-r--r--src/plugins/cppcheck/cppcheckoptions.h7
-rw-r--r--src/plugins/cppcheck/cppcheckrunner.cpp4
-rw-r--r--src/plugins/cppcheck/cppcheckrunner.h4
-rw-r--r--src/plugins/cppeditor/CppEditor.json.in2
-rw-r--r--src/plugins/cppeditor/semantichighlighter.cpp10
-rw-r--r--src/plugins/debugger/shared/cdbsymbolpathlisteditor.cpp19
-rw-r--r--src/plugins/debugger/shared/cdbsymbolpathlisteditor.h2
-rw-r--r--src/plugins/debugger/shared/symbolpathsdialog.cpp21
-rw-r--r--src/plugins/debugger/shared/symbolpathsdialog.h8
-rw-r--r--src/plugins/docker/dockerdevice.cpp112
-rw-r--r--src/plugins/help/litehtmlhelpviewer.cpp4
-rw-r--r--src/plugins/imageviewer/imageviewerfile.cpp10
-rw-r--r--src/plugins/mcusupport/mcusupportoptions.cpp104
-rw-r--r--src/plugins/mcusupport/mcusupportoptions.h14
-rw-r--r--src/plugins/mcusupport/mcusupportoptionspage.cpp2
-rw-r--r--src/plugins/mcusupport/mcusupportsdk.cpp129
-rw-r--r--src/plugins/projectexplorer/kitmanager.cpp15
-rw-r--r--src/plugins/projectexplorer/miniprojecttargetselector.cpp17
-rw-r--r--src/plugins/qbsprojectmanager/qbsnodes.cpp2
-rw-r--r--src/plugins/qmakeprojectmanager/profilecompletionassist.cpp13
-rw-r--r--src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp2
-rw-r--r--src/plugins/qmakeprojectmanager/qmakenodes.cpp17
-rw-r--r--src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp6
-rw-r--r--src/plugins/qmakeprojectmanager/qmakeparsernodes.h6
-rw-r--r--src/plugins/qmakeprojectmanager/qmakeproject.cpp3
-rw-r--r--src/plugins/qmakeprojectmanager/qmakestep.cpp5
-rw-r--r--src/plugins/qmldesigner/components/componentcore/componentcore_constants.h3
-rw-r--r--src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp10
-rw-r--r--src/plugins/qmldesigner/components/componentcore/designeractionmanager.h2
-rw-r--r--src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp3
-rw-r--r--src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp28
-rw-r--r--src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp14
-rw-r--r--src/plugins/qmldesigner/components/formeditor/formeditoritem.h4
-rw-r--r--src/plugins/qmldesigner/components/formeditor/formeditorview.cpp43
-rw-r--r--src/plugins/qmldesigner/components/formeditor/formeditorview.h1
-rw-r--r--src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp9
-rw-r--r--src/plugins/qmldesigner/components/formeditor/selectiontool.cpp20
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp23
-rw-r--r--src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp43
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/qmlanchorbindingproxy.cpp4
-rw-r--r--src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp2
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.cpp12
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelineutils.h5
-rw-r--r--src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.cpp11
-rw-r--r--src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp5
-rw-r--r--src/plugins/qmldesigner/designercore/include/itemlibraryinfo.h3
-rw-r--r--src/plugins/qmldesigner/designercore/include/nodehints.h1
-rw-r--r--src/plugins/qmldesigner/designercore/include/qmlitemnode.h1
-rw-r--r--src/plugins/qmldesigner/designercore/include/qmlobjectnode.h5
-rw-r--r--src/plugins/qmldesigner/designercore/include/qmlstate.h1
-rw-r--r--src/plugins/qmldesigner/designercore/include/qmltimelinekeyframegroup.h3
-rw-r--r--src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp2
-rw-r--r--src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp5
-rw-r--r--src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp5
-rw-r--r--src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp68
-rw-r--r--src/plugins/qmldesigner/designercore/model/qmlstate.cpp7
-rw-r--r--src/plugins/qmldesigner/designercore/model/qmltimelinekeyframegroup.cpp23
-rw-r--r--src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp6
-rw-r--r--src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp46
-rw-r--r--src/plugins/qmldesigner/designercore/model/texttomodelmerger.h7
-rw-r--r--src/plugins/qmldesigner/qtquickplugin/quick.metainfo47
-rw-r--r--src/plugins/qmljseditor/qmljseditorplugin.cpp3
-rw-r--r--src/plugins/texteditor/basefilefind.cpp2
-rw-r--r--src/plugins/valgrind/memcheckerrorview.cpp13
-rw-r--r--src/plugins/valgrind/memcheckerrorview.h8
-rw-r--r--src/plugins/valgrind/memchecktool.cpp26
-rw-r--r--src/plugins/valgrind/suppressiondialog.cpp35
-rw-r--r--src/plugins/valgrind/valgrindsettings.cpp14
-rw-r--r--src/plugins/valgrind/valgrindsettings.h6
-rw-r--r--src/shared/proparser/qmakebuiltins.cpp27
-rw-r--r--src/shared/proparser/qmakeglobals.cpp4
-rw-r--r--src/shared/proparser/qmakeglobals.h3
m---------src/shared/qbs0
-rw-r--r--src/shared/shared.pro5
-rw-r--r--src/tools/qml2puppet/qml2puppet.qbs1
-rw-r--r--tests/manual/debugger/simple/simple_test_app.cpp11
-rw-r--r--tests/system/suite_general/tst_rename_file/test.py31
-rw-r--r--tests/unit/unittest/CMakeLists.txt12
179 files changed, 3174 insertions, 1099 deletions
diff --git a/.github/workflows/build_cmake.yml b/.github/workflows/build_cmake.yml
index e3a7750119..15cab0d19f 100644
--- a/.github/workflows/build_cmake.yml
+++ b/.github/workflows/build_cmake.yml
@@ -3,7 +3,7 @@ name: CMake Build Matrix
on: [push, pull_request]
env:
- QT_VERSION: 5.15.2
+ QT_VERSION: 6.2.0
CLANG_VERSION: 130
ELFUTILS_VERSION: 0.175
CMAKE_VERSION: 3.21.1
@@ -111,24 +111,20 @@ jobs:
set(qt_package_arch_suffix "win64_msvc2019_64")
set(qt_dir_prefix "${qt_version}/msvc2019_64")
set(qt_package_suffix "-Windows-Windows_10-MSVC2019-Windows-Windows_10-X86_64")
- elseif ("${{ matrix.config.environment_script }}" MATCHES "vcvars32.bat")
- set(qt_package_arch_suffix "win32_msvc2019")
- set(qt_dir_prefix "${qt_version}/msvc2019")
- set(qt_package_suffix "-Windows-Windows_10-MSVC2019-Windows-Windows_10-X86")
endif()
elseif ("${{ runner.os }}" STREQUAL "Linux")
set(url_os "linux_x64")
set(qt_package_arch_suffix "gcc_64")
set(qt_dir_prefix "${qt_version}/gcc_64")
- set(qt_package_suffix "-Linux-RHEL_7_6-GCC-Linux-RHEL_7_6-X86_64")
+ set(qt_package_suffix "-Linux-RHEL_8_2-GCC-Linux-RHEL_8_2-X86_64")
elseif ("${{ runner.os }}" STREQUAL "macOS")
set(url_os "mac_x64")
set(qt_package_arch_suffix "clang_64")
- set(qt_dir_prefix "${qt_version}/clang_64")
- set(qt_package_suffix "-MacOS-MacOS_10_13-Clang-MacOS-MacOS_10_13-X86_64")
+ set(qt_dir_prefix "${qt_version}/macos")
+ set(qt_package_suffix "-MacOS-MacOS_11_00-Clang-MacOS-MacOS_11_00-X86_64-ARM64")
endif()
- set(qt_base_url "https://\${qt_mirror}/online/qtsdkrepository/${url_os}/desktop/qt5_${qt_version_dotless}")
+ set(qt_base_url "https://\${qt_mirror}/online/qtsdkrepository/${url_os}/desktop/qt6_${qt_version_dotless}")
foreach(qt_mirror $ENV{QT_MIRRORS})
cmake_language(EVAL CODE "
message(\"Downloading: ${qt_base_url}/Updates.xml\")
@@ -141,13 +137,13 @@ jobs:
endforeach()
file(READ ./Updates.xml updates_xml)
- string(REGEX MATCH "<Name>qt.qt5.*<Version>([0-9+-.]+)</Version>" updates_xml_output "${updates_xml}")
+ string(REGEX MATCH "<Name>qt.qt6.*<Version>([0-9+-.]+)</Version>" updates_xml_output "${updates_xml}")
set(qt_package_version ${CMAKE_MATCH_1})
- file(MAKE_DIRECTORY qt5)
+ file(MAKE_DIRECTORY qt6)
# Save the path for other steps
- file(TO_CMAKE_PATH "$ENV{GITHUB_WORKSPACE}/qt5/${qt_dir_prefix}" qt_dir)
+ file(TO_CMAKE_PATH "$ENV{GITHUB_WORKSPACE}/qt6/${qt_dir_prefix}" qt_dir)
message("::set-output name=qt_dir::${qt_dir}")
function(downloadAndExtract url archive)
@@ -161,19 +157,26 @@ jobs:
break()
endif()
endforeach()
- execute_process(COMMAND ${CMAKE_COMMAND} -E tar xvf ../${archive} WORKING_DIRECTORY qt5)
+ execute_process(COMMAND ${CMAKE_COMMAND} -E tar xvf ../${archive} WORKING_DIRECTORY qt6)
endfunction()
- foreach(package qtbase qtdeclarative qttools qtsvg qtserialport qtquickcontrols qtquickcontrols2 qtgraphicaleffects qtlocation qtimageformats qttranslations)
+ foreach(package qtbase qtdeclarative qttools qtsvg qttranslations)
+ downloadAndExtract(
+ "${qt_base_url}/qt.qt6.${qt_version_dotless}.${qt_package_arch_suffix}/${qt_package_version}${package}${qt_package_suffix}.7z"
+ ${package}.7z
+ )
+ endforeach()
+
+ foreach(package qtimageformats qtserialport)
downloadAndExtract(
- "${qt_base_url}/qt.qt5.${qt_version_dotless}.${qt_package_arch_suffix}/${qt_package_version}${package}${qt_package_suffix}.7z"
+ "${qt_base_url}/qt.qt6.${qt_version_dotless}.addons.${package}.${qt_package_arch_suffix}/${qt_package_version}${package}${qt_package_suffix}.7z"
${package}.7z
)
endforeach()
- foreach(package qtquicktimeline qtquick3d qtscript)
+ foreach(package qtquicktimeline qtquick3d qt5compat qtshadertools)
downloadAndExtract(
- "${qt_base_url}/qt.qt5.${qt_version_dotless}.${package}.${qt_package_arch_suffix}/${qt_package_version}${package}${qt_package_suffix}.7z"
+ "${qt_base_url}/qt.qt6.${qt_version_dotless}.${package}.${qt_package_arch_suffix}/${qt_package_version}${package}${qt_package_suffix}.7z"
${package}.7z
)
endforeach()
@@ -181,17 +184,17 @@ jobs:
# uic depends on libicu56.so
if ("${{ runner.os }}" STREQUAL "Linux")
downloadAndExtract(
- "${qt_base_url}/qt.qt5.${qt_version_dotless}.${qt_package_arch_suffix}/${qt_package_version}icu-linux-Rhel7.2-x64.7z"
+ "${qt_base_url}/qt.qt6.${qt_version_dotless}.${qt_package_arch_suffix}/${qt_package_version}icu-linux-Rhel7.2-x64.7z"
icu.7z
)
endif()
- file(READ "qt5/${qt_dir_prefix}/mkspecs/qconfig.pri" qtconfig)
+ file(READ "qt6/${qt_dir_prefix}/mkspecs/qconfig.pri" qtconfig)
string(REPLACE "Enterprise" "OpenSource" qtconfig "${qtconfig}")
string(REPLACE "licheck.exe" "" qtconfig "${qtconfig}")
string(REPLACE "licheck64" "" qtconfig "${qtconfig}")
string(REPLACE "licheck_mac" "" qtconfig "${qtconfig}")
- file(WRITE "qt5/${qt_dir_prefix}/mkspecs/qconfig.pri" "${qtconfig}")
+ file(WRITE "qt6/${qt_dir_prefix}/mkspecs/qconfig.pri" "${qtconfig}")
if ("${{ runner.os }}" STREQUAL "Windows")
# deploy "system" runtimes into Qt, so they get deployed as well
@@ -199,7 +202,7 @@ jobs:
# deploy MinGW
foreach(file libwinpthread-1.dll libstdc++-6.dll libgcc_s_seh-1.dll)
file(INSTALL "C:/ProgramData/chocolatey/lib/mingw/tools/install/mingw64/bin/${file}"
- DESTINATION "qt5/${qt_dir_prefix}/bin"
+ DESTINATION "qt6/${qt_dir_prefix}/bin"
USE_SOURCE_PERMISSIONS)
endforeach()
else()
@@ -207,7 +210,7 @@ jobs:
foreach(file vcruntime140.dll concrt140.dll msvcp140_1.dll msvcp140_2.dll
msvcp140_codecvt_ids.dll vcruntime140_1.dll msvcp140.dll)
file(INSTALL "C:/Windows/System32/${file}"
- DESTINATION "qt5/${qt_dir_prefix}/bin")
+ DESTINATION "qt6/${qt_dir_prefix}/bin")
endforeach()
endif()
endif()
@@ -377,7 +380,7 @@ jobs:
COMMAND sudo apt update
)
execute_process(
- COMMAND sudo apt install libgl1-mesa-dev
+ COMMAND sudo apt install libgl1-mesa-dev libvulkan-dev libxcb-xinput-dev libxcb-xinerama0-dev libxkbcommon-dev libxkbcommon-x11-dev
RESULT_VARIABLE result
)
if (NOT result EQUAL 0)
diff --git a/dist/changes-6.0.0.md b/dist/changes-6.0.0.md
index c16f46c3cf..90db19cc57 100644
--- a/dist/changes-6.0.0.md
+++ b/dist/changes-6.0.0.md
@@ -24,6 +24,7 @@ Editing
### C++
+* Updated to LLVM 13
* Added completion and function hint to `clangd` support
* Added option for saving open files automatically after refactoring
(QTCREATORBUG-25924)
@@ -36,6 +37,11 @@ Editing
* Improved wizards for Qt 6.2 (QTCREATORBUG-26170)
* Simplified wizards
+### Language Server Protocol
+
+* Added support for `activeParameter` of signature information
+ (QTCREATORBUG-26346)
+
Projects
--------
@@ -56,7 +62,13 @@ Projects
`Issues` pane (QTCREATORBUG-26231)
* Fixed header file handling when mentioned in target sources
(QTCREATORBUG-23783, QTCREATORBUG-23843, QTCREATORBUG-26201,
- QTCREATORBUG-26238)
+ QTCREATORBUG-26238, QTCREATORBUG-21452, QTCREATORBUG-25644,
+ QTCREATORBUG-25782)
+* Fixed that generated files were selected for analyzing (QTCREATORBUG-25125)
+
+### qmake
+
+* Fixed crash when canceling parsing (QTCREATORBUG-26333)
Version Control Systems
-----------------------
@@ -87,6 +99,7 @@ Platforms
* Removed device selection dialog in favor of device selection in target
selector (QTCREATORBUG-23991)
* Added details to device settings (QTCREATORBUG-23991)
+* Added filter field for Android SDK manager
### Docker
@@ -103,6 +116,7 @@ André Pönitz
Artem Sokolovskii
Artur Shepilko
Assam Boudjelthia
+Christiaan Janssen
Christian Kandeler
Christian Stenger
Cristian Adam
@@ -112,14 +126,17 @@ Fawzi Mohamed
Henning Gruendl
Ihor Dutchak
Jaroslaw Kobus
+Johanna Vanhatapio
Jonas Karlsson
Kai Köhne
Kama Wójcik
+Knud Dollereder
Li Xi
Loren Burkholder
Mahmoud Badri
Marco Bubke
Martin Kampas
+Miikka Heikkinen
Miina Puuronen
Orgad Shaneh
Petar Perisin
@@ -129,6 +146,7 @@ Shantanu Tushar
Tasuku Suzuki
Thiago Macieira
Thomas Hartmann
+Tim Jenssen
Tony Leinonen
Tor Arne Vestbø
Vladimir Serdyuk
diff --git a/doc/qtcreator/images/extraimages/images/9MqUCP6JLCQ.jpg b/doc/qtcreator/images/extraimages/images/9MqUCP6JLCQ.jpg
new file mode 100644
index 0000000000..db8ba3d920
--- /dev/null
+++ b/doc/qtcreator/images/extraimages/images/9MqUCP6JLCQ.jpg
Binary files differ
diff --git a/doc/qtcreator/images/extraimages/qtdesignstudio-extraimages.qdocconf b/doc/qtcreator/images/extraimages/qtdesignstudio-extraimages.qdocconf
index a0c485b95f..7a4f2d4dce 100644
--- a/doc/qtcreator/images/extraimages/qtdesignstudio-extraimages.qdocconf
+++ b/doc/qtcreator/images/extraimages/qtdesignstudio-extraimages.qdocconf
@@ -12,4 +12,5 @@
images/pEETxSxYazg.jpg \
images/V3Po15bNErw.jpg \
images/bMXeeQw6BYs.jpg \
- images/u3kZJjlk3CY.jpg
+ images/u3kZJjlk3CY.jpg \
+ images/9MqUCP6JLCQ.jpg
diff --git a/doc/qtcreator/src/qtquick/library/qtquick-preset-components.qdoc b/doc/qtcreator/src/qtquick/library/qtquick-preset-components.qdoc
index cc896f78c9..488dbbdfe0 100644
--- a/doc/qtcreator/src/qtquick/library/qtquick-preset-components.qdoc
+++ b/doc/qtcreator/src/qtquick/library/qtquick-preset-components.qdoc
@@ -86,6 +86,7 @@
\li \l {Morph Target}
\li \l {Repeater3D}
\li \l {Loader3D}
+ \li \l {Particles}
\endlist
When you import 3D scenes from files that you exported from 3D graphics
diff --git a/doc/qtcreator/src/qtquick/qtquick-library.qdoc b/doc/qtcreator/src/qtquick/qtquick-library.qdoc
index 3c73fcfa4a..b1d03a72e6 100644
--- a/doc/qtcreator/src/qtquick/qtquick-library.qdoc
+++ b/doc/qtcreator/src/qtquick/qtquick-library.qdoc
@@ -85,6 +85,7 @@
\li \l{Scene Environment}
\li \l{Morph Target}
\li \l{Repeater3D}
+ \li \l{Particles}
\endlist
For more information about creating your own components, see
diff --git a/doc/qtdesignstudio/images/icons/attractor-16px.png b/doc/qtdesignstudio/images/icons/attractor-16px.png
new file mode 100644
index 0000000000..f2d49e7fd2
--- /dev/null
+++ b/doc/qtdesignstudio/images/icons/attractor-16px.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/emit-burst-16px.png b/doc/qtdesignstudio/images/icons/emit-burst-16px.png
new file mode 100644
index 0000000000..d425974154
--- /dev/null
+++ b/doc/qtdesignstudio/images/icons/emit-burst-16px.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/emitter-16px.png b/doc/qtdesignstudio/images/icons/emitter-16px.png
new file mode 100644
index 0000000000..1fce677afe
--- /dev/null
+++ b/doc/qtdesignstudio/images/icons/emitter-16px.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/gravity-16px.png b/doc/qtdesignstudio/images/icons/gravity-16px.png
new file mode 100644
index 0000000000..4d182620cf
--- /dev/null
+++ b/doc/qtdesignstudio/images/icons/gravity-16px.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/item-ellipse-16px.png b/doc/qtdesignstudio/images/icons/item-ellipse-16px.png
new file mode 100644
index 0000000000..ab0e225db1
--- /dev/null
+++ b/doc/qtdesignstudio/images/icons/item-ellipse-16px.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/item-polygon-16px.png b/doc/qtdesignstudio/images/icons/item-polygon-16px.png
new file mode 100644
index 0000000000..b56c7bb714
--- /dev/null
+++ b/doc/qtdesignstudio/images/icons/item-polygon-16px.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/model-blend-particle-16px.png b/doc/qtdesignstudio/images/icons/model-blend-particle-16px.png
new file mode 100644
index 0000000000..0f77c30cf2
--- /dev/null
+++ b/doc/qtdesignstudio/images/icons/model-blend-particle-16px.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/model-particle-16px.png b/doc/qtdesignstudio/images/icons/model-particle-16px.png
new file mode 100644
index 0000000000..39cf9eeb23
--- /dev/null
+++ b/doc/qtdesignstudio/images/icons/model-particle-16px.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/model-shape-16px.png b/doc/qtdesignstudio/images/icons/model-shape-16px.png
new file mode 100644
index 0000000000..4113a5070b
--- /dev/null
+++ b/doc/qtdesignstudio/images/icons/model-shape-16px.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/particle-shape-16px.png b/doc/qtdesignstudio/images/icons/particle-shape-16px.png
new file mode 100644
index 0000000000..e8242fb449
--- /dev/null
+++ b/doc/qtdesignstudio/images/icons/particle-shape-16px.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/particle-system-16px.png b/doc/qtdesignstudio/images/icons/particle-system-16px.png
new file mode 100644
index 0000000000..66a0396750
--- /dev/null
+++ b/doc/qtdesignstudio/images/icons/particle-system-16px.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/point-rotator-16px.png b/doc/qtdesignstudio/images/icons/point-rotator-16px.png
new file mode 100644
index 0000000000..f44a7f83f5
--- /dev/null
+++ b/doc/qtdesignstudio/images/icons/point-rotator-16px.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/sprite-particle-16px.png b/doc/qtdesignstudio/images/icons/sprite-particle-16px.png
new file mode 100644
index 0000000000..14c6142b86
--- /dev/null
+++ b/doc/qtdesignstudio/images/icons/sprite-particle-16px.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/sprite-sequence-16px.png b/doc/qtdesignstudio/images/icons/sprite-sequence-16px.png
new file mode 100644
index 0000000000..0174962ca4
--- /dev/null
+++ b/doc/qtdesignstudio/images/icons/sprite-sequence-16px.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/target-direction-16px.png b/doc/qtdesignstudio/images/icons/target-direction-16px.png
new file mode 100644
index 0000000000..4295336833
--- /dev/null
+++ b/doc/qtdesignstudio/images/icons/target-direction-16px.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/trail-emitter-16px.png b/doc/qtdesignstudio/images/icons/trail-emitter-16px.png
new file mode 100644
index 0000000000..284bf9af02
--- /dev/null
+++ b/doc/qtdesignstudio/images/icons/trail-emitter-16px.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/vector-direction-16px.png b/doc/qtdesignstudio/images/icons/vector-direction-16px.png
new file mode 100644
index 0000000000..ef8e871df0
--- /dev/null
+++ b/doc/qtdesignstudio/images/icons/vector-direction-16px.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/icons/wander-16px.png b/doc/qtdesignstudio/images/icons/wander-16px.png
new file mode 100644
index 0000000000..b2d43ed5c0
--- /dev/null
+++ b/doc/qtdesignstudio/images/icons/wander-16px.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-particle-system-navigator.png b/doc/qtdesignstudio/images/studio-3d-particle-system-navigator.png
new file mode 100644
index 0000000000..01e4519caa
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-particle-system-navigator.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-particles-fire-assets.png b/doc/qtdesignstudio/images/studio-3d-particles-fire-assets.png
new file mode 100644
index 0000000000..ad3a94c162
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-particles-fire-assets.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-particles-fire-components.png b/doc/qtdesignstudio/images/studio-3d-particles-fire-components.png
new file mode 100644
index 0000000000..e0f112b33b
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-particles-fire-components.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-particles-fire-emitter1.png b/doc/qtdesignstudio/images/studio-3d-particles-fire-emitter1.png
new file mode 100644
index 0000000000..ee56c599c7
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-particles-fire-emitter1.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-particles-fire-properties-particle-emitter.png b/doc/qtdesignstudio/images/studio-3d-particles-fire-properties-particle-emitter.png
new file mode 100644
index 0000000000..9b8106a8b7
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-particles-fire-properties-particle-emitter.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-particles-fire-properties-particle-system.png b/doc/qtdesignstudio/images/studio-3d-particles-fire-properties-particle-system.png
new file mode 100644
index 0000000000..9a36cba2d8
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-particles-fire-properties-particle-system.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-particles-fire-properties-particle.png b/doc/qtdesignstudio/images/studio-3d-particles-fire-properties-particle.png
new file mode 100644
index 0000000000..6b9282c650
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-particles-fire-properties-particle.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-particles-fire-properties-sprite-particle.png b/doc/qtdesignstudio/images/studio-3d-particles-fire-properties-sprite-particle.png
new file mode 100644
index 0000000000..1781d98524
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-particles-fire-properties-sprite-particle.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-particles-fire-properties-vector-direction.png b/doc/qtdesignstudio/images/studio-3d-particles-fire-properties-vector-direction.png
new file mode 100644
index 0000000000..fe4d990a0d
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-particles-fire-properties-vector-direction.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-particles-fire.gif b/doc/qtdesignstudio/images/studio-3d-particles-fire.gif
new file mode 100644
index 0000000000..292379041b
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-particles-fire.gif
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-particles.png b/doc/qtdesignstudio/images/studio-3d-particles.png
new file mode 100644
index 0000000000..48f963ede6
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-particles.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-properties-model-blend-particle.png b/doc/qtdesignstudio/images/studio-3d-properties-model-blend-particle.png
new file mode 100644
index 0000000000..a0de57542d
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-properties-model-blend-particle.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-properties-model-particle.png b/doc/qtdesignstudio/images/studio-3d-properties-model-particle.png
new file mode 100644
index 0000000000..bfbaf3a360
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-properties-model-particle.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-properties-particle-affector.png b/doc/qtdesignstudio/images/studio-3d-properties-particle-affector.png
new file mode 100644
index 0000000000..16000bdb24
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-properties-particle-affector.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-properties-particle-attractor.png b/doc/qtdesignstudio/images/studio-3d-properties-particle-attractor.png
new file mode 100644
index 0000000000..fb46ef050e
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-properties-particle-attractor.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-properties-particle-emit-burst.png b/doc/qtdesignstudio/images/studio-3d-properties-particle-emit-burst.png
new file mode 100644
index 0000000000..3638646773
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-properties-particle-emit-burst.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-properties-particle-emitter.png b/doc/qtdesignstudio/images/studio-3d-properties-particle-emitter.png
new file mode 100644
index 0000000000..291d413290
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-properties-particle-emitter.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-properties-particle-gravity.png b/doc/qtdesignstudio/images/studio-3d-properties-particle-gravity.png
new file mode 100644
index 0000000000..b945aa85c1
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-properties-particle-gravity.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-properties-particle-model-shape.png b/doc/qtdesignstudio/images/studio-3d-properties-particle-model-shape.png
new file mode 100644
index 0000000000..910b73ae37
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-properties-particle-model-shape.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-properties-particle-point-rotator.png b/doc/qtdesignstudio/images/studio-3d-properties-particle-point-rotator.png
new file mode 100644
index 0000000000..4069a35bda
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-properties-particle-point-rotator.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-properties-particle-rotation.png b/doc/qtdesignstudio/images/studio-3d-properties-particle-rotation.png
new file mode 100644
index 0000000000..207d0a6763
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-properties-particle-rotation.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-properties-particle-shape.png b/doc/qtdesignstudio/images/studio-3d-properties-particle-shape.png
new file mode 100644
index 0000000000..b84095fa69
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-properties-particle-shape.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-properties-particle-sprite-sequence.png b/doc/qtdesignstudio/images/studio-3d-properties-particle-sprite-sequence.png
new file mode 100644
index 0000000000..8a41409b12
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-properties-particle-sprite-sequence.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-properties-particle-system.png b/doc/qtdesignstudio/images/studio-3d-properties-particle-system.png
new file mode 100644
index 0000000000..1d2205c7cc
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-properties-particle-system.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-properties-particle-target-direction.png b/doc/qtdesignstudio/images/studio-3d-properties-particle-target-direction.png
new file mode 100644
index 0000000000..a5d7b63010
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-properties-particle-target-direction.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-properties-particle-vector-direction.png b/doc/qtdesignstudio/images/studio-3d-properties-particle-vector-direction.png
new file mode 100644
index 0000000000..898fd75f15
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-properties-particle-vector-direction.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-properties-particle-wander-global.png b/doc/qtdesignstudio/images/studio-3d-properties-particle-wander-global.png
new file mode 100644
index 0000000000..bbf45be008
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-properties-particle-wander-global.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-properties-particle-wander-unique.png b/doc/qtdesignstudio/images/studio-3d-properties-particle-wander-unique.png
new file mode 100644
index 0000000000..dabef3c86f
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-properties-particle-wander-unique.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-properties-particle-wander.png b/doc/qtdesignstudio/images/studio-3d-properties-particle-wander.png
new file mode 100644
index 0000000000..0ce910c09f
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-properties-particle-wander.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-properties-particle.png b/doc/qtdesignstudio/images/studio-3d-properties-particle.png
new file mode 100644
index 0000000000..0615eb7733
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-properties-particle.png
Binary files differ
diff --git a/doc/qtdesignstudio/images/studio-3d-properties-sprite-particle.png b/doc/qtdesignstudio/images/studio-3d-properties-sprite-particle.png
new file mode 100644
index 0000000000..6a3a7705f5
--- /dev/null
+++ b/doc/qtdesignstudio/images/studio-3d-properties-sprite-particle.png
Binary files differ
diff --git a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc
index f917223dfc..dc8dea8f13 100644
--- a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc
+++ b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc
@@ -105,6 +105,14 @@
\li \l{Morph Target}
\li \l{Repeater3D}
\li \l{Loader3D}
+ \li \l{Particles}
+ \list
+ \li \l {Particle System}
+ \li \l {Logical Particles}
+ \li \l {Particle Emitters}
+ \li \l {Particle Affectors}
+ \li \l {Particle Directions}
+ \endlist
\endlist
\li \l {Creating Component Instances}
\li \l {Creating Custom Components}
diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-lights.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-lights.qdoc
index 239a168970..8fd1539d51 100644
--- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-lights.qdoc
+++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-lights.qdoc
@@ -60,7 +60,7 @@
\li More Information
\row
- \li \inlineimage spot.png
+ \li \inlineimage directional.png
\li Directional Light
\li
\li \l{DirectionalLight}{Light Directional}
diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-particles.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-particles.qdoc
new file mode 100644
index 0000000000..06e08b267a
--- /dev/null
+++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-particles.qdoc
@@ -0,0 +1,1088 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Design Studio.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \page studio-3d-particles.html
+ \previouspage studio-3d-loader-3d.html
+ \nextpage studio-3d-particle-system.html
+
+ \title Particles
+
+ A \e {particle system} enables you to use sprites, 3D models, or images to
+ create effects that are hard to reproduce with conventional rendering
+ techniques. This includes chaotic systems, natural phenomena, or processes
+ caused by chemical reactions. For example, you can simulate fire, smoke,
+ sparks, explosions, flowing water, fog, snow, stars, and galaxies.
+
+ The \QDS particle system contains the following main types of components:
+
+ \list
+ \li \l {Particle System}
+ \li \l {Logical Particles}
+ \li \l {Particle Emitters}
+ \li \l {Particle Affectors}
+ \li \l {Particle Directions}
+ \endlist
+
+ \section1 Adding a Particle System
+
+ Preset particle components are available in \l Library >
+ \uicontrol Components > \uicontrol {Qt Quick 3D Particles 3D}
+ after you add the \uicontrol {QtQuick3D.Particles3D} module to
+ your project, as instructed in \l{Adding and Removing Modules}.
+
+ \image studio-3d-particles.png "3D Particles"
+
+ When you add an instance of the \uicontrol {Particle System} component to a
+ scene, \QDS automatically adds instances of the \uicontrol {Sprite Particle},
+ \uicontrol Emitter, and \uicontrol {Vector Direction} components for you.
+
+ \image studio-3d-particle-system-navigator.png "Particle System in Navigator"
+
+ The \uicontrol {Sprite Particle} is a visual \l{Textures}{2D texture}
+ particle. To use \l{3D Models}{3D model} particles, add instances of the
+ \uicontrol {Model Particle} component to the particle system. It is
+ important to define the amount of particles to use in \uicontrol Properties
+ > \uicontrol Particle > \uicontrol {Max amount}, so that the optimal buffer
+ sizes get allocated. You can also specify the color and opacity of the
+ particles, as well as the speed at which they fade in and out.
+
+ The \uicontrol Emitter component emits the sprite or model particles.
+ You can specify particle shape and the area where they are emitted. Use
+ variation in particle size and rotation for more natural results. Further,
+ define the life span and initial direction of speed of the emitted
+ particles.
+
+ Usually, affectors are used to make particle movement more interesting.
+ For example, you could add an instance of the \uicontrol Gravity component
+ to simulate falling objects, an instance of the \uicontrol Wander component
+ to simulate flying objects that follow wavy curves, or an instance of the
+ \uicontrol {Point Rotator} to simulate windy weather.
+
+ To add a particle system that emits sprite particles:
+
+ \list 1
+ \li Select \uicontrol Library > \uicontrol Assets > \inlineimage plus.png
+ to add your sprites, 3D models, textures, and other graphical
+ \l{Assets}{assets} to the project.
+ \li Drag-and-drop an instance of the \uicontrol {Particle System}
+ component from \uicontrol Library to a scene component instance
+ in \l Navigator.
+ \li Drag-and-drop an instance of the \uicontrol Texture component
+ from \uicontrol Library > \uicontrol Components >
+ \uicontrol {Qt Quick 3D} to the sprite particle instance
+ in \uicontrol Navigator.
+ \li Drag-and-drop the sprite image from \uicontrol Library >
+ \uicontrol Assets to the texture instance in \uicontrol Navigator.
+ \li Select the sprite particle instance in \uicontrol Navigator to
+ display its properties in \l Properties.
+ \li In \uicontrol Sprite, select the texture instance.
+ \li Select the emitter instance in \uicontrol Navigator to
+ display its properties in \uicontrol Properties.
+ \li In \uicontrol Particle, select the particle instance to emit.
+ \li Select the vector 3D instance in \uicontrol Navigator to
+ display its properties in \uicontrol Properties.
+ \li In \uicontrol Direction, set the emitted particle velocity
+ towards the target vector.
+ \endlist
+
+ Add instances of other components according to your use case. The following
+ section describes how to modify particle system component property values by
+ using the simulation of fire as an example.
+
+ \section1 Example: Fire
+
+ \image studio-3d-particles-fire.gif "Fire simulated by using particles"
+
+ In this section, we explore using particle system components and modifying
+ their property values, such as particle source images and their color, life
+ span, and fading effects, to simulate fire.
+
+ We will need the following \l{Assets}{assets}:
+
+ \list
+ \li Two images of flames: \e flame_01.png and \e flame_03.png
+ \li An image of smoke: \e smoke_01.png
+ \endlist
+
+ \image studio-3d-particles-fire-assets.png "The assets in Library"
+
+ We will also need instances of the following components:
+
+ \list
+ \li \l {Particle System} (one instance)
+ \li \l {Sprite Particle} (four instances)
+ \li \l {Emitter} (four instances)
+ \li \l {Vector Direction} (four instances)
+ \li \l {Textures}{Texture} (four instances)
+ \endlist
+
+ We start by creating a \l{Using Project Wizards}{Qt Quick 3D Application}
+ project and adding the assets to it. We then follow the instructions above
+ to add the \uicontrol {Particle System} component instance, which provides
+ us with our first \uicontrol Emitter, \uicontrol {Sprite Particle}, and
+ \uicontrol {Vector Direction} instances.
+
+ We add a \uicontrol Texture component instance with \e flame_01.png as the
+ source, and then add the texture as the sprite to use for the sprite
+ particle. We now have all the component instances we need for our first
+ emitter. To keep all the component instances together, we drag-and-drop the
+ sprite particle instance to the emitter instance in \uicontrol Navigator.
+
+ \image studio-3d-particles-fire-emitter1.png "First emitter in fire example in Navigator"
+
+ We need more than one particle to realistically simulate a fire, so we will
+ add three more emitter instances to the particle system. We can copy-paste
+ the first instance in \uicontrol Navigator, and just modify two of the
+ texture instances to use \e flame_03.png as the source and one of them to
+ use \e smoke_01.png as the source. We use three different flame particles
+ to be able to layer them and to hide the fact that two of them actually use
+ the same texture.
+
+ \image studio-3d-particles-fire-components.png "Fire particle system component instances in Navigator"
+
+ We can now start playing with the particle system component properties to
+ achieve the artistic effect that we want. To see how the changes in property
+ values affect the simulation, we will open the live preview by selecting
+ \inlineimage live_preview.png
+ on the main toolbar (or by pressing \key {Alt+P}).
+
+ First, we will specify property values for the \uicontrol {Particle System}
+ component. We want to spawn some particles before the simulation starts, so
+ that the fire will be roaring at start. To achieve this, we set the value of
+ \uicontrol Properties > \uicontrol {Particle System} >
+ \uicontrol {Start Time} to \e 2000 milliseconds. We will
+ use the default values for the other properties.
+
+ \image studio-3d-particles-fire-properties-particle-system.png "Particle System properties"
+
+ Then, we will specify how individual particles are spawned. We set the
+ value of \uicontrol Properties > \uicontrol {Particle Emitter} >
+ \uicontrol {Emit rate} to \e 40.00 for the flame particles and \e 20.00
+ for the smoke particle, because we want to have more flames than smoke.
+
+ \image studio-3d-particles-fire-properties-particle-emitter.png "Particle Emitter properties"
+
+ To increase the visibility of the smoke at the top, we set the value of
+ \uicontrol {Life span} to \e 1200 milliseconds for the smoke particles
+ and \e 900, \e 1000, and \e 600 milliseconds for the flame particles.
+ Further, we set \uicontrol {Life span variation} to \e 100, \e 200, and
+ \e 300 for the flame particles to have some of them expire sooner than
+ others.
+
+ To scale the particles during their lifespan, we set
+ \uicontrol {Particle scale} to \e 2.00 for all particles and
+ \uicontrol {Particle end scale} to \e 5.00 for the flame
+ particles and to \e 6.00 for the smoke particles.
+
+ We can now modify the appearance of the particles by setting their color in
+ \uicontrol Properties > \uicontrol Particle. We select transparent yellow,
+ orange, and transparent gray in \uicontrol Color and set values for
+ \uicontrol {Color variation} to use slightly different colors for the
+ individual particles.
+
+ \image studio-3d-particles-fire-properties-particle.png "Particle properties"
+
+ We set \uicontrol {Fade in effect} and \uicontrol {Fade out effect} values
+ to \uicontrol FadeOpacity to change particle opacity between 0 and 1 over
+ the time specified in milliseconds in \uicontrol {Fade in duration} and
+ \uicontrol {Fade out duration}. Fading duration is calculated into the
+ particle lifespan, and therefore the smoke particles are actually never
+ fully visible with our settings.
+
+ To specify that the particle texture should always be aligned face towards
+ the screen, we enable \uicontrol Properties > \uicontrol {Sprite Particle} >
+ \uicontrol Billboard for all the particle component instances.
+
+ \image studio-3d-particles-fire-properties-sprite-particle.png "Sprite Particle properties"
+
+ Finally, we will specify the direction in which the particles move by
+ modifying the property values of the \uicontrol {Vector Direction} component
+ instances in \uicontrol Properties > \uicontrol {Particle Vector Direction}.
+
+ In \uicontrol Direction, we set \uicontrol Y to \e 100.00 to make particles
+ move \e up, and \uicontrol Z to \e -100.00 to make them move in the
+ direction opposite to the target vector. We set the \uicontrol Direction
+ value to \e -50.00 for one of the flame particles and to \e -80.00 for the
+ smoke particle.
+
+ \image studio-3d-particles-fire-properties-vector-direction.png "Vector Direction properties"
+
+ We set the \uicontrol {Direction Variation} values for the different
+ vector direction instances to \e 10.00, \e 8.00, and \e 12.00 to make some
+ flames spread wider than others. You can try different values until you
+ get the effect you want.
+
+ \section1 Performance Considerations
+
+ The particles are designed to be usable on a variety of hardware on
+ desktops, as well as mobile and embedded devices. However, in addition
+ to rendering the maximum amount of particle elements on the screen,
+ extensibility to different use-cases, rendering quality, integration
+ with the other UI elements, are also important.
+
+ Currently, the rendering runs on GPU, while the particle system logic
+ runs on CPU. However, the \e {stateless particle system} enables you
+ to move the system logic onto GPU if that seems beneficial. The initial
+ measurements indicate that the system is quite well balanced between
+ CPU and GPU. The stateless system also enables animating particles by
+ using a \l{Timeline}{timeline}. The model particles use instanced rendering
+ to boost the performance. Therefore, OpenGL ES 2.0 isn't sufficient to make
+ rendering performant, and at least OpenGL ES 3.0, Vulkan, or some other
+ modern backend is required.
+
+ To get a more concrete view on the actual performance, the video below shows
+ a particles Testbed application running on four different Android devices.
+ These devices and their chipsets and GPUs could be considered to be
+ lower-end to mid-range, confirming that the particles can perform well also
+ on affordable hardware.
+
+ \youtube 9MqUCP6JLCQ
+
+ \section1 Summary of 3D Particles
+
+ \note The \uicontrol {Particles 3D} components are released as a tech
+ preview feature in \QDS 2.2, and their functionality will be improved
+ in future releases.
+
+ The following table lists preset particle components.
+
+ \table
+ \header
+ \li Icon
+ \li Name
+ \li Purpose
+ \row
+ \li \inlineimage icons/attractor-16px.png
+ \li Attractor
+ \li Attracts particles towards a specific point.
+ \row
+ \li \inlineimage icons/emit-burst-16px.png
+ \li Emit Burst
+ \li Generates declarative emitter bursts.
+ \row
+ \li \inlineimage icons/emitter-16px.png
+ \li Emitter
+ \li Emits logical particles.
+ \row
+ \li \inlineimage icons/gravity-16px.png
+ \li Gravity
+ \li Accelerates particles to a vector of the specified magnitude in the
+ specified direction.
+ \row
+ \li \inlineimage icons/model-blend-particle-16px.png
+ \li Model Blend Particle
+ \li Blends a particle effect with an instance of a \uicontrol Model
+ component.
+ \row
+ \li \inlineimage icons/model-particle-16px.png
+ \li Model Particle
+ \li Creates a particle that uses an instance of a \uicontrol Model
+ component.
+ \row
+ \li \inlineimage icons/model-shape-16px.png
+ \li Model Shape
+ \li Provides 3D shapes from a model to emitters and affectors.
+ \row
+ \li \inlineimage icons/particle-shape-16px.png
+ \li Particle Shape
+ \li Provides 3D shapes to emitters and affectors.
+ \row
+ \li \inlineimage icons/particle-system-16px.png
+ \li Particle System
+ \li Creates a particle system that includes particle, emitter, and
+ affector components.
+ \row
+ \li \inlineimage icons/point-rotator-16px.png
+ \li Point Rotator
+ \li Rotates particles around a pivot point.
+ \row
+ \li \inlineimage icons/sprite-particle-16px.png
+ \li Sprite Particle
+ \li Creates particles that use a 2D sprite texture.
+ \row
+ \li \inlineimage icons/sprite-sequence-16px.png
+ \li Sprite Sequence
+ \li Provides image sequence features for \uicontrol {Sprite Particle}
+ component instances.
+ \row
+ \li \inlineimage icons/target-direction-16px.png
+ \li Target Direction
+ \li Specifies a direction towards the target position.
+ \row
+ \li \inlineimage icons/trail-emitter-16px.png
+ \li Trail Emitter
+ \li Emits logical particles from other particles.
+ \row
+ \li \inlineimage icons/vector-direction-16px.png
+ \li Vector Direction
+ \li Specifies a direction towards the target direction.
+ \row
+ \li \inlineimage icons/wander-16px.png
+ \li Wander
+ \li Applies random wave curves to particles.
+ \endtable
+*/
+
+/*!
+ \page studio-3d-particle-system.html
+ \previouspage studio-3d-particles.html
+ \nextpage studio-3d-logical-particles.html
+
+ \title Particle System
+
+ The preset \uicontrol {Particle System} component is the root of the
+ particle system. It ties all the other components together and manages
+ the shared progression in time. Emitters and affectors must either be
+ children of the same \uicontrol {Particle System} component or refer
+ to the same \uicontrol System to be able to interact with each other.
+
+ You can add several \uicontrol {Particle System} components. Typically, you
+ would use separate \uicontrol {Particle System} instances for the components
+ that interact with each other. Or, you can use just one instance if the
+ total number of components is small enough to be controllable.
+
+ Specify settings for the particle system in \l Properties >
+ \uicontrol {Particle System}.
+
+ \image studio-3d-properties-particle-system.png "Particle System properties"
+
+ You can freely animate the particle system property values using a timeline,
+ which enables you to synchronize particles with other animations, for
+ example.
+
+ In \uicontrol {Start time}, set the time in milliseconds where the system
+ starts. This can be useful to warm up the system so that a set of particles
+ has already been emitted when the simulation starts. For example, if you set
+ the start time to 2000 and animate \uicontrol Time from 0 to 1000, the
+ animation shows particles from 2000 ms to 3000 ms.
+
+ In \uicontrol Time, set the time in milliseconds for the system. If
+ you modify the value of this property, you should usually disable
+ \uicontrol Running to stop the simulation. All particles are destroyed
+ when you select it again.
+
+ To temporarily stop the simulation, select \uicontrol Paused. Particles
+ are not destroyed, and when you deselect the check box, the simulation
+ resumes from the point where you paused it.
+
+ Select \uicontrol Logging to collect particle system statistics, such as
+ the current and maximum amounts of particles in the system or the average
+ time in milliseconds used for emitting and animating particles in each
+ frame. Logging data can be useful when developing and optimizing the
+ particle effects.
+
+ \note Logging can negatively affect performance, so it should be disabled
+ before packaging applications for release and delivery to users.
+
+ Select \uicontrol {Use random seed} to randomize the particle system
+ with the seed that you specify in \uicontrol Seed to get an identical
+ pixel-perfect particle effect on every run. You should not modify the
+ seed value during particle animation.
+*/
+
+/*!
+ \page studio-3d-logical-particles.html
+ \previouspage studio-3d-particle-system.html
+ \nextpage studio-3d-particle-emitters.html
+
+ \title Logical Particles
+
+ All the particle system components act on \e {logical particles}. Each
+ particle has a logical representation within the particle system, and this
+ is what the components act upon. Not every logical particle needs to be
+ visualized, and some logical particles could lead to multiple visual
+ particles being drawn on screen.
+
+ Two different logical particle components are supported:
+ \uicontrol {Sprite Particle} for \l{Textures}{2D texture} particles and
+ \uicontrol {Model Particle} for \l{3D Models}{3D model} particles. Model
+ particles use \l{Instanced Rendering}{instanced rendering} to enabled the
+ rendering of thousands of particles, with full \l{3D Materials}{materials}
+ and \l{Lights}{lights} support.
+
+ The following components are available for adding logical particles and
+ for modifying their actions and appearance:
+
+ \list
+ \li \l{Sprite Particle}
+ \li \l{Sprite Sequence}
+ \li \l{Model Particle}
+ \li \l{Model Blend Particle}
+ \endlist
+
+ \section1 Sprite Particle
+
+ Specify properties for sprite particles in \uicontrol Properties >
+ \uicontrol {Sprite Particle}.
+
+ \image studio-3d-properties-sprite-particle.png "Sprite Particle properties"
+
+ \uicontrol {Blend mode} determines whether particles are blended using
+ source over, screen, or multiply mode. If you select \uicontrol SourceOver,
+ the pixel component values from a foreground source are written over the
+ source by using alpha blending. If you select \uicontrol Screen, the values
+ are negated, then multiplied, negated again, and written. If you select
+ \uicontrol Multiply, they are multiplied and written.
+
+ \uicontrol Sprite defines the \l{Textures}{Texture} component used for the
+ particles. For example, to use an image of a snowflake to simulate snow,
+ create an instance of the \uicontrol Texture component with the image
+ as the \uicontrol Source.
+
+ In \uicontrol {Sprite sequence}, select the \l{Sprite Sequence} component
+ instance for the particle if the sprite texture contains a frame sequence.
+ If your image only has a single sprite frame, don't set this value.
+
+ Select \uicontrol Billboard to specify that the particle texture should
+ always be aligned face towards the screen. Enabling this property
+ automatically disables \uicontrol Particle > \uicontrol {Align mode}.
+
+ \uicontrol {Particle scale} specifies the scale multiplier of the particles.
+ To adjust particle sizes in the emitter, set \uicontrol {Particle Emitter}
+ properties.
+
+ In \uicontrol {Color table}, select the \uicontrol Texture component that is
+ used for coloring the particles. The image can be a 1D or a 2D texture.
+ Horizontal pixels determine the particle color over the value you set in
+ \uicontrol {Particle Emitter} > \uicontrol {Life span}. For example, when
+ the particle is halfway through its life, it will have the color specified
+ halfway across the image. If the image is 2D, a vertical row is randomly
+ selected for each particle. For example, a \c {256 x 4} image contains 4
+ different coloring options for particles.
+
+ \section1 Sprite Sequence
+
+ Specify properties for a sprite particle sequence that contains a frame
+ sequence in \uicontrol Properties > \uicontrol {Particle Sprite Sequence}.
+
+ \image studio-3d-properties-particle-sprite-sequence.png "Particle Sprite Sequence properties"
+
+ \uicontrol {Frame count} specifies the amount of image frames in a sprite.
+ A particle animates through these frames during its duration. The frames
+ should be laid out horizontally in the same image file. For example, the
+ sprite could be a 512x64 image, with the frame count of 8. This would make
+ each particle frame size 64x64 pixels.
+
+ \uicontrol {Frame index} specifies the initial index of the frame.
+ This is the position between frames where the animation is started. For
+ example, when the frame index is 5 and \uicontrol {Animation direction} is
+ set to \uicontrol Normal, the first rendered frame is 5. If the animation
+ direction is set to \uicontrol Reverse, the first rendered frame is 4.
+
+ The value of \uicontrol {Frame index} must be between 0 and the value of
+ \uicontrol {Frame count} minus 1. When \uicontrol {Animation direction} is
+ set to \uicontrol SingleFrame and \uicontrol {Random start} is disabled, all
+ the particles will render sprites with the frame index.
+
+ Enable \uicontrol {Random start} to start the animation from a random frame
+ between 0 and \uicontrol {Frame count} minus 1. This allows animations to
+ not look like they all just started when the animation begins.
+
+ \uicontrol Interpolate determines whether sprites are blended
+ between frames to make the animation appear smoother.
+
+ \uicontrol Duration specifies the time in milliseconds that it
+ takes for the sprite sequence to animate. For example, if the duration
+ is 400 and the \uicontrol {Frame count} is 8, each frame will be shown
+ for 50 milliseconds. When the value is -1, \uicontrol Particle >
+ \uicontrol {Life span} is used as the duration.
+
+ \uicontrol {Duration variation} defines the duration variation in
+ milliseconds. The actual duration of the animation is between
+ duration minus duration variation and duration plus duration variation.
+
+ \uicontrol {Animation direction} defines the animation playback direction
+ of the sequence. Select \uicontrol Normal to play the animation from the
+ first frame to the last frame and to jump back to the first frame from the
+ last one. Select \uicontrol Reverse to reverse the normal order. Select
+ \uicontrol Alternate or \uicontrol AlternateReverse to alternate between
+ normal and reversed orders.
+
+ If you don't want to animate the frame, select \uicontrol SingleFrame.
+ When \uicontrol {Random start} is disabled, the frame set in
+ \uicontrol {Frame index} is rendered. When it is enabled, each particle
+ renders a random frame.
+
+ \section1 Model Particle
+
+ Specify properties for model particles in \uicontrol Properties >
+ \uicontrol {Model Particle}.
+
+ \image studio-3d-properties-model-particle.png "Model Particle properties"
+
+ In \uicontrol Delegate, select the \uicontrol {3D Model} component that
+ defines each object instantiated by the particle.
+
+ \uicontrol {Instance table} provides you with access to the internal
+ instancing table of the model particle that is used to implement efficient
+ rendering. This table can be applied to the instancing property of models
+ that are not part of the particle system.
+
+ You can use this feature also to provide an instancing table without
+ showing any particles. This is done by omitting the delegate.
+
+ \section1 Model Blend Particle
+
+ Specify properties for model blend particles in \uicontrol Properties >
+ \uicontrol {Model Blend Particle}.
+
+ \image studio-3d-properties-model-blend-particle.png "Model Blend Particle properties"
+
+ The \uicontrol {Model Blend Particle} component blends a particle effect
+ with a \uicontrol {3D Model} component. The provided model needs to be
+ triangle-based. Each triangle in the model is converted into a particle,
+ which are then used by the emitter. Instead of particle shader, the model
+ is shaded using the material specified in the model. The way the effect is
+ blended is determined by \uicontrol {Model blend mode}.
+
+ The possible blend modes are:
+
+ \list
+ \li \uicontrol Construct, where the model is constructed from the
+ particles.
+ \li \uicontrol Explode, where the model is converted into particles.
+ \li \uicontrol Transfer, where \uicontrol Construct and
+ \uicontrol Explode are combined to create an effect where the
+ model is transferred from one place to another.
+ \endlist
+
+ The particles are emitted in the order they are specified in the model
+ unless \uicontrol {Activation node} is set or \uicontrol Random is enabled.
+
+ In \uicontrol Delegate, select the \uicontrol {3D Model} component that
+ defines each object instantiated by the particle.
+
+ \uicontrol {End node} specifies the transformation for the model at the end
+ of a particle effect. It defines the size, position, and rotation where the
+ model is constructed when you set \uicontrol {Model blend mode} to
+ \uicontrol Construct or \uicontrol Explode.
+
+ \uicontrol {End time} specifies the end time of the particle in
+ milliseconds. The end time is used during construction and defines
+ duration from particle lifetime at the end where the effect is blended
+ with the model positions. Before the end time, the particles' positions
+ are defined only by the particle effect, but during the end time the
+ particle position is blended linearly with the model end position.
+
+ In \uicontrol {Activation node}, select the component instance that
+ activates particles and overrides the regular emit routine. The activation
+ node can be used to control how the particles are emitted spatially when
+ the model is exploded or constructed from the particles. The activation
+ node emits a particle if the center of that particle is on the positive
+ half of the z-axis of the activation node. Animating the activation node
+ to move trough the model will cause the particles to be emitted sequentially
+ along the path the activation node moves.
+
+ To emit particles in random order instead of in the order in which they are
+ specified in the model, select \uicontrol Random.
+
+ \section1 Common Particle Properties
+
+ The properties that you specify for logical particles in
+ \uicontrol Properties > \uicontrol Particle determine the common
+ appearance of all particles.
+
+ \image studio-3d-properties-particle.png "Particle properties"
+
+ \uicontrol {Max amount} allocates data for particles. Setting this value
+ instead of just growing the data based on \uicontrol {Particle Emitter} >
+ \uicontrol {Emit rate}, \uicontrol {Life span}, and \uicontrol {Emit Bursts}
+ enables you to optimize memory usage and to modify the emit rate and life
+ span without reallocation.
+
+ \uicontrol Color determines the base color for particles. You can use the
+ \l{Picking Colors}{Color Picker} to select colors. For color variation, set
+ values in \uicontrol {Color variation}. The values are in RGBA order (X=red,
+ Y=green, Z=blue, and W=alpha), and each value should be between 0.00
+ (no variation) and 1.00 (full variation).
+
+ To apply color variation uniformly for all the color channels, enable
+ \uicontrol {Unified color variation}. This applies all variations with
+ the same random amount.
+
+ \uicontrol {Fade in effect} and \uicontrol {Fade out effect} define the
+ fading effect used when the particles appear. Fading is implemented by
+ changing the value of opacity or scale between 0 and 1 over the time
+ specified in milliseconds in \uicontrol {Fade in duration} and
+ \uicontrol {Fade out duration}. Fading duration is calculated into the
+ particle lifespan. For example, if \uicontrol {Particle Emitter} >
+ \uicontrol {Life span} is 3000, \uicontrol {Fade in duration} is 500, and
+ \uicontrol {Fade out duration} is 500, the particle will be fully visible
+ for 2000 ms.
+
+ \uicontrol {Align mode} determines the direction that particles face:
+
+ \list
+ \li Select \uicontrol AlignNone to use the value set for the emitter
+ component in \uicontrol {Particle Rotation} > \uicontrol Rotation.
+ \li Select \uicontrol AlignTowardsTarget to align the particles towards
+ the direction set in \uicontrol {Align target position}.
+ \li Select \uicontrol AlignTowardsStartVelocity to align the particles
+ towards their starting velocity direction.
+ \endlist
+
+ This value takes no effect if \uicontrol {Sprite Particle} >
+ \uicontrol Billboard is enabled.
+
+ Unlike the materials used with the models, particles default to being
+ rendered with assuming semi-transparency, and so with blending enabled.
+ This is the desired behavior most of the time due to particle textures,
+ color (alpha) variations, fadings, and so on. If you don't need the
+ blending, disable \uicontrol {Has transparency} for possible performance
+ gain.
+
+ \uicontrol {Sort mode} determines the order in which the particles are
+ drawn. You can sort particles based on their distance from the camera,
+ farthest first, or lifetime, newest or oldest first.
+
+ The particles are emitted from the location of the \l Emitter or
+ \l {Trail Emitter} component instance.
+*/
+
+/*!
+ \page studio-3d-particle-emitters.html
+ \previouspage studio-3d-logical-particles.html
+ \nextpage studio-3d-particle-affectors.html
+
+ \title Particle Emitters
+
+ The \uicontrol {Particle Emitter} component emits logical particles into
+ the system. You can determine how individual particles will look like and
+ how they are emitted. Many of the properties have \e variation counterparts,
+ such as \uicontrol {Color variation}, for adding variation to the particles.
+
+ The \uicontrol {Trail Emitter} component emits particles from the location
+ of other logicial particles. Any logical particle of the followed component
+ within the bounds of a trail emitter will cause particle emission from its
+ location, as if there were an emitter on it with the same properties as the
+ trail emitter.
+
+ Emitter components can use instances of the \l {Particle Shape} or
+ \l {Model Shape} component to emit particles from the surface of the
+ selected shape.
+
+ You always need one emitter. If the \uicontrol {Particle System} component
+ instance is the direct parent of the emitter component instance, you don't
+ need to specify the particle system separately. However, you always need
+ to select the logical particle to emit, or nothing is emitted.
+
+ The following components are available for emitting particles:
+
+ \list
+ \li \l Emitter
+ \li \l {Trail Emitter}
+ \li \l {Emit Burst}
+ \li \l {Model Shape}
+ \li \l {Particle Shape}
+ \endlist
+
+ \section1 Emitter
+
+ Specify properties for particle emitters in \uicontrol Properties >
+ \uicontrol {Particle Emitter}. You need at least one emitter.
+
+ \image studio-3d-properties-particle-emitter.png "Particle Emitter properties"
+
+ \uicontrol Emitter emits \l{Logical Particles}{logical particles} that you
+ select in \uicontrol Particle, as defined by the other properties. If the
+ \uicontrol {Particle System} component instance is not the direct parent of
+ the emitter component instance, you need to select it in \uicontrol System.
+
+ You can control the amount of particles emitted per second by setting the
+ value of \uicontrol {Emit rate} or add \uicontrol {Emit Burst} component
+ instances in \uicontrol {Emit bursts} to emit bursts of specified amounts
+ of particles at the specified point in time for the specified duration.
+
+ In \uicontrol Velocity, set a starting velocity for emitted particles.
+ If velocity is not set, particles start motionless, and velocity is
+ determined by \l{Particle Affectors}{particle affectors}.
+
+ Use \uicontrol Enabled to turn an emitter on or off. Usually, this property
+ is used in code to conditionally turn emitters on and off. To continue
+ emitting bursts, set \uicontrol {Emit rate} to 0 instead of disabling
+ \uicontrol Enabled.
+
+ In \uicontrol Shape, select the instance of the \l {Particle Shape} or
+ \l {Model Shape} component to use. The shape is scaled, positioned, and
+ rotated based on the emitter node properties. When the shape \uicontrol Fill
+ property is set to false, particles are emitted only from the surface of the
+ shape. When the shape is not defined, particles are emitted from the center
+ point of the emitter.
+
+ In \uicontrol {Life span}, specify the lifespan of a single particle
+ in milliseconds. Specify variation in the particle lifespan in
+ \uicontrol {Life span variation}. For example, to emit particles that
+ will exist from three to four seconds, set \uicontrol {Life span} to
+ 3500 ms and \uicontrol {Life span variation} to 500 ms.
+
+ \uicontrol {Particle scale} and \uicontrol {Particle end scale} specify the
+ scale multiplier of the particles at the beginning and end. For variation
+ in particle size, specify values for \uicontrol {Particle scale variation}
+ and \uicontrol {Particle end scale variation}.
+
+ \uicontrol {Depth bias} specifies the the depth bias of the emitter. Depth
+ bias is added to the object's distance from camera when sorting objects.
+ This can be used to force the rendering order of objects that are located
+ close to each other if it might otherwise change between frames. Negative
+ values cause the sorting value to move closer to the camera while positive
+ values move it further from the camera.
+
+ \section1 Trail Emitter
+
+ Specify additional properties for particle trail emitters in
+ \uicontrol Properties > \uicontrol {Particle Trail Emitter}.
+
+ Select the logical particle component to follow in \uicontrol Follow.
+
+ \section1 Particle Rotation
+
+ Specify properties for the rotation of particles in \uicontrol Properties >
+ \uicontrol {Particle Rotation}
+
+ \image studio-3d-properties-particle-rotation.png "Particle Rotation properties"
+
+ \uicontrol Rotation specifies the rotation of the particles in the
+ beginning. Rotation is defined as degrees in euler angles. For variation
+ in rotation, specify values for \uicontrol Variation.
+
+ \uicontrol Velocity specifies the rotation velocity of the particles in the
+ beginning. Rotation velocity is defined as degrees per second in euler
+ angles. For variation in velocity, specify values in
+ \uicontrol {Velocity variation}.
+
+ \section1 Emit Burst
+
+ Specify properties for emit bursts in \uicontrol Properties >
+ \uicontrol {Emit Burst}.
+
+ \image studio-3d-properties-particle-emit-burst.png "Particle Emit Burst properties"
+
+ \uicontrol Time specifies the time in milliseconds when emitting the burst
+ starts, and \uicontrol Amount specifies the amount of particles emitted
+ during the time specified in milliseconds in \uicontrol Duration.
+
+ For example, you could use two \uicontrol {Emit Burst} instances to emit 100
+ particles at the beginning and 50 particles at 2 seconds, so that both
+ bursts take 200 milliseconds. For one instance, set \uicontrol Time to 0,
+ \uicontrol Amount to 100, and \uicontrol Duration to 200. For the other
+ instance, set \uicontrol Time to 2000, \uicontrol Amount to 50, and
+ \uicontrol Duration to 200.
+
+ \section1 Particle Shape
+
+ The \uicontrol {Particle Shape} component supports shapes, such as cube,
+ sphere, and cylinder, for emitting particles from their area.
+
+ Specify properties for particle shape in \uicontrol Properties >
+ \uicontrol {Particle Shape}.
+
+ \image studio-3d-properties-particle-shape.png "Particle Shape properties"
+
+ In \uicontrol Type, select the shape to use.
+
+ Select \uicontrol Fill to fill the shape instead of just displaying its
+ outline.
+
+ \uicontrol Extents determines the extent coordinates of the shape geometry.
+
+ \section1 Model Shape
+
+ The \uicontrol {Model Shape} component specifies a template for defining the
+ model.
+
+ Specify properties for model shape in \uicontrol Properties >
+ \uicontrol {Particle Model Shape}.
+
+ \image studio-3d-properties-particle-model-shape.png "Particle Model Shape properties"
+
+ In \uicontrol Delegate, select the \uicontrol {3D Model} component that
+ defines each object instantiated by the particle.
+
+ Select \uicontrol Fill to fill the shape instead of just displaying its
+ outline.
+*/
+
+/*!
+ \page studio-3d-particle-affectors.html
+ \previouspage studio-3d-particle-emitters.html
+ \nextpage studio-3d-particle-directions.html
+
+ \title Particle Affectors
+
+ Affectors are an optional component of a particle system. They can perform
+ a variety of manipulations to the simulation, such as altering the
+ trajectory of particles or prematurely ending their life in the simulation.
+ For performance reasons, it is recommended not to use affectors in
+ high-volume particle systems.
+
+ The following affector components control how the particles are animated
+ during their lifetime:
+
+ \list
+ \li \l Attractor attracts particles towards a specific point.
+ \li \l Gravity accelerates particles to a vector of the specified
+ magnitude in the specified direction.
+ \li \l {Point Rotator} rotates particles around a pivot point.
+ \li \l Wander applies random wave curves to particles.
+ \endlist
+
+ If the system has multiple affectors, the order of affectors may result in
+ different outcome, as affectors are applied one after another.
+
+ By default, affectors affect all particles in the system, but you can limit
+ this to the particles listed in \l Properties > \l {Particle Affector} >
+ \uicontrol Particles.
+
+ \section1 Attractor
+
+ The \uicontrol Attractor component attracts particles towards a position
+ inside the \l{3D Views}{View 3D} component instance. To model the gravity
+ of a massive object whose center of gravity is far away, use an instance of
+ the \l Gravity component.
+
+ The attraction position is defined either by using the position of the
+ attractor and the value of \uicontrol {Position variation} or by selecting
+ an instance of the \uicontrol {Particle Shape} or \uicontrol {Model Shape}
+ component in \uicontrol Shape. If both position and shape are defined, the
+ shape is used.
+
+ Specify settings for \uicontrol Attractor components in
+ \uicontrol Properties > \uicontrol {Particle Attractor}.
+
+ \image studio-3d-properties-particle-attractor.png "Particle Attractor properties"
+
+ \uicontrol {Position variation} specifies the variation on attract position.
+ Instead of attracting particles into a single point, it attracts them
+ randomly towards a wider area. For example, to attract particles into some
+ random point inside a (50, 50, 50) cube at position (100, 0, 0) within 2 to
+ 4 seconds, set \uicontrol X, \uicontrol Y, and \uicontrol Z to 50.00,
+ \uicontrol Duration to 3000, and \uicontrol {Duration variation} to 1000.
+
+ In \uicontrol Shape, select an instance of the \l {Particle Shape} or
+ \l {Model Shape} component to attract particles into a random position
+ inside the shape.
+
+ \uicontrol Duration specifies the duration in milliseconds that it takes
+ for particles to reach the attraction position. When the value is -1,
+ \uicontrol {Particle Emitter} > \uicontrol {Life span} is used as
+ the duration. If you specify \uicontrol {Duration variation}, the actual
+ duration to reach the attractor is between duration minus duration variation
+ and duration plus duration variation.
+
+ Select \uicontrol {Hide at end} to make the particle disappear when it
+ reaches the attractor.
+
+ Select \uicontrol {Use cached positions} to cache possible positions within
+ the attractor's \uicontrol Shape. Cached positions give less random results
+ but are better for performance.
+
+ \uicontrol {Positions amount} specifies the amount of possible positions
+ stored within the attractor's \uicontrol Shape. By default, the amount
+ equals the particle count, but you can specify a lower amount for a smaller
+ cache. Specify a higher amount for additional randomization.
+
+ \section1 Gravity
+
+ The \uicontrol Gravity component models the gravity of a massive object
+ whose center of gravity is far away, and thus the gravitational pull is
+ effectively constant across the scene. To model the gravity of an object
+ near or inside the scene, use an \l Attractor component instance.
+
+ Specify settings for \uicontrol Gravity component instances in
+ \uicontrol Properties > \uicontrol {Particle Gravity}.
+
+ \image studio-3d-properties-particle-gravity.png "Particle Gravity properties"
+
+ \uicontrol Magnitude defines the magnitude in particle position change in
+ degrees per second. A negative value accelerates in the opposite way from
+ the direction specified in \uicontrol Direction. Direction \uicontrol X,
+ \uicontrol Y, and \uicontrol Z values are automatically normalized to a unit
+ vector.
+
+ \section1 Point Rotator
+
+ Specify settings for \uicontrol {Point Rotator} component instances in
+ \uicontrol Properties > \uicontrol {Point Rotator}.
+
+ \image studio-3d-properties-particle-point-rotator.png "Particle Point Rotator properties"
+
+ The \uicontrol {Point Rotator} component rotates particles around the
+ pivot point specified in \uicontrol {Pivot point} towards the direction
+ specified in \uicontrol Direction. Direction \uicontrol X, \uicontrol Y, and
+ \uicontrol Z values are automatically normalized to a unit vector.
+
+ \uicontrol Magnitude defines the magnitude in particle position change in
+ degrees per second. A negative value accelerates in the opposite way from
+ the direction specified in \uicontrol Direction.
+
+ \section1 Wander
+
+ The \uicontrol Wander component applies random wave curves to particles.
+ Curves can combine \l {Global Wander Properties}{global} values that are
+ the same for all particles and \l{Unique Wander Properties}{unique} values
+ that differ randomly.
+
+ Specify settings for \uicontrol Wander component instances in
+ \uicontrol Properties > \uicontrol {Particle Wander}.
+
+ \image studio-3d-properties-particle-wander.png "Particle Wander properties"
+
+ \uicontrol {Fade in duration} specifies the duration in milliseconds for
+ fading in the affector. After this duration, the wandering will be in full
+ effect. Setting this value can be useful to emit from a specific position or
+ shape, otherwise wander will affect the position also at the beginning.
+
+ \uicontrol {Fade out duration} specifies the duration in milliseconds for
+ fading out the affector. Setting this value can be useful to reduce the
+ wander when the particle life time ends, for example when combined with an
+ instance of the \l Attractor component so that the end positions will match
+ the shape.
+
+ \section2 Global Wander Properties
+
+ Specify global settings for \uicontrol Wander component instances in
+ \uicontrol Properties > \uicontrol {Global}.
+
+ \image studio-3d-properties-particle-wander-global.png "Global Particle Wander properties"
+
+ \uicontrol Amount specifies the distance that each particle moves
+ at the ends of curves. For example, if you set the value of \uicontrol X to
+ 100.00, \uicontrol Y to 10.00, and \uicontrol Z to 0.00, all particles
+ wander between (100, 10, 0) and (-100, -10, 0).
+
+ \uicontrol Pace defines the frequency at which each particle wanders in
+ curves per second, starting from \uicontrol {Pace start}. The meaningful
+ range for pace start is between 0 .. 2 * PI.
+
+ \section2 Unique Wander Properties
+
+ Specify unique settings for the \uicontrol Wander component instances in
+ \uicontrol Properties > \uicontrol {Unique}.
+
+ \image studio-3d-properties-particle-wander-unique.png "Unique Particle Wander properties"
+
+ \uicontrol Amount specifies the distance that each particle moves
+ at the ends of curves. Specify amount variation for each particle between
+ 0.00 and 1.00 in \uicontrol {Amount variation}. When the amount variation is
+ 0.00, all particles reach the maximum amount. When it is 0.50, every
+ particle reaches between 0.50 and 1.50 of the amount. For example, if you
+ set \uicontrol Amount \uicontrol X to 100.00, \uicontrol Y to 50.00, and
+ \uicontrol Z to 20.00) and \uicontrol {Amount variation} to 0.10, the
+ particles' maximum wave distances are something random between (110, 55, 22)
+ and (90, 45, 18).
+
+ \uicontrol Pace defines the frequency at which each particle wanders in
+ curves per second. Specify unique pace variation for each particle between
+ 0.00 and 1.00 in \uicontrol {Pace variation}. When the variation is 0.00,
+ all particles wander at the same frequency. For example, if you set
+ \uicontrol Pace \uicontrol X to 1.00, \uicontrol Y to 2.00, and \uicontrol Z
+ to 4.00 and \uicontrol {Pace variation} to 0.50, the particles' wave paces
+ are something random between (2.00, 4.00, 8.00) and (0.50, 1.00, 2.00).
+
+ \section1 Particle Affector
+
+ Specify common settings for particle affectors in \uicontrol Properties >
+ \l {Particle Affector}.
+
+ \image studio-3d-properties-particle-affector.png "Affector properties"
+
+ If the affector is not a direct child component of the particle system,
+ select the \uicontrol {Particle System} component instance to affect in
+ \uicontrol System.
+
+ To only affect some of the particles in the particle system, select
+ them in \uicontrol Particles. Select \inlineimage plus.png
+ to add logical particles to the list.
+
+ Deselect \uicontrol Enabled to turn the affector off. Usually, this
+ property is used in code to conditionally turn affectors off and on.
+*/
+
+/*!
+ \page studio-3d-particle-directions.html
+ \previouspage studio-3d-particle-affectors.html
+ \nextpage quick-component-instances.html
+
+ \title Particle Directions
+
+ Directions can be specified by specifying the emitted particle velocity
+ towards either the target position or the target vector.
+
+ \section1 Target Direction
+
+ The \uicontrol {Target Direction} component sets emitted particle velocity
+ towards the target position.
+
+ Specify common settings for particle target direction in
+ \uicontrol Properties > \uicontrol {Particle Target Direction}.
+
+ \image studio-3d-properties-particle-target-direction.png "Particle Target Direction properties"
+
+ \uicontrol Position specifies the position of the target of
+ the particle. For variation in the target position, specify
+ \uicontrol {Position variation}.
+
+ \uicontrol Normalized determines whether the distance to the position
+ affects the magnitude of the particle's velocity. Enable
+ \uicontrol Normalize to derive the velocity amount only from
+ \uicontrol Magnitude and \uicontrol {Magnitude variation}.
+
+ \uicontrol Magnitude specifies the magnitude in \uicontrol Position change
+ per second. A negative value accelerates the in the opposite direction
+ from the position. When \uicontrol Normalized is disabled, this value is
+ multiplied with the distance to the target position. For variation in the
+ magnitude, specify \uicontrol {Magnitude variation}.
+
+ For example, to emit particles towards the target position (100, 0, 0) with
+ random magnitude between 10..20, set \uicontrol Magnitude to 15.00 and
+ \uicontrol {Magnitude variation} to 5.00. Further, enable
+ \uicontrol Normalized.
+
+ \section1 Vector Direction
+
+ The \uicontrol {Vector Direction} component sets emitted particle velocity
+ towards the target vector. The length of the direction vector is used as
+ the velocity magnitude.
+
+ Specify common settings for particle vector direction in
+ \uicontrol Properties > \uicontrol {Particle Vector Direction}.
+
+ \image studio-3d-properties-particle-vector-direction.png "Vector Direction properties"
+
+ \uicontrol Direction specifies the direction of the target of the particle.
+ A positive \uicontrol Y value means \e up, while a negative value means
+ \e down. A negative \uicontrol Z value causes the particles to move in the
+ direction opposite to the target vector.
+
+ For variation in the target direction, specify
+ \uicontrol {Direction variation}. Enable \uicontrol Normalized to
+ normalize direction after applying the variation. When it is disabled,
+ variation affects the magnitude of the particles' velocity. When
+ it is enabled, variation affects the direction, but the magnitude is
+ determined by the original direction length.
+*/
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp
index 1fda59c964..bb80c2e2e1 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp
@@ -397,8 +397,19 @@ void NodeInstanceServer::reparentInstances(const QVector<ReparentContainer> &con
if (hasInstanceForId(container.instanceId())) {
ServerNodeInstance instance = instanceForId(container.instanceId());
if (instance.isValid()) {
- instance.reparent(instanceForId(container.oldParentInstanceId()), container.oldParentProperty(),
- instanceForId(container.newParentInstanceId()), container.newParentProperty());
+ ServerNodeInstance newParent = instanceForId(container.newParentInstanceId());
+ PropertyName newParentProperty = container.newParentProperty();
+ if (!isInformationServer()) {
+ // Children of the component wraps are left out of the node tree to avoid
+ // incorrectly rendering them
+ if (newParent.isComponentWrap()) {
+ newParent = {};
+ newParentProperty.clear();
+ }
+ }
+ instance.reparent(instanceForId(container.oldParentInstanceId()),
+ container.oldParentProperty(),
+ newParent, newParentProperty);
}
}
}
@@ -1287,8 +1298,15 @@ PixmapChangedCommand NodeInstanceServer::createPixmapChangedCommand(const QList<
QVector<ImageContainer> imageVector;
for (const ServerNodeInstance &instance : instanceList) {
- if (instance.isValid() && instance.hasContent())
- imageVector.append(ImageContainer(instance.instanceId(), instance.renderImage(), instance.instanceId()));
+ if (!instance.isValid())
+ continue;
+
+ QImage renderImage;
+ // We need to return empty image if instance has no content to correctly update the
+ // item image in case the instance changed from having content to not having content.
+ if (instance.hasContent())
+ renderImage = instance.renderImage();
+ imageVector.append(ImageContainer(instance.instanceId(), renderImage, instance.instanceId()));
}
return PixmapChangedCommand(imageVector);
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp
index ab764a98b3..4eb8cdd65f 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp
@@ -415,6 +415,16 @@ bool ObjectNodeInstance::isLockedInEditor() const
return m_isLockedInEditor;
}
+bool ObjectNodeInstance::isComponentWrap() const
+{
+ return m_isComponentWrap;
+}
+
+void ObjectNodeInstance::setComponentWrap(bool wrap)
+{
+ m_isComponentWrap = wrap;
+}
+
void ObjectNodeInstance::setModifiedFlag(bool b)
{
m_isModified = b;
@@ -732,6 +742,10 @@ QObject *ObjectNodeInstance::createComponentWrap(const QString &nodeSource, cons
QQmlComponent *component = new QQmlComponent(context->engine());
QByteArray data(nodeSource.toUtf8());
+ if (data.isEmpty()) {
+ // Add a fake root element as an empty component is not valid and crashes in some cases
+ data.append("QtObject{}");
+ }
data.prepend(importCode);
component->setData(data, context->baseUrl().resolved(QUrl("createComponent.qml")));
QObject *object = component;
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.h
index c66365d392..67e1663496 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.h
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.h
@@ -202,6 +202,9 @@ public:
virtual void setLockedInEditor(bool b);
bool isLockedInEditor() const;
+ bool isComponentWrap() const;
+ void setComponentWrap(bool wrap);
+
void setModifiedFlag(bool b);
protected:
@@ -234,6 +237,7 @@ private:
bool m_isModified = false;
bool m_isLockedInEditor = false;
bool m_isHiddenInEditor = false;
+ bool m_isComponentWrap = false;
static QHash<EnumerationName, QVariant> m_enumationValueHash;
};
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.cpp
index 89e8c5d2db..2e1e03fa63 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.cpp
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.cpp
@@ -37,6 +37,7 @@
#include <addimportcontainer.h>
#include <createscenecommand.h>
#include <reparentinstancescommand.h>
+#include <removeinstancescommand.h>
#include <clearscenecommand.h>
#include <QDebug>
@@ -193,6 +194,32 @@ QList<QQuickItem*> Qt5NodeInstanceServer::allItems() const
return QList<QQuickItem*>();
}
+void Qt5NodeInstanceServer::markRepeaterParentDirty(qint32 id) const
+{
+ if (!hasInstanceForId(id))
+ return;
+
+ ServerNodeInstance instance = instanceForId(id);
+ if (!instance.isValid())
+ return;
+
+ ServerNodeInstance parentInstance = instance.parent();
+ if (!parentInstance.isValid())
+ return;
+
+ // If a Repeater instance was moved/removed, the old parent must be marked dirty to rerender it
+ const QByteArray type("QQuickRepeater");
+ if (ServerNodeInstance::isSubclassOf(instance.internalObject(), type))
+ DesignerSupport::addDirty(parentInstance.rootQuickItem(), QQuickDesignerSupport::Content);
+
+ // Repeater's parent must also be dirtied when a child of a repeater was moved/removed.
+ if (ServerNodeInstance::isSubclassOf(parentInstance.internalObject(), type)) {
+ ServerNodeInstance parentsParent = parentInstance.parent();
+ if (parentsParent.isValid())
+ DesignerSupport::addDirty(parentsParent.rootQuickItem(), QQuickDesignerSupport::Content);
+ }
+}
+
bool Qt5NodeInstanceServer::initRhi(RenderViewData &viewData)
{
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
@@ -523,9 +550,23 @@ void Qt5NodeInstanceServer::clearScene(const ClearSceneCommand &command)
void Qt5NodeInstanceServer::reparentInstances(const ReparentInstancesCommand &command)
{
+ const QVector<ReparentContainer> &containerVector = command.reparentInstances();
+ for (const ReparentContainer &container : containerVector)
+ markRepeaterParentDirty(container.instanceId());
+
NodeInstanceServer::reparentInstances(command.reparentInstances());
startRenderTimer();
}
+void Qt5NodeInstanceServer::removeInstances(const RemoveInstancesCommand &command)
+{
+ const QVector<qint32> &idVector = command.instanceIds();
+ for (const qint32 id : idVector)
+ markRepeaterParentDirty(id);
+
+ NodeInstanceServer::removeInstances(command);
+ startRenderTimer();
+}
+
} // QmlDesigner
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.h
index 4af451b61a..d93ecad84e 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.h
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.h
@@ -67,6 +67,7 @@ public:
void createScene(const CreateSceneCommand &command) override;
void clearScene(const ClearSceneCommand &command) override;
void reparentInstances(const ReparentInstancesCommand &command) override;
+ void removeInstances(const RemoveInstancesCommand &command) override;
QImage grabWindow() override;
QImage grabItem(QQuickItem *item) override;
@@ -79,6 +80,7 @@ protected:
void resetAllItems();
void setupScene(const CreateSceneCommand &command) override;
QList<QQuickItem*> allItems() const;
+ void markRepeaterParentDirty(qint32 id) const;
struct RenderViewData {
QPointer<QQuickWindow> window = nullptr;
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.cpp
index daa1cab473..fb3113bdcd 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.cpp
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.cpp
@@ -101,6 +101,10 @@ void Qt5PreviewNodeInstanceServer::changeState(const ChangeStateCommand &/*comma
QImage Qt5PreviewNodeInstanceServer::renderPreviewImage()
{
+ // Ensure the state preview image is always clipped properly to root item dimensions
+ if (auto rootItem = qobject_cast<QQuickItem *>(rootNodeInstance().internalObject()))
+ rootItem->setClip(true);
+
rootNodeInstance().updateDirtyNodeRecursive();
QRectF boundingRect = rootNodeInstance().boundingRect();
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.cpp
index 2046a2039e..1dae931af3 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.cpp
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.cpp
@@ -779,6 +779,9 @@ void QuickItemNodeInstance::reparent(const ObjectNodeInstance::Pointer &oldParen
ObjectNodeInstance::reparent(oldParentInstance, oldParentProperty, newParentInstance, newParentProperty);
+ if (!newParentInstance)
+ quickItem()->setParentItem(nullptr);
+
if (instanceIsValidLayoutable(newParentInstance, newParentProperty)) {
setInLayoutable(true);
setMovable(false);
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.cpp
index be4c3ea626..2aa17d9e2b 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.cpp
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.cpp
@@ -150,6 +150,11 @@ bool ServerNodeInstance::holdsGraphical() const
return m_nodeInstance->isQuickItem();
}
+bool ServerNodeInstance::isComponentWrap() const
+{
+ return m_nodeInstance->isComponentWrap();
+}
+
void ServerNodeInstance::updateDirtyNodeRecursive()
{
m_nodeInstance->updateAllDirtyNodesRecursive();
@@ -280,6 +285,8 @@ ServerNodeInstance ServerNodeInstance::create(NodeInstanceServer *nodeInstanceSe
instance.internalInstance()->setInstanceId(instanceContainer.instanceId());
+ instance.internalInstance()->setComponentWrap(componentWrap == WrapAsComponent);
+
instance.internalInstance()->initialize(instance.m_nodeInstance, instanceContainer.metaFlags());
// Handle hidden state to initialize pickable state
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.h
index 786a09fd47..52ead77e12 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.h
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.h
@@ -178,6 +178,8 @@ public:
void updateDirtyNodeRecursive();
bool holdsGraphical() const;
+ bool isComponentWrap() const;
+
private: // functions
ServerNodeInstance(const QSharedPointer<Internal::ObjectNodeInstance> &abstractInstance);
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/LoaderSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/LoaderSpecifics.qml
new file mode 100644
index 0000000000..2417c54587
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/LoaderSpecifics.qml
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+
+import QtQuick 2.15
+import QtQuick.Layouts 1.15
+import HelperWidgets 2.0
+import StudioTheme 1.0 as StudioTheme
+
+Column {
+ anchors.left: parent.left
+ anchors.right: parent.right
+
+ Section {
+ caption: qsTr("Loader")
+ anchors.left: parent.left
+ anchors.right: parent.right
+
+ SectionLayout {
+ PropertyLabel {
+ text: qsTr("Active")
+ tooltip: qsTr("This property is true if the Loader is currently active.")
+ }
+
+ SecondColumnLayout {
+ CheckBox {
+ text: backendValues.active.valueToString
+ backendValue: backendValues.active
+ implicitWidth: StudioTheme.Values.twoControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {
+ text: qsTr("Source")
+ tooltip: qsTr("This property holds the URL of the QML component to instantiate.")
+ }
+
+ SecondColumnLayout {
+ UrlChooser {
+ filter: "*.qml"
+ backendValue: backendValues.source
+ }
+
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {
+ text: qsTr("Source Component")
+ tooltip: qsTr("This property holds the component to instantiate.")
+ }
+
+ SecondColumnLayout {
+ ItemFilterComboBox {
+ typeFilter: "Component"
+ validator: RegExpValidator { regExp: /(^$|^[a-z_]\w*)/ }
+ backendValue: backendValues.sourceComponent
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {
+ text: qsTr("Asynchronous")
+ tooltip: qsTr("This property holds whether the component will be instantiated asynchronously.")
+ }
+
+ SecondColumnLayout {
+ CheckBox {
+ text: backendValues.asynchronous.valueToString
+ backendValue: backendValues.asynchronous
+ implicitWidth: StudioTheme.Values.twoControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ ExpandingSpacer {}
+ }
+ }
+ }
+}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/RepeaterSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/RepeaterSpecifics.qml
new file mode 100644
index 0000000000..33dfc387b1
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/RepeaterSpecifics.qml
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+import QtQuick 2.15
+import QtQuick.Layouts 1.15
+import HelperWidgets 2.0
+import StudioTheme 1.0 as StudioTheme
+
+Column {
+ anchors.left: parent.left
+ anchors.right: parent.right
+
+ Section {
+ caption: qsTr("Repeater")
+ anchors.left: parent.left
+ anchors.right: parent.right
+
+ SectionLayout {
+ PropertyLabel {
+ text: qsTr("Model")
+ tooltip: qsTr("The model providing data for the repeater. This can simply specify the number of delegate instances to create or it can be bound to an actual model.")
+ }
+
+ SecondColumnLayout {
+ LineEdit {
+ backendValue: backendValues.model
+ showTranslateCheckBox: false
+ writeAsExpression: true
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ width: implicitWidth
+ }
+
+ ExpandingSpacer {}
+ }
+
+ PropertyLabel {
+ text: qsTr("Delegate")
+ tooltip: qsTr("The delegate provides a template defining each object instantiated by the repeater.")
+ }
+
+ SecondColumnLayout {
+ ItemFilterComboBox {
+ typeFilter: "Component"
+ validator: RegExpValidator { regExp: /(^$|^[a-z_]\w*)/ }
+ backendValue: backendValues.delegate
+ implicitWidth: StudioTheme.Values.singleControlColumnWidth
+ + StudioTheme.Values.actionIndicatorWidth
+ }
+
+ ExpandingSpacer {}
+ }
+ }
+ }
+}
diff --git a/share/qtcreator/templates/wizards/projects/qtquickuiprototype/wizard.json b/share/qtcreator/templates/wizards/projects/qtquickuiprototype/wizard.json
index 366ec1e4cd..78dbbd9956 100644
--- a/share/qtcreator/templates/wizards/projects/qtquickuiprototype/wizard.json
+++ b/share/qtcreator/templates/wizards/projects/qtquickuiprototype/wizard.json
@@ -14,6 +14,7 @@
"options":
[
{ "key": "QmlProjectFileName", "value": "%{JS: Util.fileName(value('ProjectDirectory') + '/' + value('ProjectName'), 'qmlproject')}" },
+ { "key": "IsQt6", "value": "%{JS: value('QtVersion').IsQt6}" },
{ "key": "MainQmlFileName", "value": "%{JS: Util.fileName(value('ProjectName'), 'qml')}" },
{ "key": "QtQuickVersion", "value": "%{JS: value('QtVersion').QtQuickVersion}" },
{ "key": "QtQuickWindowVersion", "value": "%{JS: value('QtVersion').QtQuickWindowVersion}" },
@@ -50,7 +51,8 @@
{
"QtQuickVersion": "",
"QtQuickWindowVersion": "",
- "QtQuickVirtualKeyboardImport": ""
+ "QtQuickVirtualKeyboardImport": "",
+ "IsQt6": true
}
},
{
@@ -59,7 +61,8 @@
{
"QtQuickVersion": "2.15",
"QtQuickWindowVersion": "2.15",
- "QtQuickVirtualKeyboardImport": "QtQuick.VirtualKeyboard 2.15"
+ "QtQuickVirtualKeyboardImport": "QtQuick.VirtualKeyboard 2.15",
+ "IsQt6": false
}
},
{
@@ -68,7 +71,8 @@
{
"QtQuickVersion": "2.14",
"QtQuickWindowVersion": "2.14",
- "QtQuickVirtualKeyboardImport": "QtQuick.VirtualKeyboard 2.14"
+ "QtQuickVirtualKeyboardImport": "QtQuick.VirtualKeyboard 2.14",
+ "IsQt6": false
}
},
{
@@ -77,7 +81,8 @@
{
"QtQuickVersion": "2.13",
"QtQuickWindowVersion": "2.13",
- "QtQuickVirtualKeyboardImport": "QtQuick.VirtualKeyboard 2.4"
+ "QtQuickVirtualKeyboardImport": "QtQuick.VirtualKeyboard 2.4",
+ "IsQt6": false
}
},
{
@@ -86,7 +91,8 @@
{
"QtQuickVersion": "2.12",
"QtQuickWindowVersion": "2.12",
- "QtQuickVirtualKeyboardImport": "QtQuick.VirtualKeyboard 2.4"
+ "QtQuickVirtualKeyboardImport": "QtQuick.VirtualKeyboard 2.4",
+ "IsQt6": false
}
}
]
diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp
index d63ae11611..3ce402589c 100644
--- a/src/libs/qmljs/qmljscheck.cpp
+++ b/src/libs/qmljs/qmljscheck.cpp
@@ -617,7 +617,6 @@ class UnsupportedTypesByQmlUi : public QStringList
{
public:
UnsupportedTypesByQmlUi() : QStringList({"ShaderEffect",
- "Component",
"Drawer"})
{
append(UnsupportedTypesByVisualDesigner());
diff --git a/src/libs/utils/environment.cpp b/src/libs/utils/environment.cpp
index 2d3d6edf3b..76f32ccb98 100644
--- a/src/libs/utils/environment.cpp
+++ b/src/libs/utils/environment.cpp
@@ -136,8 +136,7 @@ void Environment::setupEnglishOutput()
set("LANGUAGE", "en_US:en");
}
-static FilePath searchInDirectory(const Environment &env,
- const QStringList &execs,
+static FilePath searchInDirectory(const QStringList &execs,
const FilePath &directory,
QSet<FilePath> &alreadyChecked)
{
@@ -226,7 +225,7 @@ static FilePath searchInDirectoriesHelper(const Environment &env,
QSet<FilePath> alreadyChecked;
for (const FilePath &dir : dirs) {
- FilePath tmp = searchInDirectory(env, execs, dir, alreadyChecked);
+ FilePath tmp = searchInDirectory(execs, dir, alreadyChecked);
if (!tmp.isEmpty() && (!func || func(tmp)))
return tmp;
}
@@ -236,7 +235,7 @@ static FilePath searchInDirectoriesHelper(const Environment &env,
return FilePath();
for (const FilePath &p : env.path()) {
- FilePath tmp = searchInDirectory(env, execs, p, alreadyChecked);
+ FilePath tmp = searchInDirectory(execs, p, alreadyChecked);
if (!tmp.isEmpty() && (!func || func(tmp)))
return tmp;
}
@@ -281,14 +280,14 @@ FilePaths Environment::findAllInPath(const QString &executable,
QSet<FilePath> result;
QSet<FilePath> alreadyChecked;
for (const FilePath &dir : additionalDirs) {
- FilePath tmp = searchInDirectory(*this, execs, dir, alreadyChecked);
+ FilePath tmp = searchInDirectory(execs, dir, alreadyChecked);
if (!tmp.isEmpty() && (!func || func(tmp)))
result << tmp;
}
if (!executable.contains('/')) {
for (const FilePath &p : path()) {
- FilePath tmp = searchInDirectory(*this, execs, p, alreadyChecked);
+ FilePath tmp = searchInDirectory(execs, p, alreadyChecked);
if (!tmp.isEmpty() && (!func || func(tmp)))
result << tmp;
}
diff --git a/src/libs/utils/pathlisteditor.cpp b/src/libs/utils/pathlisteditor.cpp
index 61d215fd83..d24be4995b 100644
--- a/src/libs/utils/pathlisteditor.cpp
+++ b/src/libs/utils/pathlisteditor.cpp
@@ -130,6 +130,7 @@ PathListEditor::PathListEditor(QWidget *parent) :
});
addButton(tr("Delete Line"), this, [this] { deletePathAtCursor(); });
addButton(tr("Clear"), this, [this] { d->edit->clear(); });
+ connect(d->edit, &QPlainTextEdit::textChanged, this, &PathListEditor::changed);
}
PathListEditor::~PathListEditor()
diff --git a/src/libs/utils/pathlisteditor.h b/src/libs/utils/pathlisteditor.h
index 07392fbe6e..edd70336c0 100644
--- a/src/libs/utils/pathlisteditor.h
+++ b/src/libs/utils/pathlisteditor.h
@@ -58,6 +58,9 @@ public:
void setPathList(const QString &pathString);
void setFileDialogTitle(const QString &l);
+signals:
+ void changed();
+
protected:
// Index after which to insert further "Add" buttons
static const int lastInsertButtonIndex;
diff --git a/src/plugins/android/androidavdmanager.cpp b/src/plugins/android/androidavdmanager.cpp
index 27af7b76db..1b30803910 100644
--- a/src/plugins/android/androidavdmanager.cpp
+++ b/src/plugins/android/androidavdmanager.cpp
@@ -203,6 +203,7 @@ bool AndroidAvdManager::removeAvd(const QString &name) const
qCDebug(avdManagerLog) << "Running command (removeAvd):" << command.toUserOutput();
QtcProcess proc;
proc.setTimeoutS(5);
+ proc.setEnvironment(AndroidConfigurations::toolsEnvironment(m_config));
proc.setCommand(command);
proc.runBlocking();
return proc.result() == QtcProcess::FinishedWithSuccess;
diff --git a/src/plugins/android/androidbuildapkstep.cpp b/src/plugins/android/androidbuildapkstep.cpp
index 4c47418c85..dc6b42754c 100644
--- a/src/plugins/android/androidbuildapkstep.cpp
+++ b/src/plugins/android/androidbuildapkstep.cpp
@@ -196,7 +196,7 @@ QWidget *AndroidBuildApkWidget::createApplicationGroup()
});
auto formLayout = new QFormLayout(group);
- formLayout->addRow(tr("Android build SDK:"), targetSDKComboBox);
+ formLayout->addRow(tr("Android build platform SDK:"), targetSDKComboBox);
auto createAndroidTemplatesButton = new QPushButton(tr("Create Templates"));
createAndroidTemplatesButton->setToolTip(
@@ -939,7 +939,8 @@ QVariant AndroidBuildApkStep::data(Utils::Id id) const
}
if (id == Constants::SdkLocation)
return QVariant::fromValue(AndroidConfigurations::currentConfig().sdkLocation());
- if (id == Constants::AndroidABIs)
+
+ if (id == Constants::AndroidMkSpecAbis)
return AndroidManager::applicationAbis(target());
return AbstractProcessStep::data(id);
diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp
index f1eb6379d6..c769a744d2 100644
--- a/src/plugins/android/androidconfigurations.cpp
+++ b/src/plugins/android/androidconfigurations.cpp
@@ -1127,10 +1127,9 @@ void AndroidConfigurations::removeOldToolChains()
void AndroidConfigurations::removeUnusedDebuggers()
{
- const QList<BaseQtVersion *> qtVersions
- = QtVersionManager::versions([](const BaseQtVersion *v) {
- return v->type() == Constants::ANDROIDQT;
- });
+ const QList<BaseQtVersion*> qtVersions = QtVersionManager::versions([](const BaseQtVersion *v) {
+ return v->type() == Constants::ANDROID_QT_TYPE;
+ });
QVector<FilePath> uniqueNdks;
for (const BaseQtVersion *qt : qtVersions) {
@@ -1267,8 +1266,8 @@ void AndroidConfigurations::updateAutomaticKitList()
removeUnusedDebuggers();
QHash<Abi, QList<const BaseQtVersion *> > qtVersionsForArch;
- const QList<BaseQtVersion *> qtVersions = QtVersionManager::versions([](const BaseQtVersion *v) {
- return v->type() == Constants::ANDROIDQT;
+ const QList<BaseQtVersion*> qtVersions = QtVersionManager::versions([](const BaseQtVersion *v) {
+ return v->type() == Constants::ANDROID_QT_TYPE;
});
for (const BaseQtVersion *qtVersion : qtVersions) {
const Abis qtAbis = qtVersion->qtAbis();
@@ -1322,9 +1321,8 @@ void AndroidConfigurations::updateAutomaticKitList()
QStringList abis = static_cast<const AndroidQtVersion *>(qt)->androidAbis();
Debugger::DebuggerKitAspect::setDebugger(k, findOrRegisterDebugger(tc, abis));
- k->setSticky(ToolChainKitAspect::id(), true);
+ BuildDeviceKitAspect::setDeviceId(k, DeviceManager::defaultDesktopDevice()->id());
k->setSticky(QtKitAspect::id(), true);
- k->setSticky(DeviceKitAspect::id(), true);
k->setMutable(DeviceKitAspect::id(), true);
k->setSticky(DeviceTypeKitAspect::id(), true);
diff --git a/src/plugins/android/androidconstants.h b/src/plugins/android/androidconstants.h
index df8c821f98..02bb50b348 100644
--- a/src/plugins/android/androidconstants.h
+++ b/src/plugins/android/androidconstants.h
@@ -42,9 +42,9 @@ namespace Internal {
namespace Constants {
const char ANDROID_SETTINGS_ID[] = "BB.Android Configurations";
const char ANDROID_TOOLCHAIN_TYPEID[] = "Qt4ProjectManager.ToolChain.Android";
-const char ANDROIDQT[] = "Qt4ProjectManager.QtVersion.Android";
+const char ANDROID_QT_TYPE[] = "Qt4ProjectManager.QtVersion.Android";
-const char ANDROID_AMSTARTARGS[] = "Android.AmStartArgs";
+const char ANDROID_AM_START_ARGS[] = "Android.AmStartArgs";
// Note: Can be set on RunConfiguration using an aspect and/or
// the AndroidRunnerWorker using recordData()
const char ANDROID_PRESTARTSHELLCMDLIST[] = "Android.PreStartShellCmdList";
@@ -67,32 +67,33 @@ const char ANDROID_ARCHITECTURE[] = "Android.Architecture";
const char ANDROID_PACKAGE_SOURCE_DIR[] = "ANDROID_PACKAGE_SOURCE_DIR";
const char ANDROID_EXTRA_LIBS[] = "ANDROID_EXTRA_LIBS";
const char ANDROID_ABI[] = "ANDROID_ABI";
+const char ANDROID_TARGET_ARCH[] = "ANDROID_TARGET_ARCH";
const char ANDROID_ABIS[] = "ANDROID_ABIS";
const char ANDROID_APPLICATION_ARGUMENTS[] = "ANDROID_APPLICATION_ARGUMENTS";
-const char QT_ANDROID_APPLICATION_ARGUMENTS[] = "QT_ANDROID_APPLICATION_ARGUMENTS";
const char ANDROID_DEPLOYMENT_SETTINGS_FILE[] = "ANDROID_DEPLOYMENT_SETTINGS_FILE";
const char ANDROID_SO_LIBS_PATHS[] = "ANDROID_SO_LIBS_PATHS";
-const char ANDROID_PACKAGENAME[] = "Android.PackageName";
-const char ANDROID_PACKAGE_INSTALLATION_STEP_ID[]
- = "Qt4ProjectManager.AndroidPackageInstallationStep";
+const char ANDROID_PACKAGE_INSTALL_STEP_ID[] = "Qt4ProjectManager.AndroidPackageInstallationStep";
const char ANDROID_BUILD_APK_ID[] = "QmakeProjectManager.AndroidBuildApkStep";
const char ANDROID_DEPLOY_QT_ID[] = "Qt4ProjectManager.AndroidDeployQtStep";
const char AndroidPackageSourceDir[] = "AndroidPackageSourceDir"; // QString
const char AndroidDeploySettingsFile[] = "AndroidDeploySettingsFile"; // QString
const char AndroidExtraLibs[] = "AndroidExtraLibs"; // QStringList
-// REMOVE ME
-const char AndroidArch[] = "AndroidArch"; // QString
+const char AndroidAbi[] = "AndroidAbi"; // QString
+const char AndroidAbis[] = "AndroidAbis"; // QStringList
+const char AndroidMkSpecAbis[] = "AndroidMkSpecAbis"; // QStringList
const char AndroidSoLibPath[] = "AndroidSoLibPath"; // QStringList
const char AndroidTargets[] = "AndroidTargets"; // QStringList
+const char AndroidApplicationArgs[] = "AndroidApplicationArgs"; // QString
+
+// For qbs support
const char AndroidApk[] = "Android.APK"; // QStringList
const char AndroidManifest[] = "Android.Manifest"; // QStringList
const char AndroidNdkPlatform[] = "AndroidNdkPlatform"; //QString
const char NdkLocation[] = "NdkLocation"; // FileName
const char SdkLocation[] = "SdkLocation"; // FileName
-const char AndroidABIs[] = "AndroidABIs"; // QString
// Android Device
const Utils::Id AndroidSerialNumber = "AndroidSerialNumber";
diff --git a/src/plugins/android/androiddeployqtstep.cpp b/src/plugins/android/androiddeployqtstep.cpp
index 12d3b3e651..8c50c7a52c 100644
--- a/src/plugins/android/androiddeployqtstep.cpp
+++ b/src/plugins/android/androiddeployqtstep.cpp
@@ -149,14 +149,14 @@ bool AndroidDeployQtStep::init()
info = androidDeployQtStep->m_deviceInfo;
const BuildSystem *bs = buildSystem();
- auto selectedAbis = bs->property(Constants::ANDROID_ABIS).toStringList();
+ auto selectedAbis = bs->property(Constants::AndroidAbis).toStringList();
const QString buildKey = target()->activeBuildKey();
if (selectedAbis.isEmpty())
- selectedAbis = bs->extraData(buildKey, Constants::ANDROID_ABIS).toStringList();
+ selectedAbis = bs->extraData(buildKey, Constants::AndroidAbis).toStringList();
if (selectedAbis.isEmpty())
- selectedAbis.append(bs->extraData(buildKey, Constants::AndroidArch).toString());
+ selectedAbis.append(bs->extraData(buildKey, Constants::AndroidAbi).toString());
if (!info.isValid()) {
const IDevice *dev = DeviceKitAspect::device(kit()).data();
diff --git a/src/plugins/android/androidmanager.cpp b/src/plugins/android/androidmanager.cpp
index 7d2a568303..e8944fba17 100644
--- a/src/plugins/android/androidmanager.cpp
+++ b/src/plugins/android/androidmanager.cpp
@@ -408,7 +408,7 @@ QString AndroidManager::apkDevicePreferredAbi(const Target *target)
auto libsPath = androidBuildDirectory(target).pathAppended("libs");
if (!libsPath.exists()) {
if (const ProjectNode *node = currentProjectNode(target))
- return preferredAbi(node->data(Android::Constants::ANDROID_ABIS).toStringList(),
+ return preferredAbi(node->data(Android::Constants::AndroidAbis).toStringList(),
target);
}
QStringList apkAbis;
diff --git a/src/plugins/android/androidpackageinstallationstep.cpp b/src/plugins/android/androidpackageinstallationstep.cpp
index cc951859ea..e1eb82e0cc 100644
--- a/src/plugins/android/androidpackageinstallationstep.cpp
+++ b/src/plugins/android/androidpackageinstallationstep.cpp
@@ -196,7 +196,7 @@ void AndroidPackageInstallationStep::doRun()
AndroidPackageInstallationFactory::AndroidPackageInstallationFactory()
{
- registerStep<AndroidPackageInstallationStep>(Constants::ANDROID_PACKAGE_INSTALLATION_STEP_ID);
+ registerStep<AndroidPackageInstallationStep>(Constants::ANDROID_PACKAGE_INSTALL_STEP_ID);
setSupportedStepList(ProjectExplorer::Constants::BUILDSTEPS_BUILD);
setSupportedDeviceType(Android::Constants::ANDROID_DEVICE_TYPE);
setRepeatable(false);
diff --git a/src/plugins/android/androidpotentialkit.cpp b/src/plugins/android/androidpotentialkit.cpp
index 128c853c84..79a1afebec 100644
--- a/src/plugins/android/androidpotentialkit.cpp
+++ b/src/plugins/android/androidpotentialkit.cpp
@@ -74,7 +74,7 @@ bool AndroidPotentialKit::isEnabled() const
}
return QtSupport::QtVersionManager::version([](const QtSupport::BaseQtVersion *v) {
- return v->isValid() && v->type() == QString::fromLatin1(Constants::ANDROIDQT);
+ return v->isValid() && v->type() == QString::fromLatin1(Constants::ANDROID_QT_TYPE);
});
}
diff --git a/src/plugins/android/androidqtversion.cpp b/src/plugins/android/androidqtversion.cpp
index 3cc9b06ccd..b53dc9e8d0 100644
--- a/src/plugins/android/androidqtversion.cpp
+++ b/src/plugins/android/androidqtversion.cpp
@@ -197,7 +197,7 @@ void AndroidQtVersion::parseMkSpec(ProFileEvaluator *evaluator) const
{
m_androidAbis = evaluator->values("ALL_ANDROID_ABIS");
if (m_androidAbis.isEmpty())
- m_androidAbis = QStringList{evaluator->value("ANDROID_TARGET_ARCH")};
+ m_androidAbis = QStringList{evaluator->value(Constants::ANDROID_TARGET_ARCH)};
const QString androidPlatform = evaluator->value("ANDROID_PLATFORM");
if (!androidPlatform.isEmpty()) {
const QRegularExpression regex("android-(\\d+)");
@@ -232,7 +232,7 @@ QSet<Utils::Id> AndroidQtVersion::targetDeviceTypes() const
AndroidQtVersionFactory::AndroidQtVersionFactory()
{
setQtVersionCreator([] { return new AndroidQtVersion; });
- setSupportedType(Constants::ANDROIDQT);
+ setSupportedType(Constants::ANDROID_QT_TYPE);
setPriority(90);
setRestrictionChecker([](const SetupData &setup) {
diff --git a/src/plugins/android/androidrunconfiguration.cpp b/src/plugins/android/androidrunconfiguration.cpp
index 5b834fa673..e1477c2615 100644
--- a/src/plugins/android/androidrunconfiguration.cpp
+++ b/src/plugins/android/androidrunconfiguration.cpp
@@ -82,13 +82,13 @@ AndroidRunConfiguration::AndroidRunConfiguration(Target *target, Utils::Id id)
if (target->buildConfigurations().first()->buildType() == BuildConfiguration::BuildType::Release) {
const QString buildKey = target->activeBuildKey();
target->buildSystem()->setExtraData(buildKey,
- Android::Constants::ANDROID_APPLICATION_ARGUMENTS,
- extraAppArgsAspect->arguments(target->macroExpander()));
+ Android::Constants::AndroidApplicationArgs,
+ extraAppArgsAspect->arguments(target->macroExpander()));
}
});
auto amStartArgsAspect = addAspect<StringAspect>();
- amStartArgsAspect->setId(Constants::ANDROID_AMSTARTARGS);
+ amStartArgsAspect->setId(Constants::ANDROID_AM_START_ARGS);
amStartArgsAspect->setSettingsKey("Android.AmStartArgsKey");
amStartArgsAspect->setLabelText(tr("Activity manager start options:"));
amStartArgsAspect->setDisplayStyle(StringAspect::LineEditDisplay);
diff --git a/src/plugins/android/androidrunnerworker.cpp b/src/plugins/android/androidrunnerworker.cpp
index db28291511..83c512a46a 100644
--- a/src/plugins/android/androidrunnerworker.cpp
+++ b/src/plugins/android/androidrunnerworker.cpp
@@ -279,7 +279,7 @@ AndroidRunnerWorker::AndroidRunnerWorker(RunWorker *runner, const QString &packa
m_extraAppParams = runControl->runnable().command.arguments();
}
- if (auto aspect = runControl->aspect(Constants::ANDROID_AMSTARTARGS)) {
+ if (auto aspect = runControl->aspect(Constants::ANDROID_AM_START_ARGS)) {
QTC_CHECK(aspect->value().type() == QVariant::String);
const QString startArgs = aspect->value().toString();
m_amStartExtraArgs = ProcessArgs::splitArgs(startArgs, OsTypeOtherUnix);
diff --git a/src/plugins/clangcodemodel/clangdclient.cpp b/src/plugins/clangcodemodel/clangdclient.cpp
index fea5314001..5b3e76bee8 100644
--- a/src/plugins/clangcodemodel/clangdclient.cpp
+++ b/src/plugins/clangcodemodel/clangdclient.cpp
@@ -66,14 +66,18 @@
#include <utils/runextensions.h>
#include <QCheckBox>
+#include <QDateTime>
+#include <QElapsedTimer>
#include <QFile>
#include <QHash>
#include <QPair>
#include <QPointer>
#include <QRegularExpression>
+#include <QtConcurrent>
#include <set>
#include <unordered_map>
+#include <utility>
using namespace CPlusPlus;
using namespace Core;
@@ -89,6 +93,7 @@ static Q_LOGGING_CATEGORY(clangdLog, "qtc.clangcodemodel.clangd", QtWarningMsg);
static Q_LOGGING_CATEGORY(clangdLogServer, "qtc.clangcodemodel.clangd.server", QtWarningMsg);
static Q_LOGGING_CATEGORY(clangdLogAst, "qtc.clangcodemodel.clangd.ast", QtWarningMsg);
static Q_LOGGING_CATEGORY(clangdLogHighlight, "qtc.clangcodemodel.clangd.highlight", QtWarningMsg);
+static Q_LOGGING_CATEGORY(clangdLogTiming, "qtc.clangcodemodel.clangd.timing", QtWarningMsg);
static QString indexingToken() { return "backgroundIndexProgress"; }
class AstNode : public JsonObject
@@ -171,6 +176,12 @@ public:
bool isNamespace() const { return role() == "declaration" && kind() == "Namespace"; }
+ bool isTemplateParameterDeclaration() const
+ {
+ return role() == "declaration" && (kind() == "TemplateTypeParm"
+ || kind() == "NonTypeTemplateParm");
+ };
+
QString type() const
{
const Utils::optional<QString> arcanaString = arcana();
@@ -305,14 +316,6 @@ static QList<AstNode> getAstPath(const AstNode &root, const Range &range)
return path;
}
-static AstNode getAstNode(const AstNode &root, const Range &range)
-{
- const QList<AstNode> path = getAstPath(root, range);
- if (!path.isEmpty())
- return path.last();
- return {};
-}
-
static Usage::Type getUsageType(const QList<AstNode> &path)
{
bool potentialWrite = false;
@@ -773,6 +776,109 @@ private:
std::unordered_map<DocType, VersionedDocData<DocType, DataType>> m_data;
};
+class TaskTimer
+{
+public:
+ TaskTimer(const QString &task) : m_task(task) {}
+
+ void stopTask()
+ {
+ // This can happen due to the RAII mechanism employed with SubtaskTimer.
+ // The subtask timers will expire immediately after, so this does not distort
+ // the timing data.
+ if (m_subtasks > 0) {
+ QTC_CHECK(m_timer.isValid());
+ m_elapsedMs += m_timer.elapsed();
+ m_timer.invalidate();
+ m_subtasks = 0;
+ }
+ m_started = false;
+ qCDebug(clangdLogTiming).noquote().nospace() << m_task << ": took " << m_elapsedMs
+ << " ms in UI thread";
+ }
+ void startSubtask()
+ {
+ // We have some callbacks that are either synchronous or asynchronous, depending on
+ // dynamic conditions. In the sync case, we will have nested subtasks, in which case
+ // the inner ones must not collect timing data, as their code blocks are already covered.
+ if (++m_subtasks > 1)
+ return;
+ if (!m_started) {
+ QTC_ASSERT(m_elapsedMs == 0, m_elapsedMs = 0);
+ m_started = true;
+ m_finalized = false;
+ qCDebug(clangdLogTiming).noquote().nospace() << m_task << ": starting";
+ }
+ qCDebug(clangdLogTiming).noquote().nospace() << m_task << ": subtask started at "
+ << QDateTime::currentDateTime().toString();
+ QTC_CHECK(!m_timer.isValid());
+ m_timer.start();
+ }
+ void stopSubtask(bool isFinalizing)
+ {
+ if (m_subtasks == 0) // See stopTask().
+ return;
+ if (isFinalizing)
+ m_finalized = true;
+ if (--m_subtasks > 0) // See startSubtask().
+ return;
+ qCDebug(clangdLogTiming).noquote().nospace() << m_task << ": subtask stopped at "
+ << QDateTime::currentDateTime().toString();
+ QTC_CHECK(m_timer.isValid());
+ m_elapsedMs += m_timer.elapsed();
+ m_timer.invalidate();
+ if (m_finalized)
+ stopTask();
+ }
+
+private:
+ const QString m_task;
+ QElapsedTimer m_timer;
+ qint64 m_elapsedMs = 0;
+ int m_subtasks = 0;
+ bool m_started = false;
+ bool m_finalized = false;
+};
+
+class SubtaskTimer
+{
+public:
+ SubtaskTimer(TaskTimer &timer) : m_timer(timer) { m_timer.startSubtask(); }
+ ~SubtaskTimer() { m_timer.stopSubtask(m_isFinalizing); }
+
+protected:
+ void makeFinalizing() { m_isFinalizing = true; }
+
+private:
+ TaskTimer &m_timer;
+ bool m_isFinalizing = false;
+};
+
+class FinalizingSubtaskTimer : public SubtaskTimer
+{
+public:
+ FinalizingSubtaskTimer(TaskTimer &timer) : SubtaskTimer(timer) { makeFinalizing(); }
+};
+
+class ThreadedSubtaskTimer
+{
+public:
+ ThreadedSubtaskTimer(const QString &task) : m_task(task)
+ {
+ qCDebug(clangdLogTiming).noquote().nospace() << m_task << ": starting thread";
+ m_timer.start();
+ }
+
+ ~ThreadedSubtaskTimer()
+ {
+ qCDebug(clangdLogTiming).noquote().nospace() << m_task << ": took " << m_timer.elapsed()
+ << " ms in dedicated thread";
+ }
+private:
+ const QString m_task;
+ QElapsedTimer m_timer;
+};
+
class ClangdClient::Private
{
public:
@@ -812,7 +918,7 @@ public:
using TextDocOrFile = const Utils::variant<const TextDocument *, Utils::FilePath>;
using AstHandler = const std::function<void(const AstNode &ast, const MessageId &)>;
MessageId getAndHandleAst(TextDocOrFile &doc, AstHandler &astHandler,
- AstCallbackMode callbackMode);
+ AstCallbackMode callbackMode, const Range &range = {});
ClangdClient * const q;
const CppEditor::ClangdSettings::Data settings;
@@ -821,9 +927,13 @@ public:
Utils::optional<SwitchDeclDefData> switchDeclDefData;
Utils::optional<LocalRefsData> localRefsData;
Utils::optional<QVersionNumber> versionNumber;
- std::unordered_map<TextDocument *, CppEditor::SemanticHighlighter> highlighters;
+
+ // The highlighters are owned by their respective documents.
+ std::unordered_map<TextDocument *, CppEditor::SemanticHighlighter *> highlighters;
+
VersionedDataCache<const TextDocument *, AstNode> astCache;
VersionedDataCache<Utils::FilePath, AstNode> externalAstCache;
+ TaskTimer highlightingTimer{"highlighting"};
quint64 nextJobId = 0;
bool isFullyIndexed = false;
bool isTesting = false;
@@ -1453,16 +1563,16 @@ void ClangdClient::followSymbol(TextDocument *document,
return;
}
- const auto astHandler = [this, id = d->followSymbolData->id, range = Range(cursor)]
+ const auto astHandler = [this, id = d->followSymbolData->id]
(const AstNode &ast, const MessageId &) {
qCDebug(clangdLog) << "received ast response for cursor";
if (!d->followSymbolData || d->followSymbolData->id != id)
return;
- d->followSymbolData->cursorNode = getAstNode(ast, range);
+ d->followSymbolData->cursorNode = ast;
if (d->followSymbolData->defLink.hasValidTarget())
d->handleGotoDefinitionResult();
};
- d->getAndHandleAst(document, astHandler, Private::AstCallbackMode::SyncIfPossible);
+ d->getAndHandleAst(document, astHandler, Private::AstCallbackMode::AlwaysAsync, Range(cursor));
}
void ClangdClient::switchDeclDef(TextDocument *document, const QTextCursor &cursor,
@@ -1912,18 +2022,19 @@ void ClangdClient::Private::handleGotoImplementationResult(
: TextDocOrFile(defLinkFilePath);
const Position defLinkPos(followSymbolData->defLink.targetLine - 1,
followSymbolData->defLink.targetColumn);
- const auto astHandler = [this, range = Range(defLinkPos, defLinkPos), id = followSymbolData->id]
+ const auto astHandler = [this, id = followSymbolData->id]
(const AstNode &ast, const MessageId &) {
qCDebug(clangdLog) << "received ast response for def link";
if (!followSymbolData || followSymbolData->id != id)
return;
- followSymbolData->defLinkNode = getAstNode(ast, range);
+ followSymbolData->defLinkNode = ast;
if (followSymbolData->pendingSymbolInfoRequests.isEmpty()
&& followSymbolData->pendingGotoDefRequests.isEmpty()) {
handleDocumentInfoResults();
}
};
- getAndHandleAst(defLinkDocVariant, astHandler, AstCallbackMode::SyncIfPossible);
+ getAndHandleAst(defLinkDocVariant, astHandler, AstCallbackMode::AlwaysAsync,
+ Range(defLinkPos, defLinkPos));
}
void ClangdClient::Private::handleDocumentInfoResults()
@@ -2013,446 +2124,37 @@ void ClangdClient::Private::setHelpItemForTooltip(const MessageId &token, const
q->hoverHandler()->setHelpItem(token, helpItem);
}
-static void collectExtraResults(QFutureInterface<HighlightingResult> &future,
- HighlightingResults &results, const AstNode &ast,
- QTextDocument *doc, const QString &docContent)
+class ExtraHighlightingResultsCollector
{
- if (!ast.isValid())
- return;
-
- static const auto lessThan = [](const HighlightingResult &r1,
- const HighlightingResult &r2) {
- return r1.line < r2.line || (r1.line == r2.line && r1.column < r2.column)
- || (r1.line == r2.line && r1.column == r2.column && r1.length < r2.length);
- };
- const auto insert = [&](const HighlightingResult &result) {
- if (!result.isValid()) // Some nodes don't have a range.
- return;
- const auto it = std::lower_bound(results.begin(), results.end(), result, lessThan);
- if (it == results.end() || *it != result) {
- qCDebug(clangdLogHighlight) << "adding additional highlighting result"
- << result.line << result.column << result.length;
- results.insert(it, result);
- return;
- }
-
- // This is for conversion operators, whose type part is only reported as a type by clangd.
- if ((it->textStyles.mainStyle == C_TYPE
- || it->textStyles.mainStyle == C_PRIMITIVE_TYPE)
- && !result.textStyles.mixinStyles.empty()
- && result.textStyles.mixinStyles.at(0) == C_OPERATOR) {
- it->textStyles.mixinStyles = result.textStyles.mixinStyles;
- }
- };
- const auto setFromRange = [doc](HighlightingResult &result, const Range &range) {
- if (!range.isValid())
- return;
- const Position startPos = range.start();
- const Position endPos = range.end();
- result.line = startPos.line() + 1;
- result.column = startPos.character() + 1;
- result.length = endPos.toPositionInDocument(doc) - startPos.toPositionInDocument(doc);
- };
- static const auto onlyIndexOf = [](const QStringView &view, const QStringView &s,
- int from = 0) {
- const int firstIndex = view.indexOf(s, from);
- if (firstIndex == -1)
- return -1;
- const int nextIndex = view.indexOf(s, firstIndex + 1);
-
- // The second condion deals with the off-by-one error in TemplateSpecialization nodes;
- // see below.
- return nextIndex == -1 || nextIndex == firstIndex + 1 ? firstIndex : -1;
- };
-
- QList<AstNode> nodes = {ast};
- while (!nodes.isEmpty()) {
- if (future.isCanceled())
- return;
- const AstNode node = nodes.takeFirst();
- const QList<AstNode> children = node.children().value_or(QList<AstNode>());
- nodes << children;
-
- if (node.kind().endsWith("Literal")) {
- HighlightingResult result;
- result.useTextSyles = true;
- const bool isStringLike = node.kind().startsWith("String")
- || node.kind().startsWith("Character");
- result.textStyles.mainStyle = isStringLike ? C_STRING : C_NUMBER;
- setFromRange(result, node.range());
- insert(result);
- continue;
- }
- if (node.role() == "type" && node.kind() == "Builtin") {
- HighlightingResult result;
- result.useTextSyles = true;
- result.textStyles.mainStyle = C_PRIMITIVE_TYPE;
- setFromRange(result, node.range());
- insert(result);
- continue;
- }
- if (node.role() == "attribute" && (node.kind() == "Override" || node.kind() == "Final")) {
- HighlightingResult result;
- result.useTextSyles = true;
- result.textStyles.mainStyle = C_KEYWORD;
- setFromRange(result, node.range());
- insert(result);
- continue;
- }
-
- const bool isExpression = node.role() == "expression";
- const bool isDeclaration = node.role() == "declaration";
-
- // Unfortunately, the exact position of a specific token is usually not
- // recorded in the AST, so if we need that, we have to search for it textually.
- // In corner cases, this might get sabotaged by e.g. comments, in which case we give up.
- const auto posForNodeStart = [doc](const AstNode &node) {
- return Utils::Text::positionInText(doc, node.range().start().line() + 1,
- node.range().start().character() + 1);
- };
- const auto posForNodeEnd = [doc](const AstNode &node) {
- return Utils::Text::positionInText(doc, node.range().end().line() + 1,
- node.range().end().character() + 1);
- };
- const int nodeStartPos = posForNodeStart(node);
- const int nodeEndPos = posForNodeEnd(node);
-
- // Match question mark and colon in ternary operators.
- if (isExpression && node.kind() == "ConditionalOperator") {
- if (children.size() != 3)
- continue;
-
- // The question mark is between sub-expressions 1 and 2, the colon is between
- // sub-expressions 2 and 3.
- const int searchStartPosQuestionMark = posForNodeEnd(children.first());
- const int searchEndPosQuestionMark = posForNodeStart(children.at(1));
- QStringView content = QStringView(docContent).mid(searchStartPosQuestionMark,
- searchEndPosQuestionMark - searchStartPosQuestionMark);
- const int questionMarkPos = onlyIndexOf(content, QStringView(QStringLiteral("?")));
- if (questionMarkPos == -1)
- continue;
- const int searchStartPosColon = posForNodeEnd(children.at(1));
- const int searchEndPosColon = posForNodeStart(children.at(2));
- content = QStringView(docContent).mid(searchStartPosColon,
- searchEndPosColon - searchStartPosColon);
- const int colonPos = onlyIndexOf(content, QStringView(QStringLiteral(":")));
- if (colonPos == -1)
- continue;
-
- const int absQuestionMarkPos = searchStartPosQuestionMark + questionMarkPos;
- const int absColonPos = searchStartPosColon + colonPos;
- if (absQuestionMarkPos > absColonPos)
- continue;
-
- HighlightingResult result;
- result.useTextSyles = true;
- result.textStyles.mainStyle = C_PUNCTUATION;
- result.textStyles.mixinStyles.push_back(C_OPERATOR);
- Utils::Text::convertPosition(doc, absQuestionMarkPos, &result.line, &result.column);
- result.length = 1;
- result.kind = CppEditor::SemanticHighlighter::TernaryIf;
- insert(result);
- Utils::Text::convertPosition(doc, absColonPos, &result.line, &result.column);
- result.kind = CppEditor::SemanticHighlighter::TernaryElse;
- insert(result);
- continue;
- }
-
- // The following functions are for matching the "<" and ">" brackets of template
- // declarations, specializations and instantiations.
- const auto insertAngleBracketInfo = [&docContent, doc, &insert](
- int searchStart1, int searchEnd1, int searchStart2, int searchEnd2) {
- const int openingAngleBracketPos = onlyIndexOf(
- QStringView(docContent).mid(searchStart1, searchEnd1 - searchStart1),
- QStringView(QStringLiteral("<")));
- if (openingAngleBracketPos == -1)
- return;
- const int absOpeningAngleBracketPos = searchStart1 + openingAngleBracketPos;
- if (absOpeningAngleBracketPos > searchStart2)
- searchStart2 = absOpeningAngleBracketPos + 1;
- if (searchStart2 >= searchEnd2)
- return;
- const int closingAngleBracketPos = onlyIndexOf(
- QStringView(docContent).mid(searchStart2, searchEnd2 - searchStart2),
- QStringView(QStringLiteral(">")));
- if (closingAngleBracketPos == -1)
- return;
-
- const int absClosingAngleBracketPos = searchStart2 + closingAngleBracketPos;
- if (absOpeningAngleBracketPos > absClosingAngleBracketPos)
- return;
-
- HighlightingResult result;
- result.useTextSyles = true;
- result.textStyles.mainStyle = C_PUNCTUATION;
- Utils::Text::convertPosition(doc, absOpeningAngleBracketPos,
- &result.line, &result.column);
- result.length = 1;
- result.kind = CppEditor::SemanticHighlighter::AngleBracketOpen;
- insert(result);
- Utils::Text::convertPosition(doc, absClosingAngleBracketPos,
- &result.line, &result.column);
- result.kind = CppEditor::SemanticHighlighter::AngleBracketClose;
- insert(result);
- };
-
- if (isDeclaration && (node.kind() == "FunctionTemplate"
- || node.kind() == "ClassTemplate")) {
- // The child nodes are the template parameters and and the function or class.
- // The opening angle bracket is before the first child node, the closing angle
- // bracket is before the function child node and after the last param node.
- const QString classOrFunctionKind = QLatin1String(node.kind() == "FunctionTemplate"
- ? "Function" : "CXXRecord");
- const auto functionOrClassIt = std::find_if(children.begin(), children.end(),
- [&classOrFunctionKind](const AstNode &n) {
- return n.role() == "declaration" && n.kind() == classOrFunctionKind;
- });
- if (functionOrClassIt == children.end() || functionOrClassIt == children.begin())
- continue;
- const int firstTemplateParamStartPos = posForNodeStart(children.first());
- const int lastTemplateParamEndPos = posForNodeEnd(*(functionOrClassIt - 1));
- const int functionOrClassStartPos = posForNodeStart(*functionOrClassIt);
- insertAngleBracketInfo(nodeStartPos, firstTemplateParamStartPos,
- lastTemplateParamEndPos, functionOrClassStartPos);
- continue;
- }
-
- static const auto findTemplateParam = [](const AstNode &n) {
- return n.role() == "declaration" && (n.kind() == "TemplateTypeParm"
- || n.kind() == "NonTypeTemplateParm");
- };
-
- if (isDeclaration && node.kind() == "TypeAliasTemplate") {
- // Children are one node of type TypeAlias and the template parameters.
- // The opening angle bracket is before the first parameter and the closing
- // angle bracket is after the last parameter.
- // The TypeAlias node seems to appear first in the AST, even though lexically
- // is comes after the parameters. We don't rely on the order here.
- // Note that there is a second pair of angle brackets. That one is part of
- // a TemplateSpecialization, which is handled further below.
- const auto firstTemplateParam = std::find_if(children.begin(), children.end(),
- findTemplateParam);
- if (firstTemplateParam == children.end())
- continue;
- const auto lastTemplateParam = std::find_if(children.rbegin(), children.rend(),
- findTemplateParam);
- QTC_ASSERT(lastTemplateParam != children.rend(), continue);
- const auto typeAlias = std::find_if(children.begin(), children.end(),
- [](const AstNode &n) { return n.kind() == "TypeAlias"; });
- if (typeAlias == children.end())
- continue;
-
- const int firstTemplateParamStartPos = posForNodeStart(*firstTemplateParam);
- const int lastTemplateParamEndPos = posForNodeEnd(*lastTemplateParam);
- const int searchEndPos = posForNodeStart(*typeAlias);
- insertAngleBracketInfo(nodeStartPos, firstTemplateParamStartPos,
- lastTemplateParamEndPos, searchEndPos);
- continue;
- }
-
- if (isDeclaration && node.kind() == "ClassTemplateSpecialization") {
- // There is one child of kind TemplateSpecialization. The first pair
- // of angle brackets comes before that.
- if (children.size() == 1) {
- const int childNodePos = posForNodeStart(children.first());
- insertAngleBracketInfo(nodeStartPos, childNodePos, nodeStartPos, childNodePos);
- }
- continue;
- }
-
- if (isDeclaration && node.kind() == "TemplateTemplateParm") {
- // The child nodes are template arguments and template parameters.
- // Arguments seem to appear before parameters in the AST, even though they
- // come after them in the source code. We don't rely on the order here.
- const auto firstTemplateParam = std::find_if(children.begin(), children.end(),
- findTemplateParam);
- if (firstTemplateParam == children.end())
- continue;
- const auto lastTemplateParam = std::find_if(children.rbegin(), children.rend(),
- findTemplateParam);
- QTC_ASSERT(lastTemplateParam != children.rend(), continue);
- const auto templateArg = std::find_if(children.begin(), children.end(),
- [](const AstNode &n) { return n.role() == "template argument"; });
-
- const int firstTemplateParamStartPos = posForNodeStart(*firstTemplateParam);
- const int lastTemplateParamEndPos = posForNodeEnd(*lastTemplateParam);
- const int searchEndPos = templateArg == children.end()
- ? nodeEndPos : posForNodeStart(*templateArg);
- insertAngleBracketInfo(nodeStartPos, firstTemplateParamStartPos,
- lastTemplateParamEndPos, searchEndPos);
- continue;
- }
-
- // {static,dynamic,reinterpret}_cast<>().
- if (isExpression && node.kind().startsWith("CXX") && node.kind().endsWith("Cast")) {
- // First child is type, second child is expression.
- // The opening angle bracket is before the first child, the closing angle bracket
- // is between the two children.
- if (children.size() == 2) {
- insertAngleBracketInfo(nodeStartPos, posForNodeStart(children.first()),
- posForNodeEnd(children.first()),
- posForNodeStart(children.last()));
- }
- continue;
- }
-
- if (node.kind() == "TemplateSpecialization") {
- // First comes the template type, then the template arguments.
- // The opening angle bracket is before the first template argument,
- // the closing angle bracket is after the last template argument.
- // The first child node has no range, so we start searching at the parent node.
- if (children.size() >= 2) {
- int searchStart2 = posForNodeEnd(children.last());
- int searchEnd2 = nodeEndPos;
-
- // There is a weird off-by-one error on the clang side: If there is a
- // nested template instantiation *and* there is no space between
- // the closing angle brackets, then the inner TemplateSpecialization node's range
- // will extend one character too far, covering the outer's closing angle bracket.
- // This is what we are correcting for here.
- // This issue is tracked at https://github.com/clangd/clangd/issues/871.
- if (searchStart2 == searchEnd2)
- --searchStart2;
- insertAngleBracketInfo(nodeStartPos, posForNodeStart(children.at(1)),
- searchStart2, searchEnd2);
- }
- continue;
- }
-
- if (!isExpression && !isDeclaration)
- continue;
-
- // Operators, overloaded ones in particular.
- static const QString operatorPrefix = "operator";
- QString detail = node.detail().value_or(QString());
- const bool isCallToNew = node.kind() == "CXXNew";
- const bool isCallToDelete = node.kind() == "CXXDelete";
- if (!isCallToNew && !isCallToDelete
- && (!detail.startsWith(operatorPrefix) || detail == operatorPrefix)) {
- continue;
- }
-
- if (!isCallToNew && !isCallToDelete)
- detail.remove(0, operatorPrefix.length());
-
- HighlightingResult result;
- result.useTextSyles = true;
- const bool isConversionOp = node.kind() == "CXXConversion";
- const bool isOverloaded = !isConversionOp
- && (isDeclaration || ((!isCallToNew && !isCallToDelete)
- || node.arcanaContains("CXXMethod")));
- result.textStyles.mainStyle = isConversionOp
- ? C_PRIMITIVE_TYPE
- : isCallToNew || isCallToDelete || detail.at(0).isSpace()
- ? C_KEYWORD : C_PUNCTUATION;
- result.textStyles.mixinStyles.push_back(C_OPERATOR);
- if (isOverloaded)
- result.textStyles.mixinStyles.push_back(C_OVERLOADED_OPERATOR);
- if (isDeclaration)
- result.textStyles.mixinStyles.push_back(C_DECLARATION);
-
- const QStringView nodeText = QStringView(docContent)
- .mid(nodeStartPos, nodeEndPos - nodeStartPos);
-
- if (isCallToNew || isCallToDelete) {
- result.line = node.range().start().line() + 1;
- result.column = node.range().start().character() + 1;
- result.length = isCallToNew ? 3 : 6;
- insert(result);
- if (node.arcanaContains("array")) {
- const int openingBracketOffset = nodeText.indexOf('[');
- if (openingBracketOffset == -1)
- continue;
- const int closingBracketOffset = nodeText.lastIndexOf(']');
- if (closingBracketOffset == -1 || closingBracketOffset < openingBracketOffset)
- continue;
-
- result.textStyles.mainStyle = C_PUNCTUATION;
- result.length = 1;
- Utils::Text::convertPosition(doc,
- nodeStartPos + openingBracketOffset,
- &result.line, &result.column);
- insert(result);
- Utils::Text::convertPosition(doc,
- nodeStartPos + closingBracketOffset,
- &result.line, &result.column);
- insert(result);
- }
- continue;
- }
-
- if (isExpression && (detail == QLatin1String("()") || detail == QLatin1String("[]"))) {
- result.line = node.range().start().line() + 1;
- result.column = node.range().start().character() + 1;
- result.length = 1;
- insert(result);
- result.line = node.range().end().line() + 1;
- result.column = node.range().end().character();
- insert(result);
- continue;
- }
-
- const int opStringLen = detail.at(0).isSpace() ? detail.length() - 1 : detail.length();
-
- // The simple case: Call to operator+, +=, * etc.
- if (nodeEndPos - nodeStartPos == opStringLen) {
- setFromRange(result, node.range());
- insert(result);
- continue;
- }
-
- const int prefixOffset = nodeText.indexOf(operatorPrefix);
- if (prefixOffset == -1)
- continue;
-
- const bool isArray = detail == "[]";
- const bool isCall = detail == "()";
- const bool isArrayNew = detail == " new[]";
- const bool isArrayDelete = detail == " delete[]";
- const QStringView searchTerm = isArray || isCall
- ? QStringView(detail).chopped(1) : isArrayNew || isArrayDelete
- ? QStringView(detail).chopped(2) : detail;
- const int opStringOffset = nodeText.indexOf(searchTerm, prefixOffset
- + operatorPrefix.length());
- if (opStringOffset == -1 || nodeText.indexOf(operatorPrefix, opStringOffset) != -1)
- continue;
-
- const int opStringOffsetInDoc = nodeStartPos + opStringOffset
- + detail.length() - opStringLen;
- Utils::Text::convertPosition(doc, opStringOffsetInDoc, &result.line, &result.column);
- result.length = opStringLen;
- if (isArray || isCall)
- result.length = 1;
- else if (isArrayNew || isArrayDelete)
- result.length -= 2;
- if (!isArray && !isCall)
- insert(result);
- if (!isArray && !isCall && !isArrayNew && !isArrayDelete)
- continue;
+public:
+ ExtraHighlightingResultsCollector(QFutureInterface<HighlightingResult> &future,
+ HighlightingResults &results, const AstNode &ast,
+ const QTextDocument *doc, const QString &docContent);
- result.textStyles.mainStyle = C_PUNCTUATION;
- result.length = 1;
- const int openingParenOffset = nodeText.indexOf(
- isCall ? '(' : '[', prefixOffset + operatorPrefix.length());
- if (openingParenOffset == -1)
- continue;
- const int closingParenOffset = nodeText.indexOf(isCall ? ')' : ']', openingParenOffset + 1);
- if (closingParenOffset == -1 || closingParenOffset < openingParenOffset)
- continue;
- Utils::Text::convertPosition(doc, nodeStartPos + openingParenOffset,
- &result.line, &result.column);
- insert(result);
- Utils::Text::convertPosition(doc, nodeStartPos + closingParenOffset,
- &result.line, &result.column);
- insert(result);
- }
-}
+ void collect();
+private:
+ static bool lessThan(const HighlightingResult &r1, const HighlightingResult &r2);
+ static int onlyIndexOf(const QStringView &text, const QStringView &subString, int from = 0);
+ int posForNodeStart(const AstNode &node) const;
+ int posForNodeEnd(const AstNode &node) const;
+ void insertResult(const HighlightingResult &result);
+ void insertAngleBracketInfo(int searchStart1, int searchEnd1, int searchStart2, int searchEnd2);
+ void setResultPosFromRange(HighlightingResult &result, const Range &range);
+ void collectFromNode(const AstNode &node);
+ void visitNode(const AstNode&node);
+
+ QFutureInterface<HighlightingResult> &m_future;
+ HighlightingResults &m_results;
+ const AstNode &m_ast;
+ const QTextDocument * const m_doc;
+ const QString &m_docContent;
+};
// clangd reports also the #ifs, #elses and #endifs around the disabled code as disabled,
// and not even in a consistent manner. We don't want this, so we have to clean up here.
// But note that we require this behavior, as otherwise we would not be able to grey out
// e.g. empty lines after an #fdef, due to the lack of symbols.
-static QList<BlockRange> cleanupDisabledCode(HighlightingResults &results, QTextDocument *doc,
+static QList<BlockRange> cleanupDisabledCode(HighlightingResults &results, const QTextDocument *doc,
const QString &docContent)
{
QList<BlockRange> ifdefedOutRanges;
@@ -2513,12 +2215,13 @@ static void semanticHighlighter(QFutureInterface<HighlightingResult> &future,
const QPointer<TextEditorWidget> &widget,
int docRevision, const QVersionNumber &clangdVersion)
{
+ ThreadedSubtaskTimer t("highlighting");
if (future.isCanceled()) {
future.reportFinished();
return;
}
- QTextDocument doc(docContents);
+ const QTextDocument doc(docContents);
const auto isOutputParameter = [&ast](const ExpandedSemanticToken &token) {
if (token.modifiers.contains("usedAsMutableReference"))
return true;
@@ -2545,8 +2248,8 @@ static void semanticHighlighter(QFutureInterface<HighlightingResult> &future,
return false;
};
- const auto toResult = [&ast, &isOutputParameter, &clangdVersion]
- (const ExpandedSemanticToken &token) {
+ const std::function<HighlightingResult(const ExpandedSemanticToken &)> toResult
+ = [&ast, &isOutputParameter, &clangdVersion](const ExpandedSemanticToken &token) {
TextStyles styles;
if (token.type == "variable") {
if (token.modifiers.contains("functionScope")) {
@@ -2629,13 +2332,13 @@ static void semanticHighlighter(QFutureInterface<HighlightingResult> &future,
return HighlightingResult(token.line, token.column, token.length, styles);
};
- HighlightingResults results = Utils::transform(tokens, toResult);
+ auto results = QtConcurrent::blockingMapped<HighlightingResults>(tokens, toResult);
const QList<BlockRange> ifdefedOutBlocks = cleanupDisabledCode(results, &doc, docContents);
QMetaObject::invokeMethod(widget, [widget, ifdefedOutBlocks, docRevision] {
if (widget && widget->textDocument()->document()->revision() == docRevision)
widget->setIfdefedOutBlocks(ifdefedOutBlocks);
}, Qt::QueuedConnection);
- collectExtraResults(future, results, ast, &doc, docContents);
+ ExtraHighlightingResultsCollector(future, results, ast, &doc, docContents).collect();
if (!future.isCanceled()) {
qCDebug(clangdLog) << "reporting" << results.size() << "highlighting results";
future.reportResults(QVector<HighlightingResult>(results.cbegin(),
@@ -2659,12 +2362,14 @@ static void semanticHighlighter(QFutureInterface<HighlightingResult> &future,
void ClangdClient::Private::handleSemanticTokens(TextDocument *doc,
const QList<ExpandedSemanticToken> &tokens)
{
+ SubtaskTimer t(highlightingTimer);
qCDebug(clangdLog()) << "handling LSP tokens" << doc->filePath() << tokens.size();
for (const ExpandedSemanticToken &t : tokens)
qCDebug(clangdLogHighlight()) << '\t' << t.line << t.column << t.length << t.type
<< t.modifiers;
const auto astHandler = [this, tokens, doc](const AstNode &ast, const MessageId &) {
+ FinalizingSubtaskTimer t(highlightingTimer);
if (!q->documentOpen(doc))
return;
if (clangdLogAst().isDebugEnabled())
@@ -2692,12 +2397,13 @@ void ClangdClient::Private::handleSemanticTokens(TextDocument *doc,
auto it = highlighters.find(doc);
if (it == highlighters.end()) {
- it = highlighters.emplace(doc, doc).first;
+ it = highlighters.insert(std::make_pair(doc, new CppEditor::SemanticHighlighter(doc)))
+ .first;
} else {
- it->second.updateFormatMapFromFontSettings();
+ it->second->updateFormatMapFromFontSettings();
}
- it->second.setHighlightingRunner(runner);
- it->second.run();
+ it->second->setHighlightingRunner(runner);
+ it->second->run();
};
getAndHandleAst(doc, astHandler, AstCallbackMode::SyncIfPossible);
}
@@ -3014,26 +2720,30 @@ void ClangdCompletionItem::apply(TextDocumentManipulatorInterface &manipulator,
MessageId ClangdClient::Private::getAndHandleAst(const TextDocOrFile &doc,
const AstHandler &astHandler,
- AstCallbackMode callbackMode)
+ AstCallbackMode callbackMode, const Range &range)
{
const auto textDocPtr = Utils::get_if<const TextDocument *>(&doc);
const TextDocument * const textDoc = textDocPtr ? *textDocPtr : nullptr;
const Utils::FilePath filePath = textDoc ? textDoc->filePath()
: Utils::get<Utils::FilePath>(doc);
- // If the document's AST is in the cache and is up to date, call the handler.
- if (const auto ast = textDoc ? astCache.get(textDoc) : externalAstCache.get(filePath)) {
- qCDebug(clangdLog) << "using AST from cache";
- switch (callbackMode) {
- case AstCallbackMode::SyncIfPossible:
- astHandler(*ast, {});
- break;
- case AstCallbackMode::AlwaysAsync:
- QMetaObject::invokeMethod(q, [ast, astHandler] { astHandler(*ast, {}); },
+ // If the entire AST is requested and the document's AST is in the cache and it is up to date,
+ // call the handler.
+ const bool fullAstRequested = !range.isValid();
+ if (fullAstRequested) {
+ if (const auto ast = textDoc ? astCache.get(textDoc) : externalAstCache.get(filePath)) {
+ qCDebug(clangdLog) << "using AST from cache";
+ switch (callbackMode) {
+ case AstCallbackMode::SyncIfPossible:
+ astHandler(*ast, {});
+ break;
+ case AstCallbackMode::AlwaysAsync:
+ QMetaObject::invokeMethod(q, [ast, astHandler] { astHandler(*ast, {}); },
Qt::QueuedConnection);
- break;
+ break;
+ }
+ return {};
}
- return {};
}
// Otherwise retrieve the AST from clangd.
@@ -3041,7 +2751,6 @@ MessageId ClangdClient::Private::getAndHandleAst(const TextDocOrFile &doc,
class AstParams : public JsonObject
{
public:
- AstParams() {}
AstParams(const TextDocumentIdentifier &document, const Range &range = {})
{
setTextDocument(document);
@@ -3071,19 +2780,22 @@ MessageId ClangdClient::Private::getAndHandleAst(const TextDocOrFile &doc,
explicit AstRequest(const AstParams &params) : Request("textDocument/ast", params) {}
};
- AstRequest request(AstParams(TextDocumentIdentifier(DocumentUri::fromFilePath(filePath))));
+ AstRequest request(AstParams(TextDocumentIdentifier(DocumentUri::fromFilePath(filePath)),
+ range));
request.setResponseCallback([this, filePath, guardedTextDoc = QPointer(textDoc), astHandler,
- docRev = textDoc ? getRevision(textDoc) : -1,
+ fullAstRequested, docRev = textDoc ? getRevision(textDoc) : -1,
fileRev = getRevision(filePath), reqId = request.id()]
(AstRequest::Response response) {
qCDebug(clangdLog) << "retrieved AST from clangd";
const auto result = response.result();
const AstNode ast = result ? *result : AstNode();
- if (guardedTextDoc) {
- if (docRev == getRevision(guardedTextDoc))
- astCache.insert(guardedTextDoc, ast);
- } else if (fileRev == getRevision(filePath) && !q->documentForFilePath(filePath)) {
- externalAstCache.insert(filePath, ast);
+ if (fullAstRequested) {
+ if (guardedTextDoc) {
+ if (docRev == getRevision(guardedTextDoc))
+ astCache.insert(guardedTextDoc, ast);
+ } else if (fileRev == getRevision(filePath) && !q->documentForFilePath(filePath)) {
+ externalAstCache.insert(filePath, ast);
+ }
}
astHandler(ast, reqId);
});
@@ -3092,6 +2804,465 @@ MessageId ClangdClient::Private::getAndHandleAst(const TextDocOrFile &doc,
return request.id();
}
+ExtraHighlightingResultsCollector::ExtraHighlightingResultsCollector(
+ QFutureInterface<HighlightingResult> &future, HighlightingResults &results,
+ const AstNode &ast, const QTextDocument *doc, const QString &docContent)
+ : m_future(future), m_results(results), m_ast(ast), m_doc(doc), m_docContent(docContent)
+{
+
+}
+
+void ExtraHighlightingResultsCollector::collect()
+{
+ if (!m_ast.isValid())
+ return;
+ visitNode(m_ast);
+}
+
+bool ExtraHighlightingResultsCollector::lessThan(const HighlightingResult &r1,
+ const HighlightingResult &r2)
+{
+ return r1.line < r2.line || (r1.line == r2.line && r1.column < r2.column)
+ || (r1.line == r2.line && r1.column == r2.column && r1.length < r2.length);
+}
+
+int ExtraHighlightingResultsCollector::onlyIndexOf(const QStringView &text,
+ const QStringView &subString, int from)
+{
+ const int firstIndex = text.indexOf(subString, from);
+ if (firstIndex == -1)
+ return -1;
+ const int nextIndex = text.indexOf(subString, firstIndex + 1);
+
+ // The second condion deals with the off-by-one error in TemplateSpecialization nodes;
+ // see collectFromNode().
+ return nextIndex == -1 || nextIndex == firstIndex + 1 ? firstIndex : -1;
+}
+
+// Unfortunately, the exact position of a specific token is usually not
+// recorded in the AST, so if we need that, we have to search for it textually.
+// In corner cases, this might get sabotaged by e.g. comments, in which case we give up.
+int ExtraHighlightingResultsCollector::posForNodeStart(const AstNode &node) const
+{
+ return Utils::Text::positionInText(m_doc, node.range().start().line() + 1,
+ node.range().start().character() + 1);
+}
+
+int ExtraHighlightingResultsCollector::posForNodeEnd(const AstNode &node) const
+{
+ return Utils::Text::positionInText(m_doc, node.range().end().line() + 1,
+ node.range().end().character() + 1);
+}
+
+void ExtraHighlightingResultsCollector::insertResult(const HighlightingResult &result)
+{
+ if (!result.isValid()) // Some nodes don't have a range.
+ return;
+ const auto it = std::lower_bound(m_results.begin(), m_results.end(), result, lessThan);
+ if (it == m_results.end() || *it != result) {
+ qCDebug(clangdLogHighlight) << "adding additional highlighting result"
+ << result.line << result.column << result.length;
+ m_results.insert(it, result);
+ return;
+ }
+
+ // This is for conversion operators, whose type part is only reported as a type by clangd.
+ if ((it->textStyles.mainStyle == C_TYPE
+ || it->textStyles.mainStyle == C_PRIMITIVE_TYPE)
+ && !result.textStyles.mixinStyles.empty()
+ && result.textStyles.mixinStyles.at(0) == C_OPERATOR) {
+ it->textStyles.mixinStyles = result.textStyles.mixinStyles;
+ }
+}
+
+// For matching the "<" and ">" brackets of template declarations, specializations
+// and instantiations.
+void ExtraHighlightingResultsCollector::insertAngleBracketInfo(int searchStart1, int searchEnd1,
+ int searchStart2, int searchEnd2)
+{
+ const int openingAngleBracketPos = onlyIndexOf(
+ QStringView(m_docContent).mid(searchStart1, searchEnd1 - searchStart1),
+ QStringView(QStringLiteral("<")));
+ if (openingAngleBracketPos == -1)
+ return;
+ const int absOpeningAngleBracketPos = searchStart1 + openingAngleBracketPos;
+ if (absOpeningAngleBracketPos > searchStart2)
+ searchStart2 = absOpeningAngleBracketPos + 1;
+ if (searchStart2 >= searchEnd2)
+ return;
+ const int closingAngleBracketPos = onlyIndexOf(
+ QStringView(m_docContent).mid(searchStart2, searchEnd2 - searchStart2),
+ QStringView(QStringLiteral(">")));
+ if (closingAngleBracketPos == -1)
+ return;
+
+ const int absClosingAngleBracketPos = searchStart2 + closingAngleBracketPos;
+ if (absOpeningAngleBracketPos > absClosingAngleBracketPos)
+ return;
+
+ HighlightingResult result;
+ result.useTextSyles = true;
+ result.textStyles.mainStyle = C_PUNCTUATION;
+ Utils::Text::convertPosition(m_doc, absOpeningAngleBracketPos, &result.line, &result.column);
+ result.length = 1;
+ result.kind = CppEditor::SemanticHighlighter::AngleBracketOpen;
+ insertResult(result);
+ Utils::Text::convertPosition(m_doc, absClosingAngleBracketPos, &result.line, &result.column);
+ result.kind = CppEditor::SemanticHighlighter::AngleBracketClose;
+ insertResult(result);
+}
+
+void ExtraHighlightingResultsCollector::setResultPosFromRange(HighlightingResult &result,
+ const Range &range)
+{
+ if (!range.isValid())
+ return;
+ const Position startPos = range.start();
+ const Position endPos = range.end();
+ result.line = startPos.line() + 1;
+ result.column = startPos.character() + 1;
+ result.length = endPos.toPositionInDocument(m_doc) - startPos.toPositionInDocument(m_doc);
+}
+
+void ExtraHighlightingResultsCollector::collectFromNode(const AstNode &node)
+{
+ if (node.kind().endsWith("Literal")) {
+ HighlightingResult result;
+ result.useTextSyles = true;
+ const bool isStringLike = node.kind().startsWith("String")
+ || node.kind().startsWith("Character");
+ result.textStyles.mainStyle = isStringLike ? C_STRING : C_NUMBER;
+ setResultPosFromRange(result, node.range());
+ insertResult(result);
+ return;
+ }
+ if (node.role() == "type" && node.kind() == "Builtin") {
+ HighlightingResult result;
+ result.useTextSyles = true;
+ result.textStyles.mainStyle = C_PRIMITIVE_TYPE;
+ setResultPosFromRange(result, node.range());
+ insertResult(result);
+ return;
+ }
+ if (node.role() == "attribute" && (node.kind() == "Override" || node.kind() == "Final")) {
+ HighlightingResult result;
+ result.useTextSyles = true;
+ result.textStyles.mainStyle = C_KEYWORD;
+ setResultPosFromRange(result, node.range());
+ insertResult(result);
+ return;
+ }
+
+ const bool isExpression = node.role() == "expression";
+ const bool isDeclaration = node.role() == "declaration";
+
+ const int nodeStartPos = posForNodeStart(node);
+ const int nodeEndPos = posForNodeEnd(node);
+ const QList<AstNode> children = node.children().value_or(QList<AstNode>());
+
+ // Match question mark and colon in ternary operators.
+ if (isExpression && node.kind() == "ConditionalOperator") {
+ if (children.size() != 3)
+ return;
+
+ // The question mark is between sub-expressions 1 and 2, the colon is between
+ // sub-expressions 2 and 3.
+ const int searchStartPosQuestionMark = posForNodeEnd(children.first());
+ const int searchEndPosQuestionMark = posForNodeStart(children.at(1));
+ QStringView content = QStringView(m_docContent).mid(
+ searchStartPosQuestionMark,
+ searchEndPosQuestionMark - searchStartPosQuestionMark);
+ const int questionMarkPos = onlyIndexOf(content, QStringView(QStringLiteral("?")));
+ if (questionMarkPos == -1)
+ return;
+ const int searchStartPosColon = posForNodeEnd(children.at(1));
+ const int searchEndPosColon = posForNodeStart(children.at(2));
+ content = QStringView(m_docContent).mid(searchStartPosColon,
+ searchEndPosColon - searchStartPosColon);
+ const int colonPos = onlyIndexOf(content, QStringView(QStringLiteral(":")));
+ if (colonPos == -1)
+ return;
+
+ const int absQuestionMarkPos = searchStartPosQuestionMark + questionMarkPos;
+ const int absColonPos = searchStartPosColon + colonPos;
+ if (absQuestionMarkPos > absColonPos)
+ return;
+
+ HighlightingResult result;
+ result.useTextSyles = true;
+ result.textStyles.mainStyle = C_PUNCTUATION;
+ result.textStyles.mixinStyles.push_back(C_OPERATOR);
+ Utils::Text::convertPosition(m_doc, absQuestionMarkPos, &result.line, &result.column);
+ result.length = 1;
+ result.kind = CppEditor::SemanticHighlighter::TernaryIf;
+ insertResult(result);
+ Utils::Text::convertPosition(m_doc, absColonPos, &result.line, &result.column);
+ result.kind = CppEditor::SemanticHighlighter::TernaryElse;
+ insertResult(result);
+ return;
+ }
+
+ if (isDeclaration && (node.kind() == "FunctionTemplate"
+ || node.kind() == "ClassTemplate")) {
+ // The child nodes are the template parameters and and the function or class.
+ // The opening angle bracket is before the first child node, the closing angle
+ // bracket is before the function child node and after the last param node.
+ const QString classOrFunctionKind = QLatin1String(node.kind() == "FunctionTemplate"
+ ? "Function" : "CXXRecord");
+ const auto functionOrClassIt = std::find_if(children.begin(), children.end(),
+ [&classOrFunctionKind](const AstNode &n) {
+ return n.role() == "declaration" && n.kind() == classOrFunctionKind;
+ });
+ if (functionOrClassIt == children.end() || functionOrClassIt == children.begin())
+ return;
+ const int firstTemplateParamStartPos = posForNodeStart(children.first());
+ const int lastTemplateParamEndPos = posForNodeEnd(*(functionOrClassIt - 1));
+ const int functionOrClassStartPos = posForNodeStart(*functionOrClassIt);
+ insertAngleBracketInfo(nodeStartPos, firstTemplateParamStartPos,
+ lastTemplateParamEndPos, functionOrClassStartPos);
+ return;
+ }
+
+ const auto isTemplateParamDecl = [](const AstNode &node) {
+ return node.isTemplateParameterDeclaration();
+ };
+ if (isDeclaration && node.kind() == "TypeAliasTemplate") {
+ // Children are one node of type TypeAlias and the template parameters.
+ // The opening angle bracket is before the first parameter and the closing
+ // angle bracket is after the last parameter.
+ // The TypeAlias node seems to appear first in the AST, even though lexically
+ // is comes after the parameters. We don't rely on the order here.
+ // Note that there is a second pair of angle brackets. That one is part of
+ // a TemplateSpecialization, which is handled further below.
+ const auto firstTemplateParam = std::find_if(children.begin(), children.end(),
+ isTemplateParamDecl);
+ if (firstTemplateParam == children.end())
+ return;
+ const auto lastTemplateParam = std::find_if(children.rbegin(), children.rend(),
+ isTemplateParamDecl);
+ QTC_ASSERT(lastTemplateParam != children.rend(), return);
+ const auto typeAlias = std::find_if(children.begin(), children.end(),
+ [](const AstNode &n) { return n.kind() == "TypeAlias"; });
+ if (typeAlias == children.end())
+ return;
+
+ const int firstTemplateParamStartPos = posForNodeStart(*firstTemplateParam);
+ const int lastTemplateParamEndPos = posForNodeEnd(*lastTemplateParam);
+ const int searchEndPos = posForNodeStart(*typeAlias);
+ insertAngleBracketInfo(nodeStartPos, firstTemplateParamStartPos,
+ lastTemplateParamEndPos, searchEndPos);
+ return;
+ }
+
+ if (isDeclaration && node.kind() == "ClassTemplateSpecialization") {
+ // There is one child of kind TemplateSpecialization. The first pair
+ // of angle brackets comes before that.
+ if (children.size() == 1) {
+ const int childNodePos = posForNodeStart(children.first());
+ insertAngleBracketInfo(nodeStartPos, childNodePos, nodeStartPos, childNodePos);
+ }
+ return;
+ }
+
+ if (isDeclaration && node.kind() == "TemplateTemplateParm") {
+ // The child nodes are template arguments and template parameters.
+ // Arguments seem to appear before parameters in the AST, even though they
+ // come after them in the source code. We don't rely on the order here.
+ const auto firstTemplateParam = std::find_if(children.begin(), children.end(),
+ isTemplateParamDecl);
+ if (firstTemplateParam == children.end())
+ return;
+ const auto lastTemplateParam = std::find_if(children.rbegin(), children.rend(),
+ isTemplateParamDecl);
+ QTC_ASSERT(lastTemplateParam != children.rend(), return);
+ const auto templateArg = std::find_if(children.begin(), children.end(),
+ [](const AstNode &n) { return n.role() == "template argument"; });
+
+ const int firstTemplateParamStartPos = posForNodeStart(*firstTemplateParam);
+ const int lastTemplateParamEndPos = posForNodeEnd(*lastTemplateParam);
+ const int searchEndPos = templateArg == children.end()
+ ? nodeEndPos : posForNodeStart(*templateArg);
+ insertAngleBracketInfo(nodeStartPos, firstTemplateParamStartPos,
+ lastTemplateParamEndPos, searchEndPos);
+ return;
+ }
+
+ // {static,dynamic,reinterpret}_cast<>().
+ if (isExpression && node.kind().startsWith("CXX") && node.kind().endsWith("Cast")) {
+ // First child is type, second child is expression.
+ // The opening angle bracket is before the first child, the closing angle bracket
+ // is between the two children.
+ if (children.size() == 2) {
+ insertAngleBracketInfo(nodeStartPos, posForNodeStart(children.first()),
+ posForNodeEnd(children.first()),
+ posForNodeStart(children.last()));
+ }
+ return;
+ }
+
+ if (node.kind() == "TemplateSpecialization") {
+ // First comes the template type, then the template arguments.
+ // The opening angle bracket is before the first template argument,
+ // the closing angle bracket is after the last template argument.
+ // The first child node has no range, so we start searching at the parent node.
+ if (children.size() >= 2) {
+ int searchStart2 = posForNodeEnd(children.last());
+ int searchEnd2 = nodeEndPos;
+
+ // There is a weird off-by-one error on the clang side: If there is a
+ // nested template instantiation *and* there is no space between
+ // the closing angle brackets, then the inner TemplateSpecialization node's range
+ // will extend one character too far, covering the outer's closing angle bracket.
+ // This is what we are correcting for here.
+ // This issue is tracked at https://github.com/clangd/clangd/issues/871.
+ if (searchStart2 == searchEnd2)
+ --searchStart2;
+ insertAngleBracketInfo(nodeStartPos, posForNodeStart(children.at(1)),
+ searchStart2, searchEnd2);
+ }
+ return;
+ }
+
+ if (!isExpression && !isDeclaration)
+ return;
+
+ // Operators, overloaded ones in particular.
+ static const QString operatorPrefix = "operator";
+ QString detail = node.detail().value_or(QString());
+ const bool isCallToNew = node.kind() == "CXXNew";
+ const bool isCallToDelete = node.kind() == "CXXDelete";
+ if (!isCallToNew && !isCallToDelete
+ && (!detail.startsWith(operatorPrefix) || detail == operatorPrefix)) {
+ return;
+ }
+
+ if (!isCallToNew && !isCallToDelete)
+ detail.remove(0, operatorPrefix.length());
+
+ HighlightingResult result;
+ result.useTextSyles = true;
+ const bool isConversionOp = node.kind() == "CXXConversion";
+ const bool isOverloaded = !isConversionOp
+ && (isDeclaration || ((!isCallToNew && !isCallToDelete)
+ || node.arcanaContains("CXXMethod")));
+ result.textStyles.mainStyle = isConversionOp
+ ? C_PRIMITIVE_TYPE
+ : isCallToNew || isCallToDelete || detail.at(0).isSpace()
+ ? C_KEYWORD : C_PUNCTUATION;
+ result.textStyles.mixinStyles.push_back(C_OPERATOR);
+ if (isOverloaded)
+ result.textStyles.mixinStyles.push_back(C_OVERLOADED_OPERATOR);
+ if (isDeclaration)
+ result.textStyles.mixinStyles.push_back(C_DECLARATION);
+
+ const QStringView nodeText = QStringView(m_docContent)
+ .mid(nodeStartPos, nodeEndPos - nodeStartPos);
+
+ if (isCallToNew || isCallToDelete) {
+ result.line = node.range().start().line() + 1;
+ result.column = node.range().start().character() + 1;
+ result.length = isCallToNew ? 3 : 6;
+ insertResult(result);
+ if (node.arcanaContains("array")) {
+ const int openingBracketOffset = nodeText.indexOf('[');
+ if (openingBracketOffset == -1)
+ return;
+ const int closingBracketOffset = nodeText.lastIndexOf(']');
+ if (closingBracketOffset == -1 || closingBracketOffset < openingBracketOffset)
+ return;
+
+ result.textStyles.mainStyle = C_PUNCTUATION;
+ result.length = 1;
+ Utils::Text::convertPosition(m_doc,
+ nodeStartPos + openingBracketOffset,
+ &result.line, &result.column);
+ insertResult(result);
+ Utils::Text::convertPosition(m_doc,
+ nodeStartPos + closingBracketOffset,
+ &result.line, &result.column);
+ insertResult(result);
+ }
+ return;
+ }
+
+ if (isExpression && (detail == QLatin1String("()") || detail == QLatin1String("[]"))) {
+ result.line = node.range().start().line() + 1;
+ result.column = node.range().start().character() + 1;
+ result.length = 1;
+ insertResult(result);
+ result.line = node.range().end().line() + 1;
+ result.column = node.range().end().character();
+ insertResult(result);
+ return;
+ }
+
+ const int opStringLen = detail.at(0).isSpace() ? detail.length() - 1 : detail.length();
+
+ // The simple case: Call to operator+, +=, * etc.
+ if (nodeEndPos - nodeStartPos == opStringLen) {
+ setResultPosFromRange(result, node.range());
+ insertResult(result);
+ return;
+ }
+
+ const int prefixOffset = nodeText.indexOf(operatorPrefix);
+ if (prefixOffset == -1)
+ return;
+
+ const bool isArray = detail == "[]";
+ const bool isCall = detail == "()";
+ const bool isArrayNew = detail == " new[]";
+ const bool isArrayDelete = detail == " delete[]";
+ const QStringView searchTerm = isArray || isCall
+ ? QStringView(detail).chopped(1) : isArrayNew || isArrayDelete
+ ? QStringView(detail).chopped(2) : detail;
+ const int opStringOffset = nodeText.indexOf(searchTerm, prefixOffset
+ + operatorPrefix.length());
+ if (opStringOffset == -1 || nodeText.indexOf(operatorPrefix, opStringOffset) != -1)
+ return;
+
+ const int opStringOffsetInDoc = nodeStartPos + opStringOffset
+ + detail.length() - opStringLen;
+ Utils::Text::convertPosition(m_doc, opStringOffsetInDoc, &result.line, &result.column);
+ result.length = opStringLen;
+ if (isArray || isCall)
+ result.length = 1;
+ else if (isArrayNew || isArrayDelete)
+ result.length -= 2;
+ if (!isArray && !isCall)
+ insertResult(result);
+ if (!isArray && !isCall && !isArrayNew && !isArrayDelete)
+ return;
+
+ result.textStyles.mainStyle = C_PUNCTUATION;
+ result.length = 1;
+ const int openingParenOffset = nodeText.indexOf(
+ isCall ? '(' : '[', prefixOffset + operatorPrefix.length());
+ if (openingParenOffset == -1)
+ return;
+ const int closingParenOffset = nodeText.indexOf(isCall ? ')' : ']', openingParenOffset + 1);
+ if (closingParenOffset == -1 || closingParenOffset < openingParenOffset)
+ return;
+ Utils::Text::convertPosition(m_doc, nodeStartPos + openingParenOffset,
+ &result.line, &result.column);
+ insertResult(result);
+ Utils::Text::convertPosition(m_doc, nodeStartPos + closingParenOffset,
+ &result.line, &result.column);
+ insertResult(result);
+}
+
+void ExtraHighlightingResultsCollector::visitNode(const AstNode &node)
+{
+ if (m_future.isCanceled())
+ return;
+ collectFromNode(node);
+ const auto children = node.children();
+ if (!children)
+ return;
+ for (const AstNode &childNode : *children)
+ visitNode(childNode);
+}
+
} // namespace Internal
} // namespace ClangCodeModel
diff --git a/src/plugins/clangformat/CMakeLists.txt b/src/plugins/clangformat/CMakeLists.txt
index eb6e0687e6..5db5f8111a 100644
--- a/src/plugins/clangformat/CMakeLists.txt
+++ b/src/plugins/clangformat/CMakeLists.txt
@@ -8,6 +8,7 @@ add_qtc_plugin(ClangFormat
clangformatchecks.ui
clangformatconfigwidget.cpp clangformatconfigwidget.h clangformatconfigwidget.ui
clangformatconstants.h
+ clangformatfile.cpp clangformatfile.h
clangformatindenter.cpp clangformatindenter.h
clangformatplugin.cpp clangformatplugin.h
clangformatsettings.cpp clangformatsettings.h
diff --git a/src/plugins/clangformat/clangformat.pro b/src/plugins/clangformat/clangformat.pro
index 4d751db2ba..da7645613c 100644
--- a/src/plugins/clangformat/clangformat.pro
+++ b/src/plugins/clangformat/clangformat.pro
@@ -15,6 +15,7 @@ unix:!macos:QMAKE_LFLAGS += -Wl,--exclude-libs,ALL
SOURCES += \
clangformatconfigwidget.cpp \
+ clangformatfile.cpp \
clangformatindenter.cpp \
clangformatplugin.cpp \
clangformatsettings.cpp \
@@ -22,6 +23,7 @@ SOURCES += \
HEADERS += \
clangformatconfigwidget.h \
+ clangformatfile.h \
clangformatindenter.h \
clangformatplugin.h \
clangformatsettings.h \
diff --git a/src/plugins/clangformat/clangformat.qbs b/src/plugins/clangformat/clangformat.qbs
index ef8ae44781..fcd77b7c1b 100644
--- a/src/plugins/clangformat/clangformat.qbs
+++ b/src/plugins/clangformat/clangformat.qbs
@@ -33,6 +33,8 @@ QtcPlugin {
"clangformatconfigwidget.h",
"clangformatconfigwidget.ui",
"clangformatconstants.h",
+ "clangformatfile.cpp",
+ "clangformatfile.h",
"clangformatindenter.cpp",
"clangformatindenter.h",
"clangformatplugin.cpp",
diff --git a/src/plugins/clangformat/clangformatconfigwidget.cpp b/src/plugins/clangformat/clangformatconfigwidget.cpp
index f0cc5d9700..0dc6d4c54f 100644
--- a/src/plugins/clangformat/clangformatconfigwidget.cpp
+++ b/src/plugins/clangformat/clangformatconfigwidget.cpp
@@ -27,6 +27,7 @@
#include "clangformatconstants.h"
#include "clangformatindenter.h"
+#include "clangformatfile.h"
#include "clangformatsettings.h"
#include "clangformatutils.h"
#include "ui_clangformatchecks.h"
@@ -121,6 +122,12 @@ ClangFormatConfigWidget::ClangFormatConfigWidget(ProjectExplorer::Project *proje
{
m_ui->setupUi(this);
+ Utils::FilePath filePath = Core::ICore::userResourcePath();
+ if (m_project)
+ filePath = filePath / "clang-format/" / currentProjectUniqueId();
+ filePath = filePath / QLatin1String(Constants::SETTINGS_FILE_NAME);
+ m_config = std::make_unique<ClangFormatFile>(filePath);
+
initChecksAndPreview();
if (m_project) {
@@ -147,6 +154,8 @@ ClangFormatConfigWidget::ClangFormatConfigWidget(ProjectExplorer::Project *proje
connectChecks();
}
+ClangFormatConfigWidget::~ClangFormatConfigWidget() = default;
+
void ClangFormatConfigWidget::initChecksAndPreview()
{
m_checksScrollArea = new QScrollArea();
@@ -191,7 +200,7 @@ void ClangFormatConfigWidget::connectChecks()
continue;
}
- auto button = qobject_cast<QPushButton *>(child);
+ const auto button = qobject_cast<QPushButton *>(child);
if (button != nullptr)
connect(button, &QPushButton::clicked, this, &ClangFormatConfigWidget::onTableChanged);
}
@@ -202,15 +211,7 @@ void ClangFormatConfigWidget::onTableChanged()
if (m_disableTableUpdate)
return;
- const std::string newConfig = tableToString(sender());
- if (newConfig.empty())
- return;
- const std::string oldConfig = m_project ? currentProjectConfigText()
- : currentGlobalConfigText();
- saveConfig(newConfig);
- fillTable();
- updatePreview();
- saveConfig(oldConfig);
+ saveChanges(sender());
}
void ClangFormatConfigWidget::hideGlobalCheckboxes()
@@ -379,16 +380,17 @@ void ClangFormatConfigWidget::fillTable()
}
}
-std::string ClangFormatConfigWidget::tableToString(QObject *sender)
+void ClangFormatConfigWidget::saveChanges(QObject *sender)
{
- std::stringstream content;
- content << "---";
-
if (sender->objectName() == "BasedOnStyle") {
- auto *basedOnStyle = m_checksWidget->findChild<QComboBox *>("BasedOnStyle");
- content << "\nBasedOnStyle: " << basedOnStyle->currentText().toStdString() << '\n';
+ const auto *basedOnStyle = m_checksWidget->findChild<QComboBox *>("BasedOnStyle");
+ m_config->setBasedOnStyle(basedOnStyle->currentText());
} else {
+ QList<ClangFormatFile::Field> fields;
+
for (QObject *child : m_checksWidget->children()) {
+ if (child->objectName() == "BasedOnStyle")
+ continue;
auto *label = qobject_cast<QLabel *>(child);
if (!label)
continue;
@@ -396,7 +398,7 @@ std::string ClangFormatConfigWidget::tableToString(QObject *sender)
QWidget *valueWidget = m_checksWidget->findChild<QWidget *>(label->text().trimmed());
if (!valueWidget) {
// Currently BraceWrapping only.
- content << '\n' << label->text().toStdString() << ":";
+ fields.append({label->text(), ""});
continue;
}
@@ -410,46 +412,34 @@ std::string ClangFormatConfigWidget::tableToString(QObject *sender)
if (plainText->toPlainText().trimmed().isEmpty())
continue;
- content << '\n' << label->text().toStdString() << ":";
+
+ std::stringstream content;
QStringList list = plainText->toPlainText().split('\n');
for (const QString &line : list)
content << "\n " << line.toStdString();
+
+ fields.append({label->text(), QString::fromStdString(content.str())});
} else {
- auto *comboBox = qobject_cast<QComboBox *>(valueWidget);
- std::string text;
- if (comboBox) {
- text = comboBox->currentText().toStdString();
+ QString text;
+ if (auto *comboBox = qobject_cast<QComboBox *>(valueWidget)) {
+ text = comboBox->currentText();
} else {
auto *lineEdit = qobject_cast<QLineEdit *>(valueWidget);
QTC_ASSERT(lineEdit, continue;);
- text = lineEdit->text().toStdString();
+ text = lineEdit->text();
}
- if (!text.empty() && text != "Default")
- content << '\n' << label->text().toStdString() << ": " << text;
+ if (!text.isEmpty() && text != "Default")
+ fields.append({label->text(), text});
}
}
- content << '\n';
+ m_config->changeFields(fields);
}
- std::string text = content.str();
- clang::format::FormatStyle style;
- style.Language = clang::format::FormatStyle::LK_Cpp;
- const std::error_code error = clang::format::parseConfiguration(text, &style);
- if (error.value() != static_cast<int>(clang::format::ParseError::Success)) {
- QMessageBox::warning(this,
- tr("Error in ClangFormat configuration"),
- QString::fromStdString(error.message()));
- fillTable();
- updatePreview();
- return std::string();
- }
-
- return text;
+ fillTable();
+ updatePreview();
}
-ClangFormatConfigWidget::~ClangFormatConfigWidget() = default;
-
void ClangFormatConfigWidget::apply()
{
ClangFormatSettings &settings = ClangFormatSettings::instance();
@@ -466,28 +456,7 @@ void ClangFormatConfigWidget::apply()
if (!m_checksWidget->isVisible())
return;
- const std::string config = tableToString(this);
- if (config.empty())
- return;
-
- saveConfig(config);
- fillTable();
- updatePreview();
-}
-
-void ClangFormatConfigWidget::saveConfig(const std::string &text) const
-{
- QString filePath = Core::ICore::userResourcePath().toString();
- if (m_project)
- filePath += "/clang-format/" + currentProjectUniqueId();
- filePath += "/" + QLatin1String(Constants::SETTINGS_FILE_NAME);
-
- QFile file(filePath);
- if (!file.open(QFile::WriteOnly))
- return;
-
- file.write(text.c_str());
- file.close();
+ saveChanges(this);
}
} // namespace ClangFormat
diff --git a/src/plugins/clangformat/clangformatconfigwidget.h b/src/plugins/clangformat/clangformatconfigwidget.h
index 869ddeccb7..4c261f9992 100644
--- a/src/plugins/clangformat/clangformatconfigwidget.h
+++ b/src/plugins/clangformat/clangformatconfigwidget.h
@@ -44,6 +44,7 @@ namespace Ui {
class ClangFormatConfigWidget;
class ClangFormatChecksWidget;
}
+class ClangFormatFile;
class ClangFormatConfigWidget : public TextEditor::CodeStyleEditorWidget
{
@@ -66,18 +67,17 @@ private:
void connectChecks();
void fillTable();
- std::string tableToString(QObject *sender);
+ void saveChanges(QObject *sender);
void hideGlobalCheckboxes();
void showGlobalCheckboxes();
-
- void saveConfig(const std::string &text) const;
void updatePreview();
ProjectExplorer::Project *m_project;
QWidget *m_checksWidget;
QScrollArea *m_checksScrollArea;
TextEditor::SnippetEditorWidget *m_preview;
+ std::unique_ptr<ClangFormatFile> m_config;
std::unique_ptr<Ui::ClangFormatChecksWidget> m_checks;
std::unique_ptr<Ui::ClangFormatConfigWidget> m_ui;
bool m_disableTableUpdate = false;
diff --git a/src/plugins/clangformat/clangformatfile.cpp b/src/plugins/clangformat/clangformatfile.cpp
new file mode 100644
index 0000000000..8e532ea797
--- /dev/null
+++ b/src/plugins/clangformat/clangformatfile.cpp
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "clangformatfile.h"
+#include <sstream>
+
+using namespace ClangFormat;
+
+ClangFormatFile::ClangFormatFile(Utils::FilePath filePath)
+ : m_filePath(filePath)
+{
+ if (!m_filePath.exists()) {
+ resetStyleToLLVM();
+ return;
+ }
+
+ m_style.Language = clang::format::FormatStyle::LK_Cpp;
+ const std::error_code error
+ = clang::format::parseConfiguration(m_filePath.fileContents().toStdString(), &m_style);
+ if (error.value() != static_cast<int>(clang::format::ParseError::Success)) {
+ resetStyleToLLVM();
+ }
+}
+
+clang::format::FormatStyle ClangFormatFile::format() {
+ return m_style;
+}
+
+Utils::FilePath ClangFormatFile::filePath()
+{
+ return m_filePath;
+}
+
+void ClangFormatFile::setStyle(clang::format::FormatStyle style)
+{
+ m_style = style;
+ saveNewFormat();
+}
+
+void ClangFormatFile::resetStyleToLLVM()
+{
+ m_style = clang::format::getLLVMStyle();
+ saveNewFormat();
+}
+
+void ClangFormatFile::setBasedOnStyle(QString styleName)
+{
+ changeField({"BasedOnStyle", styleName});
+ saveNewFormat();
+}
+
+QString ClangFormatFile::setStyle(QString style)
+{
+ const std::error_code error = clang::format::parseConfiguration(style.toStdString(), &m_style);
+ if (error.value() != static_cast<int>(clang::format::ParseError::Success)) {
+ return QString::fromStdString(error.message());
+ }
+
+ saveNewFormat(style.toUtf8());
+ return "";
+}
+
+QString ClangFormatFile::changeField(Field field)
+{
+ return changeFields({field});
+}
+
+QString ClangFormatFile::changeFields(QList<Field> fields)
+{
+ std::stringstream content;
+ content << "---" << "\n";
+
+ for (const auto &field : fields) {
+ content << field.first.toStdString() << ": " << field.second.toStdString() << "\n";
+ }
+
+ return setStyle(QString::fromStdString(content.str()));
+}
+
+void ClangFormatFile::saveNewFormat()
+{
+ std::string style = clang::format::configurationAsText(m_style);
+
+ // workaround: configurationAsText() add comment "# " before BasedOnStyle line
+ const int pos = style.find("# BasedOnStyle");
+ if (pos < style.size())
+ style.erase(pos, 2);
+ m_filePath.writeFileContents(QByteArray::fromStdString(style));
+}
+
+void ClangFormatFile::saveNewFormat(QByteArray style)
+{
+ m_filePath.writeFileContents(style);
+}
diff --git a/src/plugins/clangformat/clangformatfile.h b/src/plugins/clangformat/clangformatfile.h
new file mode 100644
index 0000000000..3fe9010f44
--- /dev/null
+++ b/src/plugins/clangformat/clangformatfile.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "utils/filepath.h"
+#include <clang/Format/Format.h>
+
+namespace ClangFormat {
+
+class ClangFormatFile
+{
+public:
+ explicit ClangFormatFile(Utils::FilePath file);
+ clang::format::FormatStyle format();
+
+ Utils::FilePath filePath();
+ void resetStyleToLLVM();
+ void setBasedOnStyle(QString styleName);
+ void setStyle(clang::format::FormatStyle style);
+ QString setStyle(QString style);
+ void clearBasedOnStyle();
+
+ using Field = std::pair<QString, QString>;
+ QString changeFields(QList<Field> fields);
+ QString changeField(Field field);
+
+private:
+ void saveNewFormat();
+ void saveNewFormat(QByteArray style);
+
+private:
+ Utils::FilePath m_filePath;
+ clang::format::FormatStyle m_style;
+};
+
+
+} // namespace ClangFormat
diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp
index 5371aae25a..f43decd940 100644
--- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp
@@ -936,7 +936,7 @@ CMakeBuildConfiguration::CMakeBuildConfiguration(Target *target, Id id)
initialArgs.append("-DCMAKE_TOOLCHAIN_FILE:PATH="
+ ndkLocation.pathAppended("build/cmake/android.toolchain.cmake").path());
- auto androidAbis = bs->data(Android::Constants::AndroidABIs).toStringList();
+ auto androidAbis = bs->data(Android::Constants::AndroidMkSpecAbis).toStringList();
QString preferredAbi;
if (androidAbis.contains(ProjectExplorer::Constants::ANDROID_ABI_ARMEABI_V7A)) {
preferredAbi = ProjectExplorer::Constants::ANDROID_ABI_ARMEABI_V7A;
diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp
index 6c84e3d872..86d97babfe 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp
@@ -128,20 +128,26 @@ QVariant CMakeTargetNode::data(Utils::Id role) const
return {};
};
+ if (role == Android::Constants::AndroidAbi)
+ return value(Android::Constants::ANDROID_ABI);
+
+ if (role == Android::Constants::AndroidAbis)
+ return value(Android::Constants::ANDROID_ABIS);
+
+ // TODO: Concerns the variables below. Qt 6 uses target properties which cannot be read
+ // by the current mechanism, and the variables start with "Qt_" prefix.
+
if (role == Android::Constants::AndroidPackageSourceDir)
return value(Android::Constants::ANDROID_PACKAGE_SOURCE_DIR);
- if (role == Android::Constants::AndroidDeploySettingsFile)
- return value(Android::Constants::ANDROID_DEPLOYMENT_SETTINGS_FILE);
-
if (role == Android::Constants::AndroidExtraLibs)
return value(Android::Constants::ANDROID_EXTRA_LIBS);
- if (role == Android::Constants::ANDROID_APPLICATION_ARGUMENTS)
- return value(Android::Constants::QT_ANDROID_APPLICATION_ARGUMENTS);
+ if (role == Android::Constants::AndroidDeploySettingsFile)
+ return value(Android::Constants::ANDROID_DEPLOYMENT_SETTINGS_FILE);
- if (role == Android::Constants::AndroidArch)
- return value(Android::Constants::ANDROID_ABI);
+ if (role == Android::Constants::AndroidApplicationArgs)
+ return value(Android::Constants::ANDROID_APPLICATION_ARGUMENTS);
if (role == Android::Constants::ANDROID_ABIS)
return value(Android::Constants::ANDROID_ABIS);
@@ -152,6 +158,9 @@ QVariant CMakeTargetNode::data(Utils::Id role) const
if (role == Android::Constants::AndroidTargets)
return values("TARGETS_BUILD_PATH");
+ if (role == Android::Constants::AndroidApk)
+ return {};
+
if (role == Ios::Constants::IosTarget) {
// For some reason the artifact is e.g. "Debug/untitled.app/untitled" which is wrong.
// It actually is e.g. "Debug-iphonesimulator/untitled.app/untitled".
diff --git a/src/plugins/cmakeprojectmanager/cmaketool.cpp b/src/plugins/cmakeprojectmanager/cmaketool.cpp
index 51daa1bb5f..d407adeb66 100644
--- a/src/plugins/cmakeprojectmanager/cmaketool.cpp
+++ b/src/plugins/cmakeprojectmanager/cmaketool.cpp
@@ -360,7 +360,7 @@ Utils::optional<CMakeTool::ReaderType> CMakeTool::readerType() const
FilePath CMakeTool::searchQchFile(const FilePath &executable)
{
- if (executable.isEmpty())
+ if (executable.isEmpty() || executable.needsDevice()) // do not register docs from devices
return {};
FilePath prefixDir = executable.parentDir().parentDir();
diff --git a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp
index d893370028..cabac0b3b9 100644
--- a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp
+++ b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp
@@ -322,8 +322,14 @@ static QStringList splitFragments(const QStringList &fragments)
return result;
}
+bool isPchFile(const FilePath &buildDirectory, const FilePath &path)
+{
+ return path.isChildOf(buildDirectory) && path.fileName().startsWith("cmake_pch");
+}
+
RawProjectParts generateRawProjectParts(const PreprocessedData &input,
- const FilePath &sourceDirectory)
+ const FilePath &sourceDirectory,
+ const FilePath &buildDirectory)
{
RawProjectParts rpps;
@@ -331,8 +337,12 @@ RawProjectParts generateRawProjectParts(const PreprocessedData &input,
for (const TargetDetails &t : input.targetDetails) {
QDir sourceDir(sourceDirectory.toString());
+ // Do not tread generated files and CMake precompiled headers as project files
+ const auto sourceFiles = Utils::filtered(t.sources, [buildDirectory](const SourceInfo &si) {
+ return !si.isGenerated && !isPchFile(buildDirectory, FilePath::fromString(si.path));
+ });
CppEditor::ProjectFileCategorizer
- categorizer({}, transform<QList>(t.sources, [&sourceDir](const SourceInfo &si) {
+ categorizer({}, transform<QList>(sourceFiles, [&sourceDir](const SourceInfo &si) {
return sourceDir.absoluteFilePath(si.path);
}));
@@ -531,6 +541,11 @@ void addCompileGroups(ProjectNode *targetRoot,
auto node = std::make_unique<FileNode>(sourcePath, Node::fileTypeForFileName(sourcePath));
node->setIsGenerated(si.isGenerated);
+ // CMake pch files are generated at configured time, but not marked as generated
+ // so that a "clean" step won't remove them and at a subsequent build they won't exist.
+ if (isPchFile(buildDirectory, sourcePath))
+ node->setIsGenerated(true);
+
// Where does the file node need to go?
if (sourcePath.isChildOf(buildDirectory) && !inSourceBuild) {
buildFileNodes.emplace_back(std::move(node));
@@ -638,9 +653,16 @@ std::unique_ptr<CMakeProjectNode> generateRootProjectNode(
void setupLocationInfoForTargets(CMakeProjectNode *rootNode, const QList<CMakeBuildTarget> &targets)
{
+ const QSet<QString> titles = Utils::transform<QSet>(targets, &CMakeBuildTarget::title);
+ QHash<QString, FolderNode *> buildKeyToNode;
+ rootNode->forEachGenericNode([&buildKeyToNode, &titles](Node *node) {
+ FolderNode *folderNode = node->asFolderNode();
+ const QString &buildKey = node->buildKey();
+ if (folderNode && titles.contains(buildKey))
+ buildKeyToNode.insert(buildKey, folderNode);
+ });
for (const CMakeBuildTarget &t : targets) {
- FolderNode *folderNode = static_cast<FolderNode *>(
- rootNode->findNode(Utils::equal(&Node::buildKey, t.title)));
+ FolderNode *folderNode = buildKeyToNode.value(t.title);
if (folderNode) {
QSet<std::pair<FilePath, int>> locations;
auto dedup = [&locations](const Backtrace &bt) {
@@ -705,7 +727,7 @@ FileApiQtcData extractData(FileApiData &input,
result.buildTargets = generateBuildTargets(data, sourceDirectory, buildDirectory, haveLibrariesRelativeToBuildDirectory);
result.cmakeFiles = std::move(data.cmakeFiles);
- result.projectParts = generateRawProjectParts(data, sourceDirectory);
+ result.projectParts = generateRawProjectParts(data, sourceDirectory, buildDirectory);
auto rootProjectNode = generateRootProjectNode(data, sourceDirectory, buildDirectory);
ProjectTree::applyTreeManager(rootProjectNode.get()); // QRC nodes
diff --git a/src/plugins/coreplugin/dialogs/newdialogwidget.cpp b/src/plugins/coreplugin/dialogs/newdialogwidget.cpp
index f8a77c4e8d..66569b85cf 100644
--- a/src/plugins/coreplugin/dialogs/newdialogwidget.cpp
+++ b/src/plugins/coreplugin/dialogs/newdialogwidget.cpp
@@ -32,7 +32,6 @@
#include <utils/qtcassert.h>
#include <utils/utilsicons.h>
-#include <QAbstractProxyModel>
#include <QDebug>
#include <QItemDelegate>
#include <QKeyEvent>
diff --git a/src/plugins/coreplugin/locator/filesystemfilter.cpp b/src/plugins/coreplugin/locator/filesystemfilter.cpp
index 95c9bf16ea..9e0751e570 100644
--- a/src/plugins/coreplugin/locator/filesystemfilter.cpp
+++ b/src/plugins/coreplugin/locator/filesystemfilter.cpp
@@ -176,8 +176,9 @@ void FileSystemFilter::accept(LocatorFilterEntry selection,
{
Q_UNUSED(selectionLength)
if (selection.filePath.isDir()) {
- const QString value = shortcutString() + ' '
- + selection.filePath.absoluteFilePath().toUserOutput() + '/';
+ const QString value
+ = shortcutString() + ' '
+ + selection.filePath.absoluteFilePath().cleanPath().pathAppended("/").toUserOutput();
*newText = value;
*selectionStart = value.length();
} else {
diff --git a/src/plugins/coreplugin/manhattanstyle.cpp b/src/plugins/coreplugin/manhattanstyle.cpp
index 9a8934b989..50837d9f15 100644
--- a/src/plugins/coreplugin/manhattanstyle.cpp
+++ b/src/plugins/coreplugin/manhattanstyle.cpp
@@ -73,14 +73,20 @@ bool styleEnabled(const QWidget *widget)
return true;
}
+static bool isInDialogOrPopup(const QWidget *widget)
+{
+ // Do not style dialogs or explicitly ignored widgets
+ const Qt::WindowType windowType = widget->window()->windowType();
+ return (windowType == Qt::Dialog || windowType == Qt::Popup);
+}
+
// Consider making this a QStyle state
bool panelWidget(const QWidget *widget)
{
if (!widget)
return false;
- // Do not style dialogs or explicitly ignored widgets
- if ((widget->window()->windowFlags() & Qt::WindowType_Mask) == Qt::Dialog)
+ if (isInDialogOrPopup(widget))
return false;
if (qobject_cast<const FancyMainWindow *>(widget))
@@ -107,8 +113,7 @@ bool lightColored(const QWidget *widget)
if (!widget)
return false;
- // Don't style dialogs or explicitly ignored widgets
- if ((widget->window()->windowFlags() & Qt::WindowType_Mask) == Qt::Dialog)
+ if (isInDialogOrPopup(widget))
return false;
const QWidget *p = widget;
diff --git a/src/plugins/cppcheck/cppcheckoptions.cpp b/src/plugins/cppcheck/cppcheckoptions.cpp
index 735557daf2..d6c7b0196c 100644
--- a/src/plugins/cppcheck/cppcheckoptions.cpp
+++ b/src/plugins/cppcheck/cppcheckoptions.cpp
@@ -39,9 +39,10 @@
#include <debugger/analyzer/analyzericons.h>
#include <QCheckBox>
-#include <QDir>
#include <QFormLayout>
+using namespace Utils;
+
namespace Cppcheck {
namespace Internal {
@@ -102,7 +103,7 @@ OptionsWidget::OptionsWidget(QWidget *parent)
void OptionsWidget::load(const CppcheckOptions &options)
{
- m_binary->setPath(options.binary);
+ m_binary->setFilePath(options.binary);
m_customArguments->setText(options.customArguments);
m_ignorePatterns->setText(options.ignoredPatterns);
m_warning->setChecked(options.warning);
@@ -121,7 +122,7 @@ void OptionsWidget::load(const CppcheckOptions &options)
void OptionsWidget::save(CppcheckOptions &options) const
{
- options.binary = m_binary->filePath().toString();
+ options.binary = m_binary->filePath();
options.customArguments = m_customArguments->text();
options.ignoredPatterns = m_ignorePatterns->text();
options.warning = m_warning->isChecked();
@@ -149,14 +150,13 @@ CppcheckOptionsPage::CppcheckOptionsPage(CppcheckTool &tool, CppcheckTrigger &tr
setCategoryIconPath(Analyzer::Icons::SETTINGSCATEGORY_ANALYZER);
CppcheckOptions options;
- if (Utils::HostOsInfo::isAnyUnixHost()) {
+ if (HostOsInfo::isAnyUnixHost()) {
options.binary = "cppcheck";
} else {
- QString programFiles = QDir::fromNativeSeparators(
- QString::fromLocal8Bit(qgetenv("PROGRAMFILES")));
+ FilePath programFiles = FilePath::fromUserInput(qEnvironmentVariable("PROGRAMFILES"));
if (programFiles.isEmpty())
programFiles = "C:/Program Files";
- options.binary = programFiles + "/Cppcheck/cppcheck.exe";
+ options.binary = programFiles / "Cppcheck/cppcheck.exe";
}
load(options);
@@ -190,7 +190,7 @@ void CppcheckOptionsPage::save(const CppcheckOptions &options) const
QSettings *s = Core::ICore::settings();
QTC_ASSERT(s, return);
s->beginGroup(Constants::SETTINGS_ID);
- s->setValue(Constants::SETTINGS_BINARY, options.binary);
+ s->setValue(Constants::SETTINGS_BINARY, options.binary.toString());
s->setValue(Constants::SETTINGS_CUSTOM_ARGUMENTS, options.customArguments);
s->setValue(Constants::SETTINGS_IGNORE_PATTERNS, options.ignoredPatterns);
s->setValue(Constants::SETTINGS_WARNING, options.warning);
@@ -213,8 +213,8 @@ void CppcheckOptionsPage::load(CppcheckOptions &options) const
QSettings *s = Core::ICore::settings();
QTC_ASSERT(s, return);
s->beginGroup(Constants::SETTINGS_ID);
- options.binary = s->value(Constants::SETTINGS_BINARY,
- options.binary).toString();
+ options.binary = FilePath::fromString(s->value(Constants::SETTINGS_BINARY,
+ options.binary.toString()).toString());
options.customArguments = s->value(Constants::SETTINGS_CUSTOM_ARGUMENTS,
options.customArguments).toString();
options.ignoredPatterns = s->value(Constants::SETTINGS_IGNORE_PATTERNS,
diff --git a/src/plugins/cppcheck/cppcheckoptions.h b/src/plugins/cppcheck/cppcheckoptions.h
index 639ffca6c9..4d4ca83cb6 100644
--- a/src/plugins/cppcheck/cppcheckoptions.h
+++ b/src/plugins/cppcheck/cppcheckoptions.h
@@ -26,6 +26,7 @@
#pragma once
#include <coreplugin/dialogs/ioptionspage.h>
+#include <utils/filepath.h>
#include <QCoreApplication>
#include <QPointer>
@@ -36,9 +37,7 @@ class QLineEdit;
class QCheckBox;
QT_END_NAMESPACE
-namespace Utils {
-class PathChooser;
-}
+namespace Utils { class PathChooser; }
namespace Cppcheck {
namespace Internal {
@@ -50,7 +49,7 @@ class OptionsWidget;
class CppcheckOptions final
{
public:
- QString binary;
+ Utils::FilePath binary;
bool warning = true;
bool style = true;
diff --git a/src/plugins/cppcheck/cppcheckrunner.cpp b/src/plugins/cppcheck/cppcheckrunner.cpp
index 2f6cffb1eb..261ccb3b63 100644
--- a/src/plugins/cppcheck/cppcheckrunner.cpp
+++ b/src/plugins/cppcheck/cppcheckrunner.cpp
@@ -74,7 +74,7 @@ CppcheckRunner::~CppcheckRunner()
m_queueTimer.stop();
}
-void CppcheckRunner::reconfigure(const QString &binary, const QString &arguments)
+void CppcheckRunner::reconfigure(const FilePath &binary, const QString &arguments)
{
m_binary = binary;
m_arguments = arguments;
@@ -157,7 +157,7 @@ void CppcheckRunner::checkQueued()
else
m_queue.begin().value() = files;
- m_process->setCommand(CommandLine(FilePath::fromString(m_binary), arguments, CommandLine::Raw));
+ m_process->setCommand(CommandLine(m_binary, arguments, CommandLine::Raw));
m_process->start();
}
diff --git a/src/plugins/cppcheck/cppcheckrunner.h b/src/plugins/cppcheck/cppcheckrunner.h
index 83aca95192..d49a9016de 100644
--- a/src/plugins/cppcheck/cppcheckrunner.h
+++ b/src/plugins/cppcheck/cppcheckrunner.h
@@ -45,7 +45,7 @@ public:
explicit CppcheckRunner(CppcheckTool &tool);
~CppcheckRunner() override;
- void reconfigure(const QString &binary, const QString &arguments);
+ void reconfigure(const Utils::FilePath &binary, const QString &arguments);
void addToQueue(const Utils::FilePaths &files,
const QString &additionalArguments = {});
void removeFromQueue(const Utils::FilePaths &files);
@@ -63,7 +63,7 @@ private:
CppcheckTool &m_tool;
Utils::QtcProcess *m_process = nullptr;
- QString m_binary;
+ Utils::FilePath m_binary;
QString m_arguments;
QHash<QString, Utils::FilePaths> m_queue;
Utils::FilePaths m_currentFiles;
diff --git a/src/plugins/cppeditor/CppEditor.json.in b/src/plugins/cppeditor/CppEditor.json.in
index 437e85c35f..b3c0149291 100644
--- a/src/plugins/cppeditor/CppEditor.json.in
+++ b/src/plugins/cppeditor/CppEditor.json.in
@@ -61,7 +61,7 @@
\" <!-- Find include guards of header files without extension, for\",
\" example, STL ones like <string>. Those can have a big initial\",
\" comment exceeding 1000 chars, though. -->\",
- \" <magic priority=\'50\'>\",
+ \" <magic priority=\'40\'>\",
\" <match value=\'#ifndef \' type=\'string\' offset=\'0:2000\'/>\",
\" <match value=\'#if \' type=\'string\' offset=\'0:2000\'/>\",
\" <match value=\'#include \' type=\'string\' offset=\'0:2000\'/>\",
diff --git a/src/plugins/cppeditor/semantichighlighter.cpp b/src/plugins/cppeditor/semantichighlighter.cpp
index b4c1a6ff1f..f3112a725b 100644
--- a/src/plugins/cppeditor/semantichighlighter.cpp
+++ b/src/plugins/cppeditor/semantichighlighter.cpp
@@ -34,6 +34,7 @@
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
+#include <QElapsedTimer>
#include <QLoggingCategory>
#include <QTextDocument>
@@ -165,6 +166,8 @@ void SemanticHighlighter::onHighlighterResultAvailable(int from, int to)
return; // aborted
qCDebug(log) << "onHighlighterResultAvailable()" << from << to;
+ QElapsedTimer t;
+ t.start();
SyntaxHighlighter *highlighter = m_baseTextDocument->syntaxHighlighter();
QTC_ASSERT(highlighter, return);
@@ -234,11 +237,17 @@ void SemanticHighlighter::onHighlighterResultAvailable(int from, int to)
}
if (parentheses.first.isValid())
TextDocumentLayout::setParentheses(parentheses.first, parentheses.second);
+
+ qCDebug(log) << "onHighlighterResultAvailable() took" << t.elapsed() << "ms";
}
void SemanticHighlighter::onHighlighterFinished()
{
QTC_ASSERT(m_watcher, return);
+
+ QElapsedTimer t;
+ t.start();
+
if (!m_watcher->isCanceled() && documentRevision() == m_revision) {
SyntaxHighlighter *highlighter = m_baseTextDocument->syntaxHighlighter();
if (QTC_GUARD(highlighter)) {
@@ -273,6 +282,7 @@ void SemanticHighlighter::onHighlighterFinished()
}
m_watcher.reset();
+ qCDebug(log) << "onHighlighterFinished() took" << t.elapsed() << "ms";
}
void SemanticHighlighter::connectWatcher()
diff --git a/src/plugins/debugger/shared/cdbsymbolpathlisteditor.cpp b/src/plugins/debugger/shared/cdbsymbolpathlisteditor.cpp
index 2e67bf675e..18fcdce5dc 100644
--- a/src/plugins/debugger/shared/cdbsymbolpathlisteditor.cpp
+++ b/src/plugins/debugger/shared/cdbsymbolpathlisteditor.cpp
@@ -35,7 +35,6 @@
#include "symbolpathsdialog.h"
#include <QCheckBox>
-#include <QDir>
#include <QDebug>
#include <QAction>
#include <QFormLayout>
@@ -165,7 +164,7 @@ void CdbSymbolPathListEditor::addSymbolPath(CdbSymbolPathListEditor::SymbolPathM
{
FilePath cacheDir;
if (promptCacheDirectory(this, &cacheDir))
- insertPathAtCursor(CdbSymbolPathListEditor::symbolPath(cacheDir.path(), mode));
+ insertPathAtCursor(CdbSymbolPathListEditor::symbolPath(cacheDir, mode));
}
void CdbSymbolPathListEditor::setupSymbolPaths()
@@ -174,13 +173,13 @@ void CdbSymbolPathListEditor::setupSymbolPaths()
const int indexOfSymbolServer = indexOfSymbolPath(currentPaths, SymbolServerPath);
const int indexOfSymbolCache = indexOfSymbolPath(currentPaths, SymbolCachePath);
- QString path;
+ FilePath path;
if (indexOfSymbolServer != -1)
- path = currentPaths.at(indexOfSymbolServer);
+ path = FilePath::fromString(currentPaths.at(indexOfSymbolServer));
if (path.isEmpty() && indexOfSymbolCache != -1)
- path = currentPaths.at(indexOfSymbolCache);
+ path = FilePath::fromString(currentPaths.at(indexOfSymbolCache));
if (path.isEmpty())
- path = TemporaryDirectory::masterDirectoryPath() + "/symbolcache";
+ path = FilePath::fromString(TemporaryDirectory::masterDirectoryPath() + "/symbolcache");
bool useSymbolServer = true;
bool useSymbolCache = true;
@@ -193,20 +192,20 @@ void CdbSymbolPathListEditor::setupSymbolPaths()
if (useSymbolCache) {
insertPathAtCursor(CdbSymbolPathListEditor::symbolPath(path, SymbolCachePath));
if (useSymbolServer)
- insertPathAtCursor(CdbSymbolPathListEditor::symbolPath(QString(), SymbolServerPath));
+ insertPathAtCursor(CdbSymbolPathListEditor::symbolPath({}, SymbolServerPath));
} else if (useSymbolServer) {
insertPathAtCursor(CdbSymbolPathListEditor::symbolPath(path, SymbolServerPath));
}
}
-QString CdbSymbolPathListEditor::symbolPath(const QString &cacheDir,
+QString CdbSymbolPathListEditor::symbolPath(const FilePath &cacheDir,
CdbSymbolPathListEditor::SymbolPathMode mode)
{
if (mode == SymbolCachePath)
- return symbolCachePrefixC + QDir::toNativeSeparators(cacheDir);
+ return symbolCachePrefixC + cacheDir.toUserOutput();
QString s = QLatin1String(symbolServerPrefixC);
if (!cacheDir.isEmpty())
- s += QDir::toNativeSeparators(cacheDir) + '*';
+ s += cacheDir.toUserOutput() + '*';
s += QLatin1String(symbolServerPostfixC);
return s;
}
diff --git a/src/plugins/debugger/shared/cdbsymbolpathlisteditor.h b/src/plugins/debugger/shared/cdbsymbolpathlisteditor.h
index df64b35bfe..f7dffcdda0 100644
--- a/src/plugins/debugger/shared/cdbsymbolpathlisteditor.h
+++ b/src/plugins/debugger/shared/cdbsymbolpathlisteditor.h
@@ -53,7 +53,7 @@ public:
static bool promptCacheDirectory(QWidget *parent, Utils::FilePath *cacheDirectory);
// Format a symbol path specification
- static QString symbolPath(const QString &cacheDir, SymbolPathMode mode);
+ static QString symbolPath(const Utils::FilePath &cacheDir, SymbolPathMode mode);
// Check for a symbol server path and extract local cache directory
static bool isSymbolServerPath(const QString &path, QString *cacheDir = nullptr);
// Check for a symbol cache path and extract local cache directory
diff --git a/src/plugins/debugger/shared/symbolpathsdialog.cpp b/src/plugins/debugger/shared/symbolpathsdialog.cpp
index db0354cca4..be8ef21afe 100644
--- a/src/plugins/debugger/shared/symbolpathsdialog.cpp
+++ b/src/plugins/debugger/shared/symbolpathsdialog.cpp
@@ -26,10 +26,14 @@
#include "symbolpathsdialog.h"
#include "ui_symbolpathsdialog.h"
+#include <utils/filepath.h>
+
#include <QMessageBox>
-using namespace Debugger;
-using namespace Internal;
+using namespace Utils;
+
+namespace Debugger {
+namespace Internal {
SymbolPathsDialog::SymbolPathsDialog(QWidget *parent) :
QDialog(parent),
@@ -54,9 +58,9 @@ bool SymbolPathsDialog::useSymbolServer() const
return ui->useSymbolServer->isChecked();
}
-QString SymbolPathsDialog::path() const
+FilePath SymbolPathsDialog::path() const
{
- return ui->pathChooser->filePath().toString();
+ return ui->pathChooser->filePath();
}
void SymbolPathsDialog::setUseSymbolCache(bool useSymbolCache)
@@ -69,13 +73,13 @@ void SymbolPathsDialog::setUseSymbolServer(bool useSymbolServer)
ui->useSymbolServer->setChecked(useSymbolServer);
}
-void SymbolPathsDialog::setPath(const QString &path)
+void SymbolPathsDialog::setPath(const FilePath &path)
{
- ui->pathChooser->setPath(path);
+ ui->pathChooser->setFilePath(path);
}
bool SymbolPathsDialog::useCommonSymbolPaths(bool &useSymbolCache, bool &useSymbolServer,
- QString &path)
+ FilePath &path)
{
SymbolPathsDialog dialog;
dialog.setUseSymbolCache(useSymbolCache);
@@ -87,3 +91,6 @@ bool SymbolPathsDialog::useCommonSymbolPaths(bool &useSymbolCache, bool &useSymb
path = dialog.path();
return ret == QDialog::Accepted;
}
+
+} // Internal
+} // Debugger
diff --git a/src/plugins/debugger/shared/symbolpathsdialog.h b/src/plugins/debugger/shared/symbolpathsdialog.h
index c3534fbcf6..c77e112ea4 100644
--- a/src/plugins/debugger/shared/symbolpathsdialog.h
+++ b/src/plugins/debugger/shared/symbolpathsdialog.h
@@ -28,6 +28,8 @@
#include <QDialog>
#include <QString>
+namespace Utils { class FilePath; }
+
namespace Debugger {
namespace Internal {
@@ -43,15 +45,15 @@ public:
bool useSymbolCache() const;
bool useSymbolServer() const;
- QString path() const;
+ Utils::FilePath path() const;
bool doNotAskAgain() const;
void setUseSymbolCache(bool useSymbolCache);
void setUseSymbolServer(bool useSymbolServer);
- void setPath(const QString &path);
+ void setPath(const Utils::FilePath &path);
void setDoNotAskAgain(bool doNotAskAgain) const;
- static bool useCommonSymbolPaths(bool &useSymbolCache, bool &useSymbolServer, QString &path);
+ static bool useCommonSymbolPaths(bool &useSymbolCache, bool &useSymbolServer, Utils::FilePath &path);
private:
Ui::SymbolPathsDialog *ui;
diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp
index 624f3eab49..638fb91db0 100644
--- a/src/plugins/docker/dockerdevice.cpp
+++ b/src/plugins/docker/dockerdevice.cpp
@@ -49,17 +49,18 @@
#include <utils/algorithm.h>
#include <utils/basetreeview.h>
#include <utils/environment.h>
+#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
-#include <utils/utilsicons.h>
#include <utils/layoutbuilder.h>
#include <utils/overridecursor.h>
+#include <utils/pathlisteditor.h>
#include <utils/port.h>
#include <utils/qtcassert.h>
#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
#include <utils/temporaryfile.h>
#include <utils/treemodel.h>
-#include <utils/fileutils.h>
+#include <utils/utilsicons.h>
#include <QApplication>
#include <QCheckBox>
@@ -75,8 +76,8 @@
#include <QRandomGenerator>
#include <QRegularExpression>
#include <QTextBrowser>
-#include <QToolButton>
#include <QThread>
+#include <QToolButton>
#include <numeric>
@@ -394,15 +395,13 @@ public:
dockerDevice->tryCreateLocalFileAccess();
});
- m_pathsLineEdit = new QLineEdit;
- m_pathsLineEdit->setText(data.repo);
- m_pathsLineEdit->setToolTip(tr("Paths in this semi-colon separated list will be "
- "mapped one-to-one into the Docker container."));
- m_pathsLineEdit->setText(data.mounts.join(';'));
- m_pathsLineEdit->setPlaceholderText(tr("List project source directories here"));
+ m_pathsListEdit = new PathListEditor;
+ m_pathsListEdit->setToolTip(tr("Paths in this list will be mapped one-to-one into the "
+ "Docker container."));
+ m_pathsListEdit->setPathList(data.mounts);
- connect(m_pathsLineEdit, &QLineEdit::textChanged, this, [dockerDevice](const QString &text) {
- dockerDevice->setMounts(text.split(';', Qt::SkipEmptyParts));
+ connect(m_pathsListEdit, &PathListEditor::changed, this, [dockerDevice, this]() {
+ dockerDevice->setMounts(m_pathsListEdit->pathList());
});
auto logView = new QTextBrowser;
@@ -443,7 +442,10 @@ public:
daemonStateLabel, m_daemonReset, m_daemonState, Break(),
m_runAsOutsideUser, Break(),
m_usePathMapping, Break(),
- tr("Paths to mount:"), m_pathsLineEdit, Break(),
+ Column {
+ new QLabel(tr("Paths to mount:")),
+ m_pathsListEdit,
+ }, Break(),
Column {
Space(20),
Row { autoDetectButton, undoAutoDetectButton, listAutoDetectedButton, Stretch() },
@@ -463,7 +465,7 @@ private:
QLabel *m_daemonState;
QCheckBox *m_runAsOutsideUser;
QCheckBox *m_usePathMapping;
- QLineEdit *m_pathsLineEdit;
+ Utils::PathListEditor *m_pathsListEdit;
KitDetector m_kitItemDetector;
};
@@ -813,34 +815,51 @@ static QString getLocalIPv4Address()
void DockerDevicePrivate::startContainer()
{
- QString tempFileName;
-
- {
- TemporaryFile temp("qtc-docker-XXXXXX");
- temp.open();
- tempFileName = temp.fileName();
- }
-
const QString display = HostOsInfo::isWindowsHost() ? QString(getLocalIPv4Address() + ":0.0")
: QString(":0");
- CommandLine dockerRun{"docker", {"run", "-i", "--cidfile=" + tempFileName,
- "--rm",
- "-e", QString("DISPLAY=%1").arg(display),
- "-e", "XAUTHORITY=/.Xauthority",
- "--net", "host"}};
+ CommandLine dockerCreate{"docker", {"create",
+ "-i",
+ "--rm",
+ "-e", QString("DISPLAY=%1").arg(display),
+ "-e", "XAUTHORITY=/.Xauthority",
+ "--net", "host"}};
#ifdef Q_OS_UNIX
+ // no getuid() and getgid() on Windows.
if (m_data.useLocalUidGid)
- dockerRun.addArgs({"-u", QString("%1:%2").arg(getuid()).arg(getgid())});
+ dockerCreate.addArgs({"-u", QString("%1:%2").arg(getuid()).arg(getgid())});
#endif
- for (const QString &mount : qAsConst(m_data.mounts)) {
- if (!mount.isEmpty())
- dockerRun.addArgs({"-v", mount + ':' + mount});
+ for (QString mount : qAsConst(m_data.mounts)) {
+ if (mount.isEmpty())
+ continue;
+ // make sure to convert windows style paths to unix style paths with the file system case:
+ // C:/dev/src -> /c/dev/src
+ if (const FilePath mountPath = FilePath::fromUserInput(mount).normalizedPathName();
+ mountPath.startsWithDriveLetter()) {
+ const QChar lowerDriveLetter = mountPath.path().at(0).toLower();
+ const FilePath path = FilePath::fromUserInput(mountPath.path().mid(2)); // strip C:
+ mount = '/' + lowerDriveLetter + path.path();
+ }
+ dockerCreate.addArgs({"-v", mount + ':' + mount});
}
- dockerRun.addArgs({"--entrypoint", "/bin/sh", m_data.imageId});
+ dockerCreate.addArgs({"--entrypoint", "/bin/sh", m_data.imageId});
+
+ LOG("RUNNING: " << dockerCreate.toUserOutput());
+ QtcProcess createProcess;
+ createProcess.setCommand(dockerCreate);
+ createProcess.runBlocking();
+
+ if (createProcess.result() != QtcProcess::FinishedWithSuccess)
+ return;
+
+ m_container = createProcess.stdOut().trimmed();
+ if (m_container.isEmpty())
+ return;
+ LOG("Container via process: " << m_container);
+ CommandLine dockerRun{"docker", {"container" , "start", "-i", "-a", m_container}};
LOG("RUNNING: " << dockerRun.toUserOutput());
QPointer<QtcProcess> shell = new QtcProcess(ProcessMode::Writer);
connect(shell, &QtcProcess::finished, this, [this, shell] {
@@ -875,23 +894,6 @@ void DockerDevicePrivate::startContainer()
return;
}
- LOG("CHECKING: " << tempFileName);
- for (int i = 0; i <= 20; ++i) {
- QFile file(tempFileName);
- if (file.open(QIODevice::ReadOnly)) {
- m_container = QString::fromUtf8(file.readAll()).trimmed();
- if (!m_container.isEmpty()) {
- LOG("Container: " << m_container);
- break;
- }
- }
- if (i == 20 || !DockerPlugin::isDaemonRunning().value_or(true)) {
- qWarning("Docker cid file empty.");
- return; // No
- }
- qApp->processEvents(); // FIXME turn this for-loop into
- QThread::msleep(100);
- }
DockerPlugin::setGlobalDaemonState(true);
}
@@ -1288,12 +1290,8 @@ QDateTime DockerDevice::lastModified(const FilePath &filePath) const
return res;
}
- QtcProcess proc;
- proc.setCommand({"stat", {"-c", "%Y", filePath.path()}});
- runProcess(proc);
- proc.waitForFinished();
-
- const qint64 secs = proc.rawStdOut().toLongLong();
+ const QString output = d->outputForRunInShell({"stat", {"-c", "%Y", filePath.path()}});
+ qint64 secs = output.toLongLong();
const QDateTime dt = QDateTime::fromSecsSinceEpoch(secs, Qt::UTC);
return dt;
}
@@ -1615,6 +1613,12 @@ void DockerDevice::aboutToBeRemoved() const
void DockerDevicePrivate::fetchSystemEnviroment()
{
+ if (m_shell) {
+ const QString remoteOutput = outputForRunInShell({"env", {}});
+ m_cachedEnviroment = Environment(remoteOutput.split('\n', Qt::SkipEmptyParts), q->osType());
+ return;
+ }
+
QtcProcess proc;
proc.setCommand({"env", {}});
diff --git a/src/plugins/help/litehtmlhelpviewer.cpp b/src/plugins/help/litehtmlhelpviewer.cpp
index c33ebf62ef..8d54e01b69 100644
--- a/src/plugins/help/litehtmlhelpviewer.cpp
+++ b/src/plugins/help/litehtmlhelpviewer.cpp
@@ -235,8 +235,10 @@ bool LiteHtmlHelpViewer::eventFilter(QObject *src, QEvent *e)
{
if (isScrollWheelZoomingEnabled() && e->type() == QEvent::Wheel) {
auto we = static_cast<QWheelEvent *>(e);
- if (we->modifiers() == Qt::ControlModifier)
+ if (we->modifiers() == Qt::ControlModifier) {
+ e->ignore();
return true;
+ }
}
return HelpViewer::eventFilter(src, e);
}
diff --git a/src/plugins/imageviewer/imageviewerfile.cpp b/src/plugins/imageviewer/imageviewerfile.cpp
index 2a6204e11d..3fe26890d0 100644
--- a/src/plugins/imageviewer/imageviewerfile.cpp
+++ b/src/plugins/imageviewer/imageviewerfile.cpp
@@ -126,7 +126,15 @@ Core::IDocument::OpenResult ImageViewerFile::openImpl(QString *errorString,
m_type = TypeMovie;
m_movie = new QMovie(fileName, QByteArray(), this);
m_movie->setCacheMode(QMovie::CacheAll);
- connect(m_movie, &QMovie::finished, m_movie, &QMovie::start);
+ connect(
+ m_movie,
+ &QMovie::finished,
+ m_movie,
+ [this] {
+ if (m_movie->isValid())
+ m_movie->start();
+ },
+ Qt::QueuedConnection);
connect(m_movie, &QMovie::resized, this, &ImageViewerFile::imageSizeChanged);
m_movie->start();
m_isPaused = false; // force update
diff --git a/src/plugins/mcusupport/mcusupportoptions.cpp b/src/plugins/mcusupport/mcusupportoptions.cpp
index 6fa37d5a83..ba6107d6fc 100644
--- a/src/plugins/mcusupport/mcusupportoptions.cpp
+++ b/src/plugins/mcusupport/mcusupportoptions.cpp
@@ -72,15 +72,15 @@ namespace Internal {
static const int KIT_VERSION = 8; // Bumps up whenever details in Kit creation change
-static QString packagePathFromSettings(const QString &settingsKey,
- QSettings::Scope scope = QSettings::UserScope,
- const QString &defaultPath = {})
+static FilePath packagePathFromSettings(const QString &settingsKey,
+ QSettings::Scope scope = QSettings::UserScope,
+ const FilePath &defaultPath = {})
{
QSettings *settings = Core::ICore::settings(scope);
const QString key = QLatin1String(Constants::SETTINGS_GROUP) + '/' +
QLatin1String(Constants::SETTINGS_KEY_PACKAGE_PREFIX) + settingsKey;
- const QString path = settings->value(key, defaultPath).toString();
- return FilePath::fromUserInput(path).toString();
+ const QString path = settings->value(key, defaultPath.toString()).toString();
+ return FilePath::fromUserInput(path);
}
static bool automaticKitCreationFromSettings(QSettings::Scope scope = QSettings::UserScope)
@@ -99,7 +99,7 @@ static bool kitNeedsQtVersion()
return !HostOsInfo::isWindowsHost();
}
-McuPackage::McuPackage(const QString &label, const QString &defaultPath,
+McuPackage::McuPackage(const QString &label, const FilePath &defaultPath,
const QString &detectionPath, const QString &settingsKey,
const McuPackageVersionDetector *versionDetector)
: m_label(label)
@@ -112,14 +112,14 @@ McuPackage::McuPackage(const QString &label, const QString &defaultPath,
m_automaticKitCreation = automaticKitCreationFromSettings(QSettings::UserScope);
}
-QString McuPackage::basePath() const
+FilePath McuPackage::basePath() const
{
- return m_fileChooser != nullptr ? m_fileChooser->filePath().toString() : m_path;
+ return m_fileChooser != nullptr ? m_fileChooser->filePath() : m_path;
}
-QString McuPackage::path() const
+FilePath McuPackage::path() const
{
- return QFileInfo(basePath() + m_relativePathModifier).absoluteFilePath();
+ return basePath().resolvePath(m_relativePathModifier).absoluteFilePath();
}
QString McuPackage::label() const
@@ -127,7 +127,7 @@ QString McuPackage::label() const
return m_label;
}
-QString McuPackage::defaultPath() const
+FilePath McuPackage::defaultPath() const
{
return m_defaultPath;
}
@@ -148,7 +148,7 @@ QWidget *McuPackage::widget()
Icons::RESET.icon());
m_fileChooser->lineEdit()->setButtonVisible(FancyLineEdit::Right, true);
connect(m_fileChooser->lineEdit(), &FancyLineEdit::rightButtonClicked, this, [&] {
- m_fileChooser->setPath(m_defaultPath);
+ m_fileChooser->setFilePath(m_defaultPath);
});
auto layout = new QGridLayout(m_widget);
@@ -168,7 +168,7 @@ QWidget *McuPackage::widget()
layout->addWidget(m_fileChooser, 0, 0, 1, 2);
layout->addWidget(m_infoLabel, 1, 0, 1, -1);
- m_fileChooser->setPath(m_path);
+ m_fileChooser->setFilePath(m_path);
QObject::connect(this, &McuPackage::statusChanged, this, [this] {
updateStatusUi();
@@ -228,7 +228,7 @@ void McuPackage::writeGeneralSettings() const
bool McuPackage::writeToSettings() const
{
- const QString savedPath = packagePathFromSettings(m_settingsKey, QSettings::UserScope, m_defaultPath);
+ const FilePath savedPath = packagePathFromSettings(m_settingsKey, QSettings::UserScope, m_defaultPath);
const QString key = QLatin1String(Constants::SETTINGS_GROUP) + '/' +
QLatin1String(Constants::SETTINGS_KEY_PACKAGE_PREFIX) + m_settingsKey;
Core::ICore::settings()->setValueWithDefault(key, m_path, m_defaultPath);
@@ -258,17 +258,18 @@ void McuPackage::setAutomaticKitCreationEnabled(const bool enabled)
void McuPackage::updatePath()
{
- m_path = m_fileChooser->rawPath();
+ m_path = m_fileChooser->rawFilePath();
m_fileChooser->lineEdit()->button(FancyLineEdit::Right)->setEnabled(m_path != m_defaultPath);
updateStatus();
}
void McuPackage::updateStatus()
{
- bool validPath = !m_path.isEmpty() && FilePath::fromString(m_path).exists();
- const FilePath detectionPath = FilePath::fromString(basePath() + "/" + m_detectionPath);
+ bool validPath = !m_path.isEmpty() && m_path.exists();
+ const FilePath detectionPath = basePath() / m_detectionPath;
const bool validPackage = m_detectionPath.isEmpty() || detectionPath.exists();
- m_detectedVersion = validPath && validPackage && m_versionDetector ? m_versionDetector->parseVersion(basePath()) : QString();
+ m_detectedVersion = validPath && validPackage && m_versionDetector
+ ? m_versionDetector->parseVersion(basePath().toString()) : QString();
const bool validVersion = m_detectedVersion.isEmpty() ||
m_versions.isEmpty() || m_versions.contains(m_detectedVersion);
@@ -293,7 +294,7 @@ void McuPackage::updateStatusUi()
QString McuPackage::statusText() const
{
- const QString displayPackagePath = FilePath::fromString(m_path).toUserOutput();
+ const QString displayPackagePath = m_path.toUserOutput();
const QString displayVersions = QStringList(m_versions.toList()).join(" or ");
const QString displayRequiredPath = QString("%1 %2").arg(
FilePath::fromString(m_detectionPath).toUserOutput(),
@@ -339,7 +340,7 @@ QString McuPackage::statusText() const
}
McuToolChainPackage::McuToolChainPackage(const QString &label,
- const QString &defaultPath,
+ const FilePath &defaultPath,
const QString &detectionPath,
const QString &settingsKey,
McuToolChainPackage::Type type,
@@ -447,11 +448,9 @@ ToolChain *McuToolChainPackage::toolChain(Id language) const
else {
const QLatin1String compilerName(
language == ProjectExplorer::Constants::C_LANGUAGE_ID ? "gcc" : "g++");
- const FilePath compiler = FilePath::fromUserInput(
- HostOsInfo::withExecutableSuffix(
- path() + (
- m_type == TypeArmGcc
- ? "/bin/arm-none-eabi-%1" : "/bar/foo-keil-%1")).arg(compilerName));
+ const QString comp = QLatin1String(m_type == TypeArmGcc ? "/bin/arm-none-eabi-%1" : "/bar/foo-keil-%1")
+ .arg(compilerName);
+ const FilePath compiler = path().pathAppended(comp).withExecutableSuffix();
tc = armGccToolChain(compiler, language);
}
@@ -479,11 +478,10 @@ QVariant McuToolChainPackage::debuggerId() const
{
using namespace Debugger;
- const FilePath command = FilePath::fromUserInput(
- HostOsInfo::withExecutableSuffix(path() + (
- m_type == TypeArmGcc
- ? "/bin/arm-none-eabi-gdb-py" : m_type == TypeIAR
- ? "../common/bin/CSpyBat" : "/bar/foo-keil-gdb")));
+ QString sub = QString::fromLatin1(m_type == TypeArmGcc ? "bin/arm-none-eabi-gdb-py"
+ : m_type == TypeIAR ? "../common/bin/CSpyBat" : "bar/foo-keil-gdb");
+
+ const FilePath command = path().pathAppended(sub).withExecutableSuffix();
const DebuggerItem *debugger = DebuggerItemManager::findByCommand(command);
QVariant debuggerId;
if (!debugger) {
@@ -591,7 +589,7 @@ McuSupportOptions::~McuSupportOptions()
void McuSupportOptions::populatePackagesAndTargets()
{
- setQulDir(FilePath::fromUserInput(qtForMCUsSdkPackage->path()));
+ setQulDir(qtForMCUsSdkPackage->path());
}
static FilePath qulDocsDir()
@@ -665,13 +663,12 @@ void McuSupportOptions::setQulDir(const FilePath &dir)
FilePath McuSupportOptions::qulDirFromSettings()
{
- return FilePath::fromUserInput(
- packagePathFromSettings(Constants::SETTINGS_KEY_PACKAGE_QT_FOR_MCUS_SDK,
- QSettings::UserScope));
+ return packagePathFromSettings(Constants::SETTINGS_KEY_PACKAGE_QT_FOR_MCUS_SDK,
+ QSettings::UserScope);
}
static void setKitProperties(const QString &kitName, Kit *k, const McuTarget *mcuTarget,
- const QString &sdkPath)
+ const FilePath &sdkPath)
{
using namespace Constants;
@@ -688,7 +685,7 @@ static void setKitProperties(const QString &kitName, Kit *k, const McuTarget *mc
if (mcuTarget->toolChainPackage()->isDesktopToolchain())
k->setDeviceTypeForIcon(DEVICE_TYPE);
k->setValue(QtSupport::SuppliesQtQuickImportPath::id(), true);
- k->setValue(QtSupport::KitQmlImportPath::id(), QVariant(sdkPath + "/include/qul"));
+ k->setValue(QtSupport::KitQmlImportPath::id(), sdkPath.pathAppended("include/qul").toVariant());
k->setValue(QtSupport::KitHasMergedHeaderPathsWithQmlImportPaths::id(), true);
QSet<Id> irrelevant = {
SysRootKitAspect::id(),
@@ -750,14 +747,13 @@ static void setKitEnvironment(Kit *k, const McuTarget *mcuTarget,
// feature of the run configuration. Otherwise, we just prepend the path, here.
if (mcuTarget->toolChainPackage()->isDesktopToolchain()
&& !CMakeProjectManager::CMakeToolManager::defaultCMakeTool()->hasFileApi())
- pathAdditions.append(QDir::toNativeSeparators(qtForMCUsSdkPackage->path() + "/bin"));
+ pathAdditions.append(qtForMCUsSdkPackage->path().pathAppended("bin").toUserOutput());
auto processPackage = [&pathAdditions, &changes](const McuPackage *package) {
if (package->addToPath())
- pathAdditions.append(QDir::toNativeSeparators(package->path()));
+ pathAdditions.append(package->path().toUserOutput());
if (!package->environmentVariableName().isEmpty())
- changes.append({package->environmentVariableName(),
- QDir::toNativeSeparators(package->path())});
+ changes.append({package->environmentVariableName(), package->path().toUserOutput()});
};
for (auto package : mcuTarget->packages())
processPackage(package);
@@ -808,7 +804,7 @@ static void updateKitEnvironment(Kit *k, const McuTarget *mcuTarget)
return item.name == varName;
});
const EnvironmentItem item = {package->environmentVariableName(),
- QDir::toNativeSeparators(package->path())};
+ package->path().toUserOutput()};
if (index != -1)
changes.replace(index, item);
else
@@ -819,7 +815,7 @@ static void updateKitEnvironment(Kit *k, const McuTarget *mcuTarget)
EnvironmentKitAspect::setEnvironmentChanges(k, changes);
}
-static void setKitCMakeOptions(Kit *k, const McuTarget* mcuTarget, const QString &qulDir)
+static void setKitCMakeOptions(Kit *k, const McuTarget* mcuTarget, const FilePath &qulDir)
{
using namespace CMakeProjectManager;
@@ -832,7 +828,7 @@ static void setKitCMakeOptions(Kit *k, const McuTarget* mcuTarget, const QString
}
if (!mcuTarget->toolChainPackage()->isDesktopToolchain()) {
- const FilePath cMakeToolchainFile = FilePath::fromString(qulDir + "/lib/cmake/Qul/toolchain/"
+ const FilePath cMakeToolchainFile = qulDir.pathAppended("lib/cmake/Qul/toolchain/"
+ mcuTarget->toolChainPackage()->cmakeToolChainFileName());
config.append(CMakeConfigItem(
@@ -844,7 +840,7 @@ static void setKitCMakeOptions(Kit *k, const McuTarget* mcuTarget, const QString
}
}
- const FilePath generatorsPath = FilePath::fromString(qulDir + "/lib/cmake/Qul/QulGenerators.cmake");
+ const FilePath generatorsPath = qulDir.pathAppended("/lib/cmake/Qul/QulGenerators.cmake");
config.append(CMakeConfigItem("QUL_GENERATORS",
generatorsPath.toString().toUtf8()));
if (!generatorsPath.exists()) {
@@ -947,7 +943,7 @@ QList<Kit *> McuSupportOptions::kitsWithMismatchedDependencies(const McuTarget *
EnvironmentKitAspect::environmentChanges(kit)));
return Utils::anyOf(mcuTarget->packages(), [&environment](const McuPackage *package) {
return !package->environmentVariableName().isEmpty() &&
- environment.value(package->environmentVariableName()) != QDir::toNativeSeparators(package->path());
+ environment.value(package->environmentVariableName()) != package->path().toUserOutput();
});
});
}
@@ -1003,13 +999,13 @@ QVersionNumber McuSupportOptions::kitQulVersion(const Kit *kit)
.toString());
}
-QString kitDependencyPath(const Kit *kit, const QString &variableName)
+static FilePath kitDependencyPath(const Kit *kit, const QString &variableName)
{
for (const NameValueItem &nameValueItem : EnvironmentKitAspect::environmentChanges(kit)) {
if (nameValueItem.name == variableName)
- return nameValueItem.value;
+ return FilePath::fromUserInput(nameValueItem.value);
}
- return QString();
+ return FilePath();
}
bool McuSupportOptions::kitUpToDate(const Kit *kit, const McuTarget *mcuTarget,
@@ -1052,13 +1048,13 @@ void McuSupportOptions::createAutomaticKits()
const QString displayPath = FilePath::fromString(qtForMCUsPackage->detectionPath())
.toUserOutput();
printMessage(tr("Path %1 exists, but does not contain %2.")
- .arg(qtForMCUsPackage->path(), displayPath),
+ .arg(qtForMCUsPackage->path().toUserOutput(), displayPath),
true);
break;
}
case McuPackage::InvalidPath: {
printMessage(tr("Path %1 does not exist. Add the path in Tools > Options > Devices > MCU.")
- .arg(qtForMCUsPackage->path()),
+ .arg(qtForMCUsPackage->path().toUserOutput()),
true);
break;
}
@@ -1079,7 +1075,7 @@ void McuSupportOptions::createAutomaticKits()
return;
}
- auto dir = FilePath::fromUserInput(qtForMCUsPackage->path());
+ FilePath dir = qtForMCUsPackage->path();
QVector<McuPackage*> packages;
QVector<McuTarget*> mcuTargets;
Sdk::targetsAndPackages(dir, &packages, &mcuTargets);
@@ -1131,7 +1127,7 @@ void McuSupportOptions::upgradeKits(UpgradeOption upgradeOption)
auto qtForMCUsPackage = Sdk::createQtForMCUsPackage();
- auto dir = FilePath::fromUserInput(qtForMCUsPackage->path());
+ auto dir = qtForMCUsPackage->path();
QVector<McuPackage*> packages;
QVector<McuTarget*> mcuTargets;
Sdk::targetsAndPackages(dir, &packages, &mcuTargets);
@@ -1169,7 +1165,7 @@ void McuSupportOptions::fixKitsDependencies()
{
auto qtForMCUsPackage = Sdk::createQtForMCUsPackage();
- auto dir = FilePath::fromUserInput(qtForMCUsPackage->path());
+ FilePath dir = qtForMCUsPackage->path();
QVector<McuPackage*> packages;
QVector<McuTarget*> mcuTargets;
Sdk::targetsAndPackages(dir, &packages, &mcuTargets);
@@ -1245,7 +1241,7 @@ void McuSupportOptions::fixExistingKits()
auto qtForMCUsPackage = Sdk::createQtForMCUsPackage();
qtForMCUsPackage->updateStatus();
if (qtForMCUsPackage->validStatus()) {
- auto dir = FilePath::fromUserInput(qtForMCUsPackage->path());
+ FilePath dir = qtForMCUsPackage->path();
QVector<McuPackage*> packages;
QVector<McuTarget*> mcuTargets;
Sdk::targetsAndPackages(dir, &packages, &mcuTargets);
diff --git a/src/plugins/mcusupport/mcusupportoptions.h b/src/plugins/mcusupport/mcusupportoptions.h
index f70722412d..346c2b9de5 100644
--- a/src/plugins/mcusupport/mcusupportoptions.h
+++ b/src/plugins/mcusupport/mcusupportoptions.h
@@ -66,15 +66,15 @@ public:
ValidPackage
};
- McuPackage(const QString &label, const QString &defaultPath,
+ McuPackage(const QString &label, const Utils::FilePath &defaultPath,
const QString &detectionPath, const QString &settingsKey,
const McuPackageVersionDetector *versionDetector = nullptr);
virtual ~McuPackage() = default;
- QString basePath() const;
- QString path() const;
+ Utils::FilePath basePath() const;
+ Utils::FilePath path() const;
QString label() const;
- QString defaultPath() const;
+ Utils::FilePath defaultPath() const;
QString detectionPath() const;
QString statusText() const;
void updateStatus();
@@ -110,12 +110,12 @@ private:
Utils::InfoLabel *m_infoLabel = nullptr;
const QString m_label;
- const QString m_defaultPath;
+ const Utils::FilePath m_defaultPath;
const QString m_detectionPath;
const QString m_settingsKey;
const McuPackageVersionDetector *m_versionDetector;
- QString m_path;
+ Utils::FilePath m_path;
QString m_relativePathModifier; // relative path to m_path to be returned by path()
QString m_detectedVersion;
QVector<QString> m_versions;
@@ -142,7 +142,7 @@ public:
};
McuToolChainPackage(const QString &label,
- const QString &defaultPath,
+ const Utils::FilePath &defaultPath,
const QString &detectionPath,
const QString &settingsKey,
Type type,
diff --git a/src/plugins/mcusupport/mcusupportoptionspage.cpp b/src/plugins/mcusupport/mcusupportoptionspage.cpp
index 9ccffd63e0..71eff41c24 100644
--- a/src/plugins/mcusupport/mcusupportoptionspage.cpp
+++ b/src/plugins/mcusupport/mcusupportoptionspage.cpp
@@ -193,7 +193,7 @@ void McuSupportOptionsWidget::updateStatus()
m_mcuTargetsInfoLabel->setVisible(valid && m_options.mcuTargets.isEmpty());
if (m_mcuTargetsInfoLabel->isVisible()) {
m_mcuTargetsInfoLabel->setType(Utils::InfoLabel::NotOk);
- const auto sdkPath = Utils::FilePath::fromString(m_options.qtForMCUsSdkPackage->basePath());
+ const Utils::FilePath sdkPath = m_options.qtForMCUsSdkPackage->basePath();
QString deprecationMessage;
if (Sdk::checkDeprecatedSdkError(sdkPath, deprecationMessage))
m_mcuTargetsInfoLabel->setText(deprecationMessage);
diff --git a/src/plugins/mcusupport/mcusupportsdk.cpp b/src/plugins/mcusupport/mcusupportsdk.cpp
index b805b4a91a..291f0647b2 100644
--- a/src/plugins/mcusupport/mcusupportsdk.cpp
+++ b/src/plugins/mcusupport/mcusupportsdk.cpp
@@ -49,15 +49,14 @@ namespace McuSupport {
namespace Internal {
namespace Sdk {
-static QString findInProgramFiles(const QString &folder)
+static FilePath findInProgramFiles(const QString &folder)
{
for (auto envVar : {"ProgramFiles", "ProgramFiles(x86)", "ProgramW6432"}) {
if (!qEnvironmentVariableIsSet(envVar))
continue;
- const Utils::FilePath dir =
- Utils::FilePath::fromUserInput(qEnvironmentVariable(envVar) + "/" + folder);
+ const FilePath dir = FilePath::fromUserInput(qEnvironmentVariable(envVar)) / folder;
if (dir.exists())
- return dir.toString();
+ return dir;
}
return {};
}
@@ -66,7 +65,7 @@ McuPackage *createQtForMCUsPackage()
{
auto result = new McuPackage(
McuPackage::tr("Qt for MCUs SDK"),
- QDir::homePath(),
+ FileUtils::homePath(),
FilePath("bin/qmltocpp").withExecutableSuffix().toString(),
Constants::SETTINGS_KEY_PACKAGE_QT_FOR_MCUS_SDK);
result->setEnvironmentVariableName("Qul_DIR");
@@ -92,22 +91,21 @@ static McuToolChainPackage *createArmGccPackage()
{
const char envVar[] = "ARMGCC_DIR";
- QString defaultPath;
+ FilePath defaultPath;
if (qEnvironmentVariableIsSet(envVar))
- defaultPath = qEnvironmentVariable(envVar);
- if (defaultPath.isEmpty() && Utils::HostOsInfo::isWindowsHost()) {
- const QDir installDir(findInProgramFiles("/GNU Tools ARM Embedded/"));
+ defaultPath = FilePath::fromUserInput(qEnvironmentVariable(envVar));
+ if (defaultPath.isEmpty() && HostOsInfo::isWindowsHost()) {
+ const FilePath installDir = findInProgramFiles("GNU Tools ARM Embedded");
if (installDir.exists()) {
// If GNU Tools installation dir has only one sub dir,
// select the sub dir, otherwise the installation dir.
- const QFileInfoList subDirs =
- installDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
+ const FilePaths subDirs = installDir.dirEntries(QDir::Dirs | QDir::NoDotAndDotDot);
if (subDirs.count() == 1)
- defaultPath = subDirs.first().filePath() + '/';
+ defaultPath = subDirs.first();
}
}
if (defaultPath.isEmpty())
- defaultPath = QDir::homePath();
+ defaultPath = FileUtils::homePath();
const QString detectionPath = Utils::HostOsInfo::withExecutableSuffix("bin/arm-none-eabi-g++");
const auto versionDetector = new McuPackageExecutableVersionDetector(
@@ -131,8 +129,8 @@ static McuToolChainPackage *createGhsToolchainPackage()
{
const char envVar[] = "GHS_COMPILER_DIR";
- const QString defaultPath =
- qEnvironmentVariableIsSet(envVar) ? qEnvironmentVariable(envVar) : QDir::homePath();
+ const FilePath defaultPath = qEnvironmentVariableIsSet(envVar)
+ ? FilePath::fromUserInput(qEnvironmentVariable(envVar)) : FileUtils::homePath();
const auto versionDetector = new McuPackageExecutableVersionDetector(
Utils::HostOsInfo::withExecutableSuffix("as850"),
@@ -155,8 +153,8 @@ static McuToolChainPackage *createGhsArmToolchainPackage()
{
const char envVar[] = "GHS_ARM_COMPILER_DIR";
- const QString defaultPath =
- qEnvironmentVariableIsSet(envVar) ? qEnvironmentVariable(envVar) : QDir::homePath();
+ const FilePath defaultPath = qEnvironmentVariableIsSet(envVar)
+ ? FilePath::fromUserInput(qEnvironmentVariable(envVar)) : FileUtils::homePath();
const auto versionDetector = new McuPackageExecutableVersionDetector(
Utils::HostOsInfo::withExecutableSuffix("asarm"),
@@ -179,20 +177,20 @@ static McuToolChainPackage *createIarToolChainPackage()
{
const char envVar[] = "IAR_ARM_COMPILER_DIR";
- QString defaultPath;
+ FilePath defaultPath;
if (qEnvironmentVariableIsSet(envVar))
- defaultPath = qEnvironmentVariable(envVar);
+ defaultPath = FilePath::fromUserInput(qEnvironmentVariable(envVar));
else {
const ProjectExplorer::ToolChain *tc =
ProjectExplorer::ToolChainManager::toolChain([](const ProjectExplorer::ToolChain *t) {
return t->typeId() == BareMetal::Constants::IAREW_TOOLCHAIN_TYPEID;
});
if (tc) {
- const Utils::FilePath compilerExecPath = tc->compilerCommand();
- defaultPath = compilerExecPath.parentDir().parentDir().toString();
+ const FilePath compilerExecPath = tc->compilerCommand();
+ defaultPath = compilerExecPath.parentDir().parentDir();
}
else
- defaultPath = QDir::homePath();
+ defaultPath = FileUtils::homePath();
}
const QString detectionPath = Utils::HostOsInfo::withExecutableSuffix("bin/iccarm");
@@ -217,17 +215,17 @@ static McuPackage *createRGLPackage()
{
const char envVar[] = "RGL_DIR";
- QString defaultPath;
+ FilePath defaultPath;
if (qEnvironmentVariableIsSet(envVar)) {
- defaultPath = qEnvironmentVariable(envVar);
+ defaultPath = FilePath::fromUserInput(qEnvironmentVariable(envVar));
} else if (Utils::HostOsInfo::isWindowsHost()) {
- defaultPath = QDir::rootPath() + "Renesas_Electronics/D1x_RGL";
- if (QFileInfo::exists(defaultPath)) {
- const QFileInfoList subDirs =
- QDir(defaultPath).entryInfoList({QLatin1String("rgl_ghs_D1Mx_*")},
+ defaultPath = FilePath::fromUserInput(QDir::rootPath() + "Renesas_Electronics/D1x_RGL");
+ if (defaultPath.exists()) {
+ const FilePaths subDirs =
+ defaultPath.dirEntries({QLatin1String("rgl_ghs_D1Mx_*")},
QDir::Dirs | QDir::NoDotAndDotDot);
if (subDirs.count() == 1)
- defaultPath = subDirs.first().filePath() + '/';
+ defaultPath = subDirs.first();
}
}
@@ -242,15 +240,15 @@ static McuPackage *createRGLPackage()
static McuPackage *createStm32CubeProgrammerPackage()
{
- QString defaultPath = QDir::homePath();
- const QString cubePath = "/STMicroelectronics/STM32Cube/STM32CubeProgrammer/";
- if (Utils::HostOsInfo::isWindowsHost()) {
- const QString programPath = findInProgramFiles(cubePath);
+ FilePath defaultPath = FileUtils::homePath();
+ const QString cubePath = "STMicroelectronics/STM32Cube/STM32CubeProgrammer";
+ if (HostOsInfo::isWindowsHost()) {
+ const FilePath programPath = findInProgramFiles(cubePath);
if (!programPath.isEmpty())
defaultPath = programPath;
} else {
- const QString programPath = QDir::homePath() + cubePath;
- if (QFileInfo::exists(programPath))
+ const FilePath programPath = FileUtils::homePath() / cubePath;
+ if (programPath.exists())
defaultPath = programPath;
}
auto result = new McuPackage(
@@ -270,18 +268,18 @@ static McuPackage *createMcuXpressoIdePackage()
{
const char envVar[] = "MCUXpressoIDE_PATH";
- QString defaultPath;
+ FilePath defaultPath;
if (qEnvironmentVariableIsSet(envVar)) {
- defaultPath = qEnvironmentVariable(envVar);
- } else if (Utils::HostOsInfo::isWindowsHost()) {
- defaultPath = QDir::rootPath() + "nxp";
- if (QFileInfo::exists(defaultPath)) {
+ defaultPath = FilePath::fromUserInput(qEnvironmentVariable(envVar));
+ } else if (HostOsInfo::isWindowsHost()) {
+ defaultPath = FilePath::fromString(QDir::rootPath() + "nxp");
+ if (defaultPath.exists()) {
// If default dir has exactly one sub dir that could be the IDE path, pre-select that.
- const QFileInfoList subDirs =
- QDir(defaultPath).entryInfoList({QLatin1String("MCUXpressoIDE*")},
+ const FilePaths subDirs =
+ defaultPath.dirEntries({QLatin1String("MCUXpressoIDE*")},
QDir::Dirs | QDir::NoDotAndDotDot);
if (subDirs.count() == 1)
- defaultPath = subDirs.first().filePath() + '/';
+ defaultPath = subDirs.first();
}
} else {
defaultPath = "/usr/local/mcuxpressoide/";
@@ -301,21 +299,20 @@ static McuPackage *createCypressProgrammerPackage()
{
const char envVar[] = "CYPRESS_AUTO_FLASH_UTILITY_DIR";
- QString defaultPath;
+ FilePath defaultPath;
if (qEnvironmentVariableIsSet(envVar)) {
- defaultPath = qEnvironmentVariable(envVar);
- } else if (Utils::HostOsInfo::isWindowsHost()) {
- auto candidate = findInProgramFiles(QLatin1String("/Cypress/Cypress Auto Flash Utility 1.0/"));
- if (QFileInfo::exists(candidate)) {
+ defaultPath = FilePath::fromUserInput(qEnvironmentVariable(envVar));
+ } else if (HostOsInfo::isWindowsHost()) {
+ FilePath candidate = findInProgramFiles("Cypress/Cypress Auto Flash Utility 1.0");
+ if (candidate.exists()) {
defaultPath = candidate;
}
} else {
- defaultPath = QLatin1String("/usr");
+ defaultPath = "/usr";
}
- if (defaultPath.isEmpty()) {
- defaultPath = QDir::homePath();
- }
+ if (defaultPath.isEmpty())
+ defaultPath = FileUtils::homePath();
auto result = new McuPackage(
"Cypress Auto Flash Utility",
@@ -378,18 +375,16 @@ static McuPackage *createBoardSdkPackage(const McuTargetDescription& desc)
};
const QString sdkName = desc.boardSdkName.isEmpty() ? generateSdkName(desc.boardSdkEnvVar) : desc.boardSdkName;
- const QString defaultPath = [&] {
+ const FilePath defaultPath = [&] {
const auto envVar = desc.boardSdkEnvVar.toLatin1();
- if (qEnvironmentVariableIsSet(envVar)) {
- return qEnvironmentVariable(envVar);
- }
+ if (qEnvironmentVariableIsSet(envVar))
+ return FilePath::fromUserInput(qEnvironmentVariable(envVar));
if (!desc.boardSdkDefaultPath.isEmpty()) {
- QString defaultPath = QDir::rootPath() + desc.boardSdkDefaultPath;
- if (QFileInfo::exists(defaultPath)) {
+ FilePath defaultPath = FilePath::fromUserInput(QDir::rootPath() + desc.boardSdkDefaultPath);
+ if (defaultPath.exists())
return defaultPath;
- }
}
- return QDir::homePath();
+ return FileUtils::homePath();
}();
const auto versionDetector = generatePackageVersionDetector(desc.boardSdkEnvVar);
@@ -404,18 +399,18 @@ static McuPackage *createBoardSdkPackage(const McuTargetDescription& desc)
return result;
}
-static McuPackage *createFreeRTOSSourcesPackage(const QString &envVar, const QString &boardSdkDir,
+static McuPackage *createFreeRTOSSourcesPackage(const QString &envVar, const FilePath &boardSdkDir,
const QString &freeRTOSBoardSdkSubDir)
{
const QString envVarPrefix = envVar.chopped(int(strlen("_FREERTOS_DIR")));
- QString defaultPath;
+ FilePath defaultPath;
if (qEnvironmentVariableIsSet(envVar.toLatin1()))
- defaultPath = qEnvironmentVariable(envVar.toLatin1());
+ defaultPath = FilePath::fromUserInput(qEnvironmentVariable(envVar.toLatin1()));
else if (!boardSdkDir.isEmpty() && !freeRTOSBoardSdkSubDir.isEmpty())
- defaultPath = boardSdkDir + "/" + freeRTOSBoardSdkSubDir;
+ defaultPath = boardSdkDir / freeRTOSBoardSdkSubDir;
else
- defaultPath = QDir::homePath();
+ defaultPath = FileUtils::homePath();
auto result = new McuPackage(
QString::fromLatin1("FreeRTOS Sources (%1)").arg(envVarPrefix),
@@ -473,7 +468,7 @@ protected:
if (vendorPkgs.contains(desc.platformVendor))
required3rdPartyPkgs.push_back(vendorPkgs.value(desc.platformVendor));
- QString boardSdkDefaultPath;
+ FilePath boardSdkDefaultPath;
if (!desc.boardSdkEnvVar.isEmpty()) {
if (!boardSdkPkgs.contains(desc.boardSdkEnvVar)) {
auto boardSdkPkg = desc.boardSdkEnvVar != "RGL_DIR"
@@ -551,7 +546,7 @@ protected:
required3rdPartyPkgs.push_back(vendorPkgs.value(desc.platformVendor));
// Board SDK specific settings
- QString boardSdkDefaultPath;
+ FilePath boardSdkDefaultPath;
if (!desc.boardSdkEnvVar.isEmpty()) {
if (!boardSdkPkgs.contains(desc.boardSdkEnvVar)) {
auto boardSdkPkg = createBoardSdkPackage(desc);
diff --git a/src/plugins/projectexplorer/kitmanager.cpp b/src/plugins/projectexplorer/kitmanager.cpp
index 3f77fee414..1c338d730a 100644
--- a/src/plugins/projectexplorer/kitmanager.cpp
+++ b/src/plugins/projectexplorer/kitmanager.cpp
@@ -238,12 +238,15 @@ void KitManager::restoreKits()
kitsToCheck.clear();
// Remove replacement kits for which the original kit has turned up again.
- Utils::erase(resultList, [&resultList](const std::unique_ptr<Kit> &k) {
- return k->isReplacementKit()
- && contains(resultList, [&k](const std::unique_ptr<Kit> &other) {
- return other->id() == k->id() && other != k;
- });
- });
+ for (auto it = resultList.begin(); it != resultList.end();) {
+ const auto &k = *it;
+ if (k->isReplacementKit() && contains(resultList, [&k](const std::unique_ptr<Kit> &other) {
+ return other->id() == k->id() && other != k; })) {
+ it = resultList.erase(it);
+ } else {
+ ++it;
+ }
+ }
static const auto kitMatchesAbiList = [](const Kit *kit, const Abis &abis) {
const QList<ToolChain *> toolchains = ToolChainKitAspect::toolChains(kit);
diff --git a/src/plugins/projectexplorer/miniprojecttargetselector.cpp b/src/plugins/projectexplorer/miniprojecttargetselector.cpp
index b73d12c578..44fc5ae511 100644
--- a/src/plugins/projectexplorer/miniprojecttargetselector.cpp
+++ b/src/plugins/projectexplorer/miniprojecttargetselector.cpp
@@ -238,6 +238,18 @@ public:
protected:
void resetOptimalWidth()
{
+ if (m_resetScheduled)
+ return;
+ m_resetScheduled = true;
+ QMetaObject::invokeMethod(this, &SelectorView::doResetOptimalWidth, Qt::QueuedConnection);
+ }
+
+private:
+ void keyPressEvent(QKeyEvent *event) override;
+ void keyReleaseEvent(QKeyEvent *event) override;
+ void doResetOptimalWidth()
+ {
+ m_resetScheduled = false;
int width = 0;
QFontMetrics fn(font());
theModel()->forItemsAtLevel<1>([this, &width, &fn](const GenericItem *item) {
@@ -246,12 +258,9 @@ protected:
setOptimalWidth(width);
}
-private:
- void keyPressEvent(QKeyEvent *event) override;
- void keyReleaseEvent(QKeyEvent *event) override;
-
int m_maxCount = 0;
int m_optimalWidth = 0;
+ bool m_resetScheduled = false;
};
class ProjectListView : public SelectorView
diff --git a/src/plugins/qbsprojectmanager/qbsnodes.cpp b/src/plugins/qbsprojectmanager/qbsnodes.cpp
index 2a155d2128..2d6049b570 100644
--- a/src/plugins/qbsprojectmanager/qbsnodes.cpp
+++ b/src/plugins/qbsprojectmanager/qbsnodes.cpp
@@ -218,7 +218,7 @@ QVariant QbsProductNode::data(Id role) const
return m_productData.value("module-properties").toObject()
.value("Qt.core.enableKeywords").toBool();
- if (role == Android::Constants::ANDROID_ABIS) {
+ if (role == Android::Constants::AndroidAbis) {
// Try using qbs.architectures
QStringList qbsAbis;
QMap<QString, QString> archToAbi {
diff --git a/src/plugins/qmakeprojectmanager/profilecompletionassist.cpp b/src/plugins/qmakeprojectmanager/profilecompletionassist.cpp
index 419b4044de..ff08bb61b9 100644
--- a/src/plugins/qmakeprojectmanager/profilecompletionassist.cpp
+++ b/src/plugins/qmakeprojectmanager/profilecompletionassist.cpp
@@ -33,21 +33,22 @@ const TextEditor::Keywords &QmakeProjectManager::Internal::qmakeKeywords()
{
static TextEditor::Keywords keywords(
QStringList{ // variables
- "ANDROID_ABIS",
+ Android::Constants::ANDROID_ABI,
+ Android::Constants::ANDROID_ABIS,
"ANDROID_API_VERSION",
- QLatin1String(Android::Constants::ANDROID_APPLICATION_ARGUMENTS),
+ Android::Constants::ANDROID_APPLICATION_ARGUMENTS,
"ANDROID_BUNDLED_JAR_DEPENDENCIES",
"ANDROID_DEPLOYMENT_DEPENDENCIES",
- QLatin1String(Android::Constants::ANDROID_DEPLOYMENT_SETTINGS_FILE),
- QLatin1String(Android::Constants::ANDROID_EXTRA_LIBS),
+ Android::Constants::ANDROID_DEPLOYMENT_SETTINGS_FILE,
+ Android::Constants::ANDROID_EXTRA_LIBS,
"ANDROID_EXTRA_PLUGINS",
"ANDROID_FEATURES",
"ANDROID_LIB_DEPENDENCIES",
"ANDROID_MIN_SDK_VERSION",
- QLatin1String(Android::Constants::ANDROID_PACKAGE_SOURCE_DIR),
+ Android::Constants::ANDROID_PACKAGE_SOURCE_DIR,
"ANDROID_PERMISSIONS",
"ANDROID_TARGET_SDK_VERSION",
- "ANDROID_TARGET_ARCH",
+ Android::Constants::ANDROID_TARGET_ARCH,
"ANDROID_VERSION_CODE",
"ANDROID_VERSION_NAME",
"ARGC",
diff --git a/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp b/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp
index beef0a160d..6b28aa6b44 100644
--- a/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp
@@ -157,7 +157,7 @@ QmakeBuildConfiguration::QmakeBuildConfiguration(Target *target, Utils::Id id)
if (DeviceTypeKitAspect::deviceTypeId(target->kit())
== Android::Constants::ANDROID_DEVICE_TYPE) {
- buildSteps()->appendStep(Android::Constants::ANDROID_PACKAGE_INSTALLATION_STEP_ID);
+ buildSteps()->appendStep(Android::Constants::ANDROID_PACKAGE_INSTALL_STEP_ID);
buildSteps()->appendStep(Android::Constants::ANDROID_BUILD_APK_ID);
}
diff --git a/src/plugins/qmakeprojectmanager/qmakenodes.cpp b/src/plugins/qmakeprojectmanager/qmakenodes.cpp
index 1214b79d64..46e95f0a68 100644
--- a/src/plugins/qmakeprojectmanager/qmakenodes.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakenodes.cpp
@@ -364,16 +364,16 @@ QStringList QmakeProFileNode::targetApplications() const
QVariant QmakeProFileNode::data(Utils::Id role) const
{
- if (role == Android::Constants::ANDROID_ABIS)
+ if (role == Android::Constants::AndroidAbis)
return variableValue(Variable::AndroidAbis);
+ if (role == Android::Constants::AndroidAbi)
+ return singleVariableValue(Variable::AndroidAbi);
+ if (role == Android::Constants::AndroidExtraLibs)
+ return variableValue(Variable::AndroidExtraLibs);
if (role == Android::Constants::AndroidPackageSourceDir)
return singleVariableValue(Variable::AndroidPackageSourceDir);
if (role == Android::Constants::AndroidDeploySettingsFile)
return singleVariableValue(Variable::AndroidDeploySettingsFile);
- if (role == Android::Constants::AndroidExtraLibs)
- return variableValue(Variable::AndroidExtraLibs);
- if (role == Android::Constants::AndroidArch)
- return singleVariableValue(Variable::AndroidArch);
if (role == Android::Constants::AndroidSoLibPath) {
TargetInformation info = targetInformation();
QStringList res = {info.buildDir.toString()};
@@ -431,8 +431,9 @@ bool QmakeProFileNode::setData(Utils::Id role, const QVariant &value) const
if (Target *target = m_buildSystem->target()) {
QtSupport::BaseQtVersion *version = QtSupport::QtKitAspect::qtVersion(target->kit());
if (version && !version->supportsMultipleQtAbis()) {
- const QString arch = pro->singleVariableValue(Variable::AndroidArch);
- scope = "contains(ANDROID_TARGET_ARCH," + arch + ')';
+ const QString arch = pro->singleVariableValue(Variable::AndroidAbi);
+ scope = QString("contains(%1,%2)").arg(Android::Constants::ANDROID_TARGET_ARCH)
+ .arg(arch);
flags |= QmakeProjectManager::Internal::ProWriter::MultiLine;
}
}
@@ -443,7 +444,7 @@ bool QmakeProFileNode::setData(Utils::Id role, const QVariant &value) const
if (role == Android::Constants::AndroidPackageSourceDir)
return pro->setProVariable(QLatin1String(Android::Constants::ANDROID_PACKAGE_SOURCE_DIR),
{value.toString()}, scope, flags);
- if (role == Android::Constants::ANDROID_APPLICATION_ARGUMENTS)
+ if (role == Android::Constants::AndroidApplicationArgs)
return pro->setProVariable(QLatin1String(Android::Constants::ANDROID_APPLICATION_ARGUMENTS),
{value.toString()}, scope, flags);
diff --git a/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp b/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp
index 6cc4634375..f64aecd296 100644
--- a/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp
@@ -1562,11 +1562,11 @@ QmakeEvalResult *QmakeProFile::evaluate(const QmakeEvalInput &input)
= exactReader->values(QLatin1String("TARGET_VERSION_EXT"));
result->newVarValues[Variable::StaticLibExtension] = exactReader->values(QLatin1String("QMAKE_EXTENSION_STATICLIB"));
result->newVarValues[Variable::ShLibExtension] = exactReader->values(QLatin1String("QMAKE_EXTENSION_SHLIB"));
- result->newVarValues[Variable::AndroidArch] = exactReader->values(QLatin1String("ANDROID_TARGET_ARCH"));
+ result->newVarValues[Variable::AndroidAbi] = exactReader->values(QLatin1String(Android::Constants::ANDROID_TARGET_ARCH));
result->newVarValues[Variable::AndroidDeploySettingsFile] = exactReader->values(QLatin1String(Android::Constants::ANDROID_DEPLOYMENT_SETTINGS_FILE));
result->newVarValues[Variable::AndroidPackageSourceDir] = exactReader->values(QLatin1String(Android::Constants::ANDROID_PACKAGE_SOURCE_DIR));
- result->newVarValues[Variable::AndroidAbis] = exactReader->values(QLatin1String("ANDROID_ABIS"));
- result->newVarValues[Variable::AndroidApplicationArguments] = exactReader->values(QLatin1String(Android::Constants::ANDROID_APPLICATION_ARGUMENTS));
+ result->newVarValues[Variable::AndroidAbis] = exactReader->values(QLatin1String(Android::Constants::ANDROID_ABIS));
+ result->newVarValues[Variable::AndroidApplicationArgs] = exactReader->values(QLatin1String(Android::Constants::ANDROID_APPLICATION_ARGUMENTS));
result->newVarValues[Variable::AndroidExtraLibs] = exactReader->values(QLatin1String(Android::Constants::ANDROID_EXTRA_LIBS));
result->newVarValues[Variable::AppmanPackageDir] = exactReader->values(QLatin1String("AM_PACKAGE_DIR"));
result->newVarValues[Variable::AppmanManifest] = exactReader->values(QLatin1String("AM_MANIFEST"));
diff --git a/src/plugins/qmakeprojectmanager/qmakeparsernodes.h b/src/plugins/qmakeprojectmanager/qmakeparsernodes.h
index fc90e0759d..cfa86b181f 100644
--- a/src/plugins/qmakeprojectmanager/qmakeparsernodes.h
+++ b/src/plugins/qmakeprojectmanager/qmakeparsernodes.h
@@ -96,12 +96,12 @@ enum class Variable {
TargetVersionExt,
StaticLibExtension,
ShLibExtension,
- AndroidArch,
- AndroidDeploySettingsFile,
+ AndroidAbi,
AndroidAbis,
+ AndroidDeploySettingsFile,
AndroidPackageSourceDir,
AndroidExtraLibs,
- AndroidApplicationArguments,
+ AndroidApplicationArgs,
AppmanPackageDir,
AppmanManifest,
IsoIcons,
diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.cpp b/src/plugins/qmakeprojectmanager/qmakeproject.cpp
index 793ec73ba8..54395bfc37 100644
--- a/src/plugins/qmakeprojectmanager/qmakeproject.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakeproject.cpp
@@ -674,8 +674,9 @@ void QmakeBuildSystem::asyncUpdate()
connect(watcher, &QFutureWatcher<void>::canceled, this, [this, watcher] {
if (!m_qmakeGlobals)
return;
- watcher->disconnect();
m_qmakeGlobals->killProcesses();
+ watcher->disconnect();
+ watcher->deleteLater();
});
connect(watcher, &QFutureWatcher<void>::finished, this, [watcher] {
watcher->disconnect();
diff --git a/src/plugins/qmakeprojectmanager/qmakestep.cpp b/src/plugins/qmakeprojectmanager/qmakestep.cpp
index 7cc65532af..9e7c7f2230 100644
--- a/src/plugins/qmakeprojectmanager/qmakestep.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakestep.cpp
@@ -632,7 +632,7 @@ void QMakeStep::abisChanged()
if (BaseQtVersion *qtVersion = QtKitAspect::qtVersion(target()->kit())) {
if (qtVersion->hasAbi(Abi::LinuxOS, Abi::AndroidLinuxFlavor)) {
- const QString prefix = "ANDROID_ABIS=";
+ const QString prefix = QString("%1=").arg(Android::Constants::ANDROID_ABIS);
QStringList args = m_extraArgs;
for (auto it = args.begin(); it != args.end(); ++it) {
if (it->startsWith(prefix)) {
@@ -643,8 +643,7 @@ void QMakeStep::abisChanged()
if (!m_selectedAbis.isEmpty())
args << prefix + '"' + m_selectedAbis.join(' ') + '"';
setExtraArguments(args);
-
- buildSystem()->setProperty(Android::Constants::ANDROID_ABIS, m_selectedAbis);
+ buildSystem()->setProperty(Android::Constants::AndroidAbis, m_selectedAbis);
} else if (qtVersion->hasAbi(Abi::DarwinOS) && !isIos(target()->kit())) {
const QString prefix = "QMAKE_APPLE_DEVICE_ARCHS=";
QStringList args = m_extraArgs;
diff --git a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h
index 4fd6fdf159..49505d2ca7 100644
--- a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h
+++ b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h
@@ -222,6 +222,9 @@ const char addImagesDisplayString[] = QT_TRANSLATE_NOOP("QmlDesignerAddResources
const char addFontsDisplayString[] = QT_TRANSLATE_NOOP("QmlDesignerAddResources", "Font Files");
const char addSoundsDisplayString[] = QT_TRANSLATE_NOOP("QmlDesignerAddResources", "Sound Files");
const char addShadersDisplayString[] = QT_TRANSLATE_NOOP("QmlDesignerAddResources", "Shader Files");
+const char add3DAssetsDisplayString[] = QT_TRANSLATE_NOOP("QmlDesignerAddResources", "3D Assets");
+const char addQt3DSPresentationsDisplayString[] = QT_TRANSLATE_NOOP("QmlDesignerAddResources",
+ "Qt 3D Studio Presentations");
const char addCustomEffectDialogDisplayString[] = QT_TRANSLATE_NOOP("QmlDesignerAddResources", "Add Custom Effect");
diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp
index d452889363..8ca977e742 100644
--- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp
@@ -262,7 +262,7 @@ bool DesignerActionManager::externalDragHasSupportedAssets(const QMimeData *mime
return false;
}
-void DesignerActionManager::handleExternalAssetsDrop(const QMimeData *mimeData) const
+QHash<QString, QStringList> DesignerActionManager::handleExternalAssetsDrop(const QMimeData *mimeData) const
{
const QList<AddResourceHandler> handlers = addResourceHandler();
// create suffix to categry and category to operation hashes
@@ -283,13 +283,19 @@ void DesignerActionManager::handleExternalAssetsDrop(const QMimeData *mimeData)
categoryFiles[category].append(url.toLocalFile());
}
+ QHash<QString, QStringList> addedCategoryFiles;
+
// run operations
const QStringList categories = categoryFiles.keys();
for (const QString &category : categories) {
AddResourceOperation operation = categoryOperation.value(category);
QStringList files = categoryFiles.value(category);
- operation(files, {});
+ bool success = operation(files, {});
+ if (success)
+ addedCategoryFiles.insert(category, files);
}
+
+ return addedCategoryFiles;
}
class VisiblityModelNodeAction : public ModelNodeContextMenuAction
diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h
index e0fe93f601..60e2c7562f 100644
--- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h
+++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h
@@ -137,7 +137,7 @@ public:
bool hasModelNodePreviewHandler(const ModelNode &node) const;
ModelNodePreviewImageOperation modelNodePreviewOperation(const ModelNode &node) const;
bool externalDragHasSupportedAssets(const QMimeData *data) const;
- void handleExternalAssetsDrop(const QMimeData *data) const;
+ QHash<QString, QStringList> handleExternalAssetsDrop(const QMimeData *data) const;
private:
void addTransitionEffectAction(const TypeName &typeName);
diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp
index 81066504de..ab2853354c 100644
--- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp
@@ -995,9 +995,8 @@ Utils::FilePath projectFilePath()
static bool addFilesToProject(const QStringList &fileNames, const QString &defaultDirectory)
{
QString directory = AddImagesDialog::getDirectory(fileNames, defaultDirectory);
-
if (directory.isEmpty())
- return true;
+ return false;
bool allSuccessful = true;
QList<QPair<QString, QString>> copyList;
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp
index 025b622789..835ba07b5b 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp
@@ -23,14 +23,15 @@
**
****************************************************************************/
-#include "edit3dwidget.h"
-#include "edit3dview.h"
-#include "edit3dcanvas.h"
-#include "edit3dactions.h"
-
-#include "qmldesignerplugin.h"
#include "designersettings.h"
+#include "edit3dactions.h"
+#include "edit3dcanvas.h"
+#include "edit3dview.h"
+#include "edit3dwidget.h"
+#include "metainfo.h"
#include "qmldesignerconstants.h"
+#include "qmldesignerplugin.h"
+#include "qmlvisualnode.h"
#include "viewmanager.h"
#include <coreplugin/actionmanager/actionmanager.h>
@@ -174,7 +175,20 @@ void Edit3DWidget::dropEvent(QDropEvent *dropEvent)
{
const DesignerActionManager &actionManager = QmlDesignerPlugin::instance()
->viewManager().designerActionManager();
- actionManager.handleExternalAssetsDrop(dropEvent->mimeData());
+ QHash<QString, QStringList> addedAssets = actionManager.handleExternalAssetsDrop(dropEvent->mimeData());
+
+ // add 3D assets to 3d editor (QtQuick3D import will be added if missing)
+ ItemLibraryInfo *itemLibInfo = m_view->model()->metaInfo().itemLibraryInfo();
+
+ const QStringList added3DAssets = addedAssets.value(ComponentCoreConstants::add3DAssetsDisplayString);
+ for (const QString &assetPath : added3DAssets) {
+ QString fileName = QFileInfo(assetPath).baseName();
+ fileName = fileName.at(0).toUpper() + fileName.mid(1); // capitalize first letter
+ QString type = QString("Quick3DAssets.%1.%1").arg(fileName);
+ QList<ItemLibraryEntry> entriesForType = itemLibInfo->entriesForType(type.toLatin1());
+ if (!entriesForType.isEmpty()) // should always be true, but just in case
+ QmlVisualNode::createQml3DNode(view(), entriesForType.at(0), m_canvas->activeScene()).modelNode();
+ }
}
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp
index 8200558d18..4f000e1295 100644
--- a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp
@@ -99,8 +99,7 @@ FormEditorItem::FormEditorItem(const QmlItemNode &qmlItemNode, FormEditorScene*
m_borderWidth(1.0),
m_highlightBoundingRect(false),
m_blurContent(false),
- m_isContentVisible(true),
- m_isFormEditorVisible(true)
+ m_isContentVisible(true)
{
setCacheMode(QGraphicsItem::NoCache);
setup();
@@ -208,17 +207,6 @@ bool FormEditorItem::isContentVisible() const
return m_isContentVisible;
}
-
-bool FormEditorItem::isFormEditorVisible() const
-{
- return m_isFormEditorVisible;
-}
-void FormEditorItem::setFormEditorVisible(bool isVisible)
-{
- m_isFormEditorVisible = isVisible;
- setVisible(isVisible);
-}
-
QPointF FormEditorItem::center() const
{
return mapToScene(qmlItemNode().instanceBoundingRect().center());
diff --git a/src/plugins/qmldesigner/components/formeditor/formeditoritem.h b/src/plugins/qmldesigner/components/formeditor/formeditoritem.h
index 769488aa4a..2213f42a66 100644
--- a/src/plugins/qmldesigner/components/formeditor/formeditoritem.h
+++ b/src/plugins/qmldesigner/components/formeditor/formeditoritem.h
@@ -104,9 +104,6 @@ public:
void setContentVisible(bool visible);
bool isContentVisible() const;
- bool isFormEditorVisible() const;
- void setFormEditorVisible(bool isVisible);
-
QPointF center() const;
qreal selectionWeigth(const QPointF &point, int iteration);
@@ -152,7 +149,6 @@ private: // variables
bool m_highlightBoundingRect;
bool m_blurContent;
bool m_isContentVisible;
- bool m_isFormEditorVisible;
};
class FormEditorFlowItem : public FormEditorItem
diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp
index 0ee760b9a1..10e1373dde 100644
--- a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp
@@ -91,6 +91,9 @@ void FormEditorView::modelAttached(Model *model)
//This function does the setup of the initial FormEditorItem tree in the scene
void FormEditorView::setupFormEditorItemTree(const QmlItemNode &qmlItemNode)
{
+ if (!qmlItemNode.hasFormEditorItem())
+ return;
+
if (qmlItemNode.isFlowTransition()) {
m_scene->addFormEditorItem(qmlItemNode, FormEditorScene::FlowTransition);
if (qmlItemNode.hasNodeParent())
@@ -199,16 +202,6 @@ void FormEditorView::removeNodeFromScene(const QmlItemNode &qmlItemNode)
m_currentTool->itemsAboutToRemoved(removedItemList);
}
-void FormEditorView::hideNodeFromScene(const QmlItemNode &qmlItemNode)
-{
- if (FormEditorItem *item = m_scene->itemForQmlItemNode(qmlItemNode)) {
- QList<FormEditorItem*> removedItems = scene()->itemsForQmlItemNodes(qmlItemNode.allSubModelNodes());
- removedItems.append(item);
- m_currentTool->itemsAboutToRemoved(removedItems);
- item->setFormEditorVisible(false);
- }
-}
-
void FormEditorView::createFormEditorWidget()
{
m_formEditorWidget = QPointer<FormEditorWidget>(new FormEditorWidget(this));
@@ -248,10 +241,7 @@ void FormEditorView::temporaryBlockView(int duration)
void FormEditorView::nodeCreated(const ModelNode &node)
{
- //If the node has source for components/custom parsers we ignore it.
- if (QmlItemNode::isValidQmlItemNode(node) && node.nodeSourceType() == ModelNode::NodeWithoutSource) //only setup QmlItems
- setupFormEditorItemTree(QmlItemNode(node));
- else if (QmlVisualNode::isFlowTransition(node))
+ if (QmlVisualNode::isFlowTransition(node))
setupFormEditorItemTree(QmlItemNode(node));
}
@@ -349,8 +339,26 @@ static inline bool hasNodeSourceParent(const ModelNode &node)
void FormEditorView::nodeReparented(const ModelNode &node, const NodeAbstractProperty &/*newPropertyParent*/, const NodeAbstractProperty &/*oldPropertyParent*/, AbstractView::PropertyChangeFlags /*propertyChange*/)
{
- if (hasNodeSourceParent(node))
- hideNodeFromScene(node);
+ // If node is not connected to scene root, don't do anything yet to avoid duplicated effort,
+ // as any removal or addition will remove or add descendants as well.
+ if (!node.isInHierarchy())
+ return;
+
+ QmlItemNode itemNode(node);
+ if (hasNodeSourceParent(node)) {
+ if (FormEditorItem *item = m_scene->itemForQmlItemNode(itemNode)) {
+ QList<FormEditorItem *> removed = scene()->itemsForQmlItemNodes(itemNode.allSubModelNodes());
+ removed.append(item);
+ m_currentTool->itemsAboutToRemoved(removed);
+ removeNodeFromScene(itemNode);
+ }
+ } else if (itemNode.isValid() && node.nodeSourceType() == ModelNode::NodeWithoutSource) {
+ if (!m_scene->itemForQmlItemNode(itemNode)) {
+ setupFormEditorItemTree(itemNode);
+ // Simulate selection change to refresh tools
+ selectedNodesChanged(selectedModelNodes(), {});
+ }
+ }
}
WidgetInfo FormEditorView::widgetInfo()
@@ -603,8 +611,7 @@ void FormEditorView::auxiliaryDataChanged(const ModelNode &node, const PropertyN
if (name == "invisible") {
if (FormEditorItem *item = scene()->itemForQmlItemNode(QmlItemNode(node))) {
bool isInvisible = data.toBool();
- if (item->isFormEditorVisible())
- item->setVisible(!isInvisible);
+ item->setVisible(!isInvisible);
ModelNode newNode(node);
if (isInvisible)
newNode.deselectNode();
diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorview.h b/src/plugins/qmldesigner/components/formeditor/formeditorview.h
index ba2b79df99..04b7d1e83e 100644
--- a/src/plugins/qmldesigner/components/formeditor/formeditorview.h
+++ b/src/plugins/qmldesigner/components/formeditor/formeditorview.h
@@ -144,7 +144,6 @@ protected:
private:
void setupFormEditorItemTree(const QmlItemNode &qmlItemNode);
void removeNodeFromScene(const QmlItemNode &qmlItemNode);
- void hideNodeFromScene(const QmlItemNode &qmlItemNode);
void createFormEditorWidget();
void temporaryBlockView(int duration = 1000);
void resetNodeInstanceView();
diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp
index 1f9fc4487b..6b81d1f92e 100644
--- a/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp
@@ -597,7 +597,14 @@ void FormEditorWidget::dropEvent(QDropEvent *dropEvent)
{
const DesignerActionManager &actionManager = QmlDesignerPlugin::instance()
->viewManager().designerActionManager();
- actionManager.handleExternalAssetsDrop(dropEvent->mimeData());
+ QHash<QString, QStringList> addedAssets = actionManager.handleExternalAssetsDrop(dropEvent->mimeData());
+
+ // add image assets to Form Editor
+ const QStringList addedImages = addedAssets.value(ComponentCoreConstants::addImagesDisplayString);
+ for (const QString &imgPath : addedImages) {
+ QmlItemNode::createQmlItemNodeFromImage(m_formEditorView, imgPath, {},
+ m_formEditorView->scene()->rootFormEditorItem()->qmlItemNode());
+ }
}
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/formeditor/selectiontool.cpp b/src/plugins/qmldesigner/components/formeditor/selectiontool.cpp
index 7230e97a1c..f240b32f76 100644
--- a/src/plugins/qmldesigner/components/formeditor/selectiontool.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/selectiontool.cpp
@@ -34,6 +34,8 @@
#include <nodemetainfo.h>
+#include <utils/algorithm.h>
+
#include <QGraphicsSceneMouseEvent>
#include <QDebug>
@@ -242,9 +244,23 @@ void SelectionTool::dragMoveEvent(const QList<QGraphicsItem*> &/*itemList*/, QGr
{
}
-void SelectionTool::itemsAboutToRemoved(const QList<FormEditorItem*> &/*itemList*/)
+void SelectionTool::itemsAboutToRemoved(const QList<FormEditorItem*> &itemList)
{
-
+ const QList<FormEditorItem *> current = items();
+
+ QList<FormEditorItem *> remaining = Utils::filtered(current, [&itemList](FormEditorItem *item) {
+ return !itemList.contains(item);
+ });
+
+ if (!remaining.isEmpty()) {
+ m_selectionIndicator.setItems(remaining);
+ m_resizeIndicator.setItems(remaining);
+ m_rotationIndicator.setItems(remaining);
+ m_anchorIndicator.setItems(remaining);
+ m_bindingIndicator.setItems(remaining);
+ } else {
+ clear();
+ }
}
void SelectionTool::clear()
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp
index 3bf7447a34..06b84d83f8 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp
@@ -240,35 +240,38 @@ void ItemLibraryView::updateImport3DSupport(const QVariantMap &supportMap)
DesignerActionManager *actionManager =
&QmlDesignerPlugin::instance()->viewManager().designerActionManager();
- // All things importable by QSSGAssetImportManager are considered to be in the same category
- // so we don't get multiple separate import dialogs when different file types are imported.
- const QString category = tr("3D Assets");
-
if (!m_importableExtensions3DMap.isEmpty())
- actionManager->unregisterAddResourceHandlers(category);
+ actionManager->unregisterAddResourceHandlers(ComponentCoreConstants::add3DAssetsDisplayString);
m_importableExtensions3DMap = extMap;
- auto handle3DModel = [this](const QStringList &fileNames, const QString &defaultDir) -> bool {
+ auto import3DModelOperation = [this](const QStringList &fileNames, const QString &defaultDir) -> bool {
auto importDlg = new ItemLibraryAssetImportDialog(fileNames, defaultDir,
m_importableExtensions3DMap,
m_importOptions3DMap, {}, {},
Core::ICore::mainWindow());
- importDlg->show();
+ importDlg->exec();
return true;
};
- auto add3DHandler = [&](const QString &category, const QString &ext) {
+ auto add3DHandler = [&](const QString &group, const QString &ext) {
const QString filter = QStringLiteral("*.%1").arg(ext);
actionManager->registerAddResourceHandler(
- AddResourceHandler(category, filter, handle3DModel, 10));
+ AddResourceHandler(group, filter,
+ import3DModelOperation, 10));
+ };
+
+ const QHash<QString, QString> groupNames {
+ {"3D Scene", ComponentCoreConstants::add3DAssetsDisplayString},
+ {"Qt 3D Studio Presentation", ComponentCoreConstants::addQt3DSPresentationsDisplayString}
};
const auto groups = extMap.keys();
for (const auto &group : groups) {
const QStringList exts = extMap[group].toStringList();
+ const QString grp = groupNames.contains(group) ? groupNames.value(group) : group;
for (const auto &ext : exts)
- add3DHandler(category, ext);
+ add3DHandler(grp, ext);
}
}
diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp
index 5abc38eba6..08dfc9fd03 100644
--- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp
+++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp
@@ -732,17 +732,22 @@ void NavigatorTreeModel::handleItemLibraryItemDrop(const QMimeData *mimeData, in
ModelNode targetNode = targetProperty.parentModelNode();
NodeMetaInfo metaInfo = targetNode.metaInfo();
TypeName typeName = newModelNode.type();
- const PropertyNameList nameList = targetNode.metaInfo().directPropertyNames();
- for (const auto &propertyName : nameList) {
- auto testType = metaInfo.propertyTypeName(propertyName);
- if (testType == typeName || newModelNode.isSubclassOf(testType)) {
- ChooseFromPropertyListDialog *dialog = nullptr;
- dialog = new ChooseFromPropertyListDialog(targetNode, testType, Core::ICore::dialogParent());
- dialog->exec();
- if (dialog->result() == QDialog::Accepted)
- targetNode.bindingProperty(dialog->selectedProperty()).setExpression(newModelNode.validId());
- delete dialog;
- break;
+
+ // Empty components are not supported and having one as property value is generally
+ // unstable, so let's not offer user to put a fresh Component into a property
+ if (typeName != "QtQml.Component") {
+ const PropertyNameList nameList = targetNode.metaInfo().directPropertyNames();
+ for (const auto &propertyName : nameList) {
+ auto testType = metaInfo.propertyTypeName(propertyName);
+ if (testType == typeName || newModelNode.isSubclassOf(testType)) {
+ ChooseFromPropertyListDialog *dialog = nullptr;
+ dialog = new ChooseFromPropertyListDialog(targetNode, testType, Core::ICore::dialogParent());
+ dialog->exec();
+ if (dialog->result() == QDialog::Accepted)
+ targetNode.bindingProperty(dialog->selectedProperty()).setExpression(newModelNode.validId());
+ delete dialog;
+ break;
+ }
}
}
}
@@ -1085,10 +1090,24 @@ void NavigatorTreeModel::moveNodesInteractive(NodeAbstractProperty &parentProper
if (modelNode.isValid()
&& modelNode != parentProperty.parentModelNode()
&& !modelNode.isAncestorOf(parentProperty.parentModelNode())
- && (modelNode.metaInfo().isSubclassOf(propertyQmlType) || propertyQmlType == "alias")) {
+ && (modelNode.metaInfo().isSubclassOf(propertyQmlType)
+ || propertyQmlType == "alias"
+ || parentProperty.name() == "data"
+ || (parentProperty.parentModelNode().metaInfo().defaultPropertyName() == parentProperty.name()
+ && propertyQmlType == "<cpp>.QQmlComponent"))) {
//### todo: allowing alias is just a heuristic
//once the MetaInfo is part of instances we can do this right
+ // We assume above that "data" property in parent accepts all types.
+ // This is a workaround for Component parents to accept children, even though they
+ // do not have an actual "data" property or apparently any other default property.
+ // When the actual reparenting happens, model will create the "data" property if
+ // it is missing.
+
+ // We allow move even if target property type doesn't match, if the target property
+ // is the default property of the parent and is of Component type.
+ // In that case an implicit component will be created.
+
bool nodeCanBeMovedToParentProperty = removeModelNodeFromNodeProperty(parentProperty, modelNode);
if (nodeCanBeMovedToParentProperty) {
reparentModelNodeToNodeProperty(parentProperty, modelNode);
diff --git a/src/plugins/qmldesigner/components/propertyeditor/qmlanchorbindingproxy.cpp b/src/plugins/qmldesigner/components/propertyeditor/qmlanchorbindingproxy.cpp
index aecab7c42c..2664f2c7a9 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/qmlanchorbindingproxy.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/qmlanchorbindingproxy.cpp
@@ -861,6 +861,7 @@ void QmlAnchorBindingProxy::anchorVertical()
m_qmlItemNode.anchors().setAnchor(AnchorLineVerticalCenter, m_verticalTarget, AnchorLineVerticalCenter);
}
+ backupPropertyAndRemove(modelNode(), "y");
m_locked = false;
}
@@ -874,6 +875,7 @@ void QmlAnchorBindingProxy::anchorHorizontal()
} else if (m_relativeVerticalTarget == Center) {
m_qmlItemNode.anchors().setAnchor(AnchorLineHorizontalCenter, m_horizontalTarget, AnchorLineHorizontalCenter);
}
+ backupPropertyAndRemove(modelNode(), "x");
m_locked = false;
}
@@ -993,6 +995,7 @@ void QmlAnchorBindingProxy::setVerticalCentered(bool centered)
if (!centered) {
m_qmlItemNode.anchors().removeAnchor(AnchorLineVerticalCenter);
m_qmlItemNode.anchors().removeMargin(AnchorLineVerticalCenter);
+ restoreProperty(m_qmlItemNode, "y");
} else {
m_relativeVerticalTarget = Center;
@@ -1020,6 +1023,7 @@ void QmlAnchorBindingProxy::setHorizontalCentered(bool centered)
if (!centered) {
m_qmlItemNode.anchors().removeAnchor(AnchorLineHorizontalCenter);
m_qmlItemNode.anchors().removeMargin(AnchorLineHorizontalCenter);
+ restoreProperty(m_qmlItemNode, "x");
} else {
m_relativeHorizontalTarget = Center;
diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp b/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp
index 12fff40c79..997852cfb2 100644
--- a/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp
+++ b/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp
@@ -46,6 +46,7 @@
#include <qmlstate.h>
#include <annotationeditor/annotationeditor.h>
#include <utils/algorithm.h>
+#include <utils/qtcassert.h>
namespace QmlDesigner {
@@ -103,6 +104,7 @@ void StatesEditorView::removeState(int nodeId)
const auto propertyChanges = modelState.propertyChanges();
for (const QmlPropertyChanges &change : propertyChanges) {
const ModelNode target = change.target();
+ QTC_ASSERT(target.isValid(), continue);
if (target.locked())
lockedTargets.push_back(target.id());
}
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.cpp
index 704d7c2b0d..ad2dfd88f6 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.cpp
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.cpp
@@ -70,14 +70,6 @@
namespace QmlDesigner {
-static int deleteKey()
-{
- if (Utils::HostOsInfo::isMacHost())
- return Qt::Key_Backspace;
-
- return Qt::Key_Delete;
-}
-
QList<QmlTimelineKeyframeGroup> allTimelineFrames(const QmlTimeline &timeline)
{
QList<QmlTimelineKeyframeGroup> returnList;
@@ -674,7 +666,7 @@ void TimelineGraphicsScene::keyReleaseEvent(QKeyEvent *keyEvent)
return;
}
- if (deleteKey() == keyEvent->key())
+ if (TimelineUtils::isDeleteKey(keyEvent->key()))
handleKeyframeDeletion();
QGraphicsScene::keyReleaseEvent(keyEvent);
@@ -838,7 +830,7 @@ bool TimelineGraphicsScene::event(QEvent *event)
{
switch (event->type()) {
case QEvent::ShortcutOverride:
- if (static_cast<QKeyEvent *>(event)->key() == deleteKey()) {
+ if (TimelineUtils::isDeleteKey(static_cast<QKeyEvent *>(event)->key())) {
QGraphicsScene::keyPressEvent(static_cast<QKeyEvent *>(event));
event->accept();
return true;
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineutils.h b/src/plugins/qmldesigner/components/timelineeditor/timelineutils.h
index ec0f4cec5b..b31bd8f422 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelineutils.h
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelineutils.h
@@ -39,6 +39,11 @@ namespace TimelineUtils {
enum class Side { Top, Right, Bottom, Left };
+inline bool isDeleteKey(int key)
+{
+ return (key == Qt::Key_Backspace) || (key == Qt::Key_Delete);
+}
+
template<typename T>
inline T clamp(const T &value, const T &lo, const T &hi)
{
diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.cpp
index ab82744cc6..e2b9578356 100644
--- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.cpp
+++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.cpp
@@ -39,6 +39,7 @@
#include "timelineplaceholder.h"
#include "timelinepropertyitem.h"
#include "timelinesectionitem.h"
+#include "timelineutils.h"
#include <designdocumentview.h>
#include <exception.h>
@@ -73,14 +74,6 @@
namespace QmlDesigner {
-static int deleteKey()
-{
- if (Utils::HostOsInfo::isMacHost())
- return Qt::Key_Backspace;
-
- return Qt::Key_Delete;
-}
-
TransitionEditorGraphicsScene::TransitionEditorGraphicsScene(TransitionEditorWidget *parent)
: AbstractScrollGraphicsScene(parent)
, m_parent(parent)
@@ -459,7 +452,7 @@ bool TransitionEditorGraphicsScene::event(QEvent *event)
{
switch (event->type()) {
case QEvent::ShortcutOverride:
- if (static_cast<QKeyEvent *>(event)->key() == deleteKey()) {
+ if (TimelineUtils::isDeleteKey(static_cast<QKeyEvent *>(event)->key())) {
QGraphicsScene::keyPressEvent(static_cast<QKeyEvent *>(event));
event->accept();
return true;
diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp
index 97dc457f39..ccf84152b5 100644
--- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp
+++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp
@@ -98,6 +98,10 @@ void TransitionEditorView::nodeRemoved(const ModelNode & removedNode,
{
if (parentProperty.name() == "transitions")
widget()->updateData(removedNode);
+
+ const ModelNode parent = parentProperty.parentModelNode();
+ if (parent.isValid() && parent.metaInfo().isSubclassOf("QtQuick.Transition"))
+ asyncUpdate(parent);
}
void TransitionEditorView::nodeReparented(const ModelNode &node,
@@ -110,7 +114,6 @@ void TransitionEditorView::nodeReparented(const ModelNode &node,
const ModelNode parent = newPropertyParent.parentModelNode();
- // qDebug() << Q_FUNC_INFO << parent;
if (parent.isValid() && parent.metaInfo().isValid()
&& parent.metaInfo().isSubclassOf("QtQuick.Transition")) {
asyncUpdate(parent);
diff --git a/src/plugins/qmldesigner/designercore/include/itemlibraryinfo.h b/src/plugins/qmldesigner/designercore/include/itemlibraryinfo.h
index c44467f002..8244fbb489 100644
--- a/src/plugins/qmldesigner/designercore/include/itemlibraryinfo.h
+++ b/src/plugins/qmldesigner/designercore/include/itemlibraryinfo.h
@@ -100,7 +100,8 @@ class QMLDESIGNERCORE_EXPORT ItemLibraryInfo : public QObject
public:
QList<ItemLibraryEntry> entries() const;
- QList<ItemLibraryEntry> entriesForType(const QByteArray &typeName, int majorVersion, int minorVersion) const;
+ QList<ItemLibraryEntry> entriesForType(const QByteArray &typeName, int majorVersion = 1,
+ int minorVersion = 0) const;
void addEntries(const QList<ItemLibraryEntry> &entries, bool overwriteDuplicate = false);
bool containsEntry(const ItemLibraryEntry &entry);
diff --git a/src/plugins/qmldesigner/designercore/include/nodehints.h b/src/plugins/qmldesigner/designercore/include/nodehints.h
index a22dd40f40..16fe18320c 100644
--- a/src/plugins/qmldesigner/designercore/include/nodehints.h
+++ b/src/plugins/qmldesigner/designercore/include/nodehints.h
@@ -62,6 +62,7 @@ public:
bool canBeDroppedInView3D() const;
bool isMovable() const;
bool isResizable() const;
+ bool hasFormEditorItem() const;
bool isStackedContainer() const;
bool canBeReparentedTo(const ModelNode &potenialParent);
QString indexPropertyForStackedContainer() const;
diff --git a/src/plugins/qmldesigner/designercore/include/qmlitemnode.h b/src/plugins/qmldesigner/designercore/include/qmlitemnode.h
index 579a4ca7b8..9be07edfbf 100644
--- a/src/plugins/qmldesigner/designercore/include/qmlitemnode.h
+++ b/src/plugins/qmldesigner/designercore/include/qmlitemnode.h
@@ -105,6 +105,7 @@ public:
bool modelIsResizable() const;
bool modelIsRotatable() const;
bool modelIsInLayout() const;
+ bool hasFormEditorItem() const;
QRectF instanceBoundingRect() const;
QRectF instanceSceneBoundingRect() const;
diff --git a/src/plugins/qmldesigner/designercore/include/qmlobjectnode.h b/src/plugins/qmldesigner/designercore/include/qmlobjectnode.h
index 59f99bbe39..7b1cfbe86e 100644
--- a/src/plugins/qmldesigner/designercore/include/qmlobjectnode.h
+++ b/src/plugins/qmldesigner/designercore/include/qmlobjectnode.h
@@ -128,14 +128,13 @@ public:
virtual bool isBlocked(const PropertyName &propName) const;
friend auto qHash(const QmlObjectNode &node) { return qHash(node.modelNode()); }
+ QList<QmlModelState> allDefinedStates() const;
+ QList<QmlModelStateOperation> allInvalidStateOperations() const;
protected:
NodeInstance nodeInstance() const;
QmlObjectNode nodeForInstance(const NodeInstance &instance) const;
QmlItemNode itemForInstance(const NodeInstance &instance) const;
-
-protected:
- QList<QmlModelState> allDefinedStates() const;
};
QMLDESIGNERCORE_EXPORT QList<ModelNode> toModelNodeList(const QList<QmlObjectNode> &fxObjectNodeList);
diff --git a/src/plugins/qmldesigner/designercore/include/qmlstate.h b/src/plugins/qmldesigner/designercore/include/qmlstate.h
index bd0aad854b..ce9a8bf3f7 100644
--- a/src/plugins/qmldesigner/designercore/include/qmlstate.h
+++ b/src/plugins/qmldesigner/designercore/include/qmlstate.h
@@ -50,6 +50,7 @@ public:
QList<QmlModelStateOperation> stateOperations(const ModelNode &node) const;
QList<QmlPropertyChanges> propertyChanges() const;
QList<QmlModelStateOperation> stateOperations() const;
+ QList<QmlModelStateOperation> allInvalidStateOperations() const;
bool hasPropertyChanges(const ModelNode &node) const;
diff --git a/src/plugins/qmldesigner/designercore/include/qmltimelinekeyframegroup.h b/src/plugins/qmldesigner/designercore/include/qmltimelinekeyframegroup.h
index a20eeae55e..6aee5ea0d5 100644
--- a/src/plugins/qmldesigner/designercore/include/qmltimelinekeyframegroup.h
+++ b/src/plugins/qmldesigner/designercore/include/qmltimelinekeyframegroup.h
@@ -72,6 +72,7 @@ public:
static bool isValidKeyframe(const ModelNode &node);
static bool checkKeyframesType(const ModelNode &node);
static QmlTimelineKeyframeGroup keyframeGroupForKeyframe(const ModelNode &node);
+ static QList<QmlTimelineKeyframeGroup> allInvalidTimelineKeyframeGroups(AbstractView *view);
void moveAllKeyframes(qreal offset);
void scaleAllKeyframes(qreal factor);
@@ -84,6 +85,8 @@ public:
void toogleRecording(bool b) const;
QmlTimeline timeline() const;
+
+ bool isDangling() const;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp
index 6ecde66a30..078a74a534 100644
--- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp
+++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp
@@ -644,7 +644,7 @@ void NodeInstanceView::nodeSourceChanged(const ModelNode &node, const QString &
m_nodeInstanceServer->changeNodeSource(changeNodeSourceCommand);
// Puppet doesn't deal with node source changes properly, so just reset the puppet for now
- delayedRestartProcess(); // TODO: Remove this once the issue is properly fixed (QDS-4955)
+ resetPuppet(); // TODO: Remove this once the issue is properly fixed (QDS-4955)
}
}
diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp
index 26b06cdc42..84cc565074 100644
--- a/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp
+++ b/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp
@@ -183,6 +183,11 @@ bool NodeHints::isResizable() const
return evaluateBooleanExpression("isResizable", true);
}
+bool NodeHints::hasFormEditorItem() const
+{
+ return evaluateBooleanExpression("hasFormEditorItem", true);
+}
+
bool NodeHints::isStackedContainer() const
{
if (!isValid())
diff --git a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp
index 03ae0ecd1d..02ecb64efd 100644
--- a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp
+++ b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp
@@ -395,6 +395,11 @@ bool QmlItemNode::modelIsInLayout() const
return false;
}
+bool QmlItemNode::hasFormEditorItem() const
+{
+ return NodeHints::fromModelNode(modelNode()).hasFormEditorItem();
+}
+
QRectF QmlItemNode::instanceBoundingRect() const
{
return QRectF(QPointF(0, 0), nodeInstance().size());
diff --git a/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp
index 556a7624ba..55305c72d1 100644
--- a/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp
+++ b/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp
@@ -328,16 +328,58 @@ QmlPropertyChanges QmlObjectNode::propertyChangeForCurrentState() const
static void removeStateOperationsForChildren(const QmlObjectNode &node)
{
if (node.isValid()) {
- foreach (QmlModelStateOperation stateOperation, node.allAffectingStatesOperations()) {
+ for (QmlModelStateOperation stateOperation : node.allAffectingStatesOperations()) {
stateOperation.modelNode().destroy(); //remove of belonging StatesOperations
}
- foreach (const QmlObjectNode &childNode, node.modelNode().directSubModelNodes()) {
+ for (const QmlObjectNode &childNode : node.modelNode().directSubModelNodes()) {
removeStateOperationsForChildren(childNode);
}
}
}
+static void removeAnimationsFromAnimation(const ModelNode &animation)
+{
+ QTC_ASSERT(animation.isValid(), return);
+
+ const QList<ModelNode> propertyAnimations = animation.subModelNodesOfType(
+ "QtQuick.PropertyAnimation");
+
+ for (const ModelNode &child : propertyAnimations) {
+ if (!child.hasBindingProperty("target")) {
+ ModelNode nonConst = animation;
+ nonConst.destroy();
+ return;
+ }
+ }
+}
+
+static void removeAnimationsFromTransition(const ModelNode &transition, const QmlObjectNode &node)
+{
+ QTC_ASSERT(node.isValid(), return);
+ QTC_ASSERT(transition.isValid(), return);
+
+ const auto children = transition.directSubModelNodes();
+ for (const ModelNode &parallel : children)
+ removeAnimationsFromAnimation(parallel);
+}
+
+static void removeDanglingAnimationsFromTransitions(const QmlObjectNode &node)
+{
+ QTC_ASSERT(node.isValid(), return);
+
+ auto root = node.view()->rootModelNode();
+
+ if (root.isValid() && root.hasProperty("transitions")) {
+ NodeAbstractProperty transitions = root.nodeAbstractProperty("transitions");
+ if (transitions.isValid()) {
+ const auto transitionNodes = transitions.directSubNodes();
+ for (const auto &transition : transitionNodes)
+ removeAnimationsFromTransition(transition, node);
+ }
+ }
+}
+
static void removeAliasExports(const QmlObjectNode &node)
{
@@ -368,6 +410,14 @@ static void removeLayerEnabled(const ModelNode &node)
}
}
+static void deleteAllReferencesToNodeAndChildren(const ModelNode &node)
+{
+ BindingProperty::deleteAllReferencesTo(node);
+ const auto subNodes = node.allSubModelNodes();
+ for (const ModelNode &child : subNodes)
+ BindingProperty::deleteAllReferencesTo(child);
+}
+
/*!
Deletes this object's node and its dependencies from the model.
Everything that belongs to this Object, the ModelNode, and ChangeOperations
@@ -406,7 +456,9 @@ void QmlObjectNode::destroy()
}
removeStateOperationsForChildren(modelNode());
- BindingProperty::deleteAllReferencesTo(modelNode());
+ deleteAllReferencesToNodeAndChildren(modelNode());
+
+ removeDanglingAnimationsFromTransitions(modelNode());
QmlFlowViewNode root(view()->rootModelNode());
@@ -515,6 +567,16 @@ QList<QmlModelState> QmlObjectNode::allDefinedStates() const
return returnList;
}
+QList<QmlModelStateOperation> QmlObjectNode::allInvalidStateOperations() const
+{
+ QList<QmlModelStateOperation> result;
+
+ const auto allStates = allDefinedStates();
+ for (const auto &state : allStates)
+ result.append(state.allInvalidStateOperations());
+ return result;
+}
+
/*!
Removes a variant property of the object specified by \a name from the
diff --git a/src/plugins/qmldesigner/designercore/model/qmlstate.cpp b/src/plugins/qmldesigner/designercore/model/qmlstate.cpp
index ea15ba1d81..26fff118d2 100644
--- a/src/plugins/qmldesigner/designercore/model/qmlstate.cpp
+++ b/src/plugins/qmldesigner/designercore/model/qmlstate.cpp
@@ -34,6 +34,7 @@
#include "qmlitemnode.h"
#include "annotation.h"
+#include <utils/algorithm.h>
#include <utils/qtcassert.h>
namespace QmlDesigner {
@@ -136,6 +137,12 @@ QList<QmlModelStateOperation> QmlModelState::stateOperations() const
return returnList;
}
+QList<QmlModelStateOperation> QmlModelState::allInvalidStateOperations() const
+{
+ return Utils::filtered(stateOperations(), [](const QmlModelStateOperation &operation) {
+ return !operation.target().isValid();
+ });
+}
/*!
Adds a change set for \a node to this state, but only if it does not
diff --git a/src/plugins/qmldesigner/designercore/model/qmltimelinekeyframegroup.cpp b/src/plugins/qmldesigner/designercore/model/qmltimelinekeyframegroup.cpp
index ad0855ad95..85eb51de44 100644
--- a/src/plugins/qmldesigner/designercore/model/qmltimelinekeyframegroup.cpp
+++ b/src/plugins/qmldesigner/designercore/model/qmltimelinekeyframegroup.cpp
@@ -153,6 +153,13 @@ QmlTimeline QmlTimelineKeyframeGroup::timeline() const
return {};
}
+bool QmlTimelineKeyframeGroup::isDangling() const
+{
+ QTC_ASSERT(isValid(), return false);
+
+ return !target().isValid() || keyframes().isEmpty();
+}
+
void QmlTimelineKeyframeGroup::setValue(const QVariant &value, qreal currentFrame)
{
QTC_ASSERT(isValid(), return );
@@ -294,6 +301,22 @@ QmlTimelineKeyframeGroup QmlTimelineKeyframeGroup::keyframeGroupForKeyframe(cons
return QmlTimelineKeyframeGroup();
}
+QList<QmlTimelineKeyframeGroup> QmlTimelineKeyframeGroup::allInvalidTimelineKeyframeGroups(AbstractView *view)
+{
+ QList<QmlTimelineKeyframeGroup> ret;
+
+ QTC_ASSERT(view, return ret);
+ QTC_ASSERT(view->model(), return ret);
+ QTC_ASSERT(view->rootModelNode().isValid(), return ret);
+
+ const auto groups = view->rootModelNode().subModelNodesOfType("QtQuick.Timeline.KeyframeGroup");
+ for (const QmlTimelineKeyframeGroup &group : groups) {
+ if (group.isDangling())
+ ret.append(group);
+ }
+ return ret;
+}
+
void QmlTimelineKeyframeGroup::moveAllKeyframes(qreal offset)
{
for (const ModelNode &childNode : modelNode().defaultNodeListProperty().toModelNodeList()) {
diff --git a/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp
index 802740fc1d..544471b03c 100644
--- a/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp
+++ b/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp
@@ -309,7 +309,11 @@ QmlObjectNode QmlVisualNode::createQmlObjectNode(AbstractView *view,
}
}
- newQmlObjectNode = QmlObjectNode(view->createModelNode(itemLibraryEntry.typeName(), majorVersion, minorVersion, propertyPairList));
+ ModelNode::NodeSourceType nodeSourceType = ModelNode::NodeWithoutSource;
+ if (itemLibraryEntry.typeName() == "QtQml.Component")
+ nodeSourceType = ModelNode::NodeWithComponentSource;
+
+ newQmlObjectNode = QmlObjectNode(view->createModelNode(itemLibraryEntry.typeName(), majorVersion, minorVersion, propertyPairList, {}, {}, nodeSourceType));
} else {
newQmlObjectNode = createQmlObjectNodeFromSource(view, itemLibraryEntry.qmlSource(), position);
}
diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp
index 0209c7a0a7..c678f3b06d 100644
--- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp
+++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp
@@ -1191,9 +1191,11 @@ void TextToModelMerger::syncNode(ModelNode &modelNode,
if (isComponentType(typeName) || isImplicitComponent)
setupComponentDelayed(modelNode, differenceHandler.isAmender());
-
- if (isCustomParserType(typeName))
+ else if (isCustomParserType(typeName))
setupCustomParserNodeDelayed(modelNode, differenceHandler.isAmender());
+ else if (!modelNode.nodeSource().isEmpty() || modelNode.nodeSourceType() != ModelNode::NodeWithoutSource)
+ clearImplicitComponentDelayed(modelNode, differenceHandler.isAmender());
+
context->enterScope(astNode);
@@ -2079,18 +2081,23 @@ void TextToModelMerger::setupComponent(const ModelNode &node)
QString componentText = m_rewriterView->extractText({node}).value(node);
- if (componentText.isEmpty())
+ if (componentText.isEmpty() && node.nodeSource().isEmpty())
return;
QString result = extractComponentFromQml(componentText);
- if (result.isEmpty())
+ if (result.isEmpty() && node.nodeSource().isEmpty())
return; //No object definition found
if (node.nodeSource() != result)
ModelNode(node).setNodeSource(result, ModelNode::NodeWithComponentSource);
}
+void TextToModelMerger::clearImplicitComponent(const ModelNode &node)
+{
+ ModelNode(node).setNodeSource({}, ModelNode::NodeWithoutSource);
+}
+
void TextToModelMerger::collectLinkErrors(QList<DocumentMessage> *errors, const ReadingContext &ctxt)
{
foreach (const QmlJS::DiagnosticMessage &diagnosticMessage, ctxt.diagnosticLinkMessages()) {
@@ -2237,9 +2244,9 @@ void TextToModelMerger::addIsoIconQrcMapping(const QUrl &fileUrl)
} while (dir.cdUp());
}
-void TextToModelMerger::setupComponentDelayed(const ModelNode &node, bool synchron)
+void TextToModelMerger::setupComponentDelayed(const ModelNode &node, bool synchronous)
{
- if (synchron) {
+ if (synchronous) {
setupComponent(node);
} else {
m_setupComponentList.insert(node);
@@ -2254,7 +2261,7 @@ void TextToModelMerger::setupCustomParserNode(const ModelNode &node)
QString modelText = m_rewriterView->extractText({node}).value(node);
- if (modelText.isEmpty())
+ if (modelText.isEmpty() && node.nodeSource().isEmpty())
return;
if (node.nodeSource() != modelText)
@@ -2262,11 +2269,11 @@ void TextToModelMerger::setupCustomParserNode(const ModelNode &node)
}
-void TextToModelMerger::setupCustomParserNodeDelayed(const ModelNode &node, bool synchron)
+void TextToModelMerger::setupCustomParserNodeDelayed(const ModelNode &node, bool synchronous)
{
Q_ASSERT(isCustomParserType(node.type()));
- if (synchron) {
+ if (synchronous) {
setupCustomParserNode(node);
} else {
m_setupCustomParserList.insert(node);
@@ -2274,15 +2281,32 @@ void TextToModelMerger::setupCustomParserNodeDelayed(const ModelNode &node, bool
}
}
+void TextToModelMerger::clearImplicitComponentDelayed(const ModelNode &node, bool synchronous)
+{
+ Q_ASSERT(!isComponentType(node.type()));
+
+ if (synchronous) {
+ clearImplicitComponent(node);
+ } else {
+ m_clearImplicitComponentList.insert(node);
+ m_setupTimer.start();
+ }
+}
+
void TextToModelMerger::delayedSetup()
{
- foreach (const ModelNode node, m_setupComponentList)
+ for (const ModelNode &node : std::as_const(m_setupComponentList))
setupComponent(node);
- foreach (const ModelNode node, m_setupCustomParserList)
+ for (const ModelNode &node : std::as_const(m_setupCustomParserList))
setupCustomParserNode(node);
+
+ for (const ModelNode &node : std::as_const(m_clearImplicitComponentList))
+ clearImplicitComponent(node);
+
m_setupCustomParserList.clear();
m_setupComponentList.clear();
+ m_clearImplicitComponentList.clear();
}
QSet<QPair<QString, QString> > TextToModelMerger::qrcMapping() const
diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h
index f2e308d02f..ed48d4ebc8 100644
--- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h
+++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h
@@ -128,8 +128,9 @@ public:
ReadingContext *context,
DifferenceHandler &differenceHandler);
- void setupComponentDelayed(const ModelNode &node, bool synchron);
- void setupCustomParserNodeDelayed(const ModelNode &node, bool synchron);
+ void setupComponentDelayed(const ModelNode &node, bool synchronous);
+ void setupCustomParserNodeDelayed(const ModelNode &node, bool synchronous);
+ void clearImplicitComponentDelayed(const ModelNode &node, bool synchronous);
void delayedSetup();
@@ -140,6 +141,7 @@ public:
private:
void setupCustomParserNode(const ModelNode &node);
void setupComponent(const ModelNode &node);
+ void clearImplicitComponent(const ModelNode &node);
void collectLinkErrors(QList<DocumentMessage> *errors, const ReadingContext &ctxt);
void collectImportErrors(QList<DocumentMessage> *errors);
void collectSemanticErrorsAndWarnings(QList<DocumentMessage> *errors,
@@ -163,6 +165,7 @@ private:
QTimer m_setupTimer;
QSet<ModelNode> m_setupComponentList;
QSet<ModelNode> m_setupCustomParserList;
+ QSet<ModelNode> m_clearImplicitComponentList;
QmlJS::ViewerContext m_vContext;
QSet<QPair<QString, QString> > m_qrcMapping;
QSet<QmlJS::ImportKey> m_possibleImportKeys;
diff --git a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo
index bbd91d40a8..019be09973 100644
--- a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo
+++ b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo
@@ -432,4 +432,51 @@ MetaInfo {
}
}
+ Type {
+ name: "QtQml.Component"
+ icon: ":/qtquickplugin/images/item-icon16.png"
+
+ Hints {
+ canBeDroppedInNavigator: true
+ canBeDroppedInFormEditor: false
+ }
+
+ ItemLibraryEntry {
+ name: "Component"
+ category: "e.Qt Quick - Component"
+ libraryIcon: ":/qtquickplugin/images/item-icon.png"
+ version: "2.0"
+ }
+ }
+
+ Type {
+ name: "QtQuick.Loader"
+ icon: ":/qtquickplugin/images/item-icon16.png"
+
+ ItemLibraryEntry {
+ name: "Loader"
+ category: "e.Qt Quick - Component"
+ libraryIcon: ":/qtquickplugin/images/item-icon.png"
+ version: "2.0"
+ Property { name: "width"; type: "int"; value: 200; }
+ Property { name: "height"; type: "int"; value: 200; }
+ }
+ }
+
+ Type {
+ name: "QtQuick.Repeater"
+ icon: ":/qtquickplugin/images/item-icon16.png"
+
+ Hints {
+ canBeDroppedInFormEditor: false
+ hasFormEditorItem: false
+ }
+
+ ItemLibraryEntry {
+ name: "Repeater"
+ category: "e.Qt Quick - Component"
+ libraryIcon: ":/qtquickplugin/images/item-icon.png"
+ version: "2.0"
+ }
+ }
}
diff --git a/src/plugins/qmljseditor/qmljseditorplugin.cpp b/src/plugins/qmljseditor/qmljseditorplugin.cpp
index e0d5703102..59cafdcfc1 100644
--- a/src/plugins/qmljseditor/qmljseditorplugin.cpp
+++ b/src/plugins/qmljseditor/qmljseditorplugin.cpp
@@ -110,6 +110,7 @@ QmlJSEditorPlugin::QmlJSEditorPlugin()
QmlJSEditorPlugin::~QmlJSEditorPlugin()
{
+ delete QmlJS::Icons::instance(); // delete object held by singleton
delete d;
d = nullptr;
m_instance = nullptr;
@@ -226,8 +227,6 @@ void QmlJSEditorPlugin::extensionsInitialized()
ExtensionSystem::IPlugin::ShutdownFlag QmlJSEditorPlugin::aboutToShutdown()
{
- delete QmlJS::Icons::instance(); // delete object held by singleton
-
return IPlugin::aboutToShutdown();
}
diff --git a/src/plugins/texteditor/basefilefind.cpp b/src/plugins/texteditor/basefilefind.cpp
index abe8777b34..f0fe19cc34 100644
--- a/src/plugins/texteditor/basefilefind.cpp
+++ b/src/plugins/texteditor/basefilefind.cpp
@@ -236,7 +236,7 @@ static QString displayText(const QString &line)
QString result = line;
auto end = result.end();
for (auto it = result.begin(); it != end; ++it) {
- if (!it->isPrint())
+ if (!it->isSpace() && !it->isPrint())
*it = QChar('?');
}
return result;
diff --git a/src/plugins/valgrind/memcheckerrorview.cpp b/src/plugins/valgrind/memcheckerrorview.cpp
index 0e45b76d43..19e3199b72 100644
--- a/src/plugins/valgrind/memcheckerrorview.cpp
+++ b/src/plugins/valgrind/memcheckerrorview.cpp
@@ -46,6 +46,7 @@
#include <QAction>
+using namespace Utils;
using namespace Valgrind::XmlProtocol;
namespace Valgrind {
@@ -56,10 +57,10 @@ MemcheckErrorView::MemcheckErrorView(QWidget *parent)
{
m_suppressAction = new QAction(this);
m_suppressAction->setText(tr("Suppress Error"));
- const QIcon icon = Utils::Icon({
- {":/utils/images/eye_open.png", Utils::Theme::TextColorNormal},
- {":/valgrind/images/suppressoverlay.png", Utils::Theme::IconsErrorColor}},
- Utils::Icon::Tint | Utils::Icon::PunchEdges).icon();
+ const QIcon icon = Icon({
+ {":/utils/images/eye_open.png", Theme::TextColorNormal},
+ {":/valgrind/images/suppressoverlay.png", Theme::IconsErrorColor}},
+ Icon::Tint | Icon::PunchEdges).icon();
m_suppressAction->setIcon(icon);
m_suppressAction->setShortcuts({QKeySequence::Delete, QKeySequence::Backspace});
m_suppressAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
@@ -69,12 +70,12 @@ MemcheckErrorView::MemcheckErrorView(QWidget *parent)
MemcheckErrorView::~MemcheckErrorView() = default;
-void MemcheckErrorView::setDefaultSuppressionFile(const QString &suppFile)
+void MemcheckErrorView::setDefaultSuppressionFile(const FilePath &suppFile)
{
m_defaultSuppFile = suppFile;
}
-QString MemcheckErrorView::defaultSuppressionFile() const
+FilePath MemcheckErrorView::defaultSuppressionFile() const
{
return m_defaultSuppFile;
}
diff --git a/src/plugins/valgrind/memcheckerrorview.h b/src/plugins/valgrind/memcheckerrorview.h
index 6a22dbf907..888140fc45 100644
--- a/src/plugins/valgrind/memcheckerrorview.h
+++ b/src/plugins/valgrind/memcheckerrorview.h
@@ -28,6 +28,8 @@
#include <debugger/analyzer/detailederrorview.h>
+#include <utils/filepath.h>
+
#include <QListView>
namespace Valgrind {
@@ -43,8 +45,8 @@ public:
MemcheckErrorView(QWidget *parent = nullptr);
~MemcheckErrorView() override;
- void setDefaultSuppressionFile(const QString &suppFile);
- QString defaultSuppressionFile() const;
+ void setDefaultSuppressionFile(const Utils::FilePath &suppFile);
+ Utils::FilePath defaultSuppressionFile() const;
ValgrindBaseSettings *settings() const { return m_settings; }
void settingsChanged(ValgrindBaseSettings *settings);
@@ -53,7 +55,7 @@ private:
QList<QAction *> customActions() const override;
QAction *m_suppressAction;
- QString m_defaultSuppFile;
+ Utils::FilePath m_defaultSuppFile;
ValgrindBaseSettings *m_settings = nullptr;
};
diff --git a/src/plugins/valgrind/memchecktool.cpp b/src/plugins/valgrind/memchecktool.cpp
index 95bfb7e756..ff97b153b6 100644
--- a/src/plugins/valgrind/memchecktool.cpp
+++ b/src/plugins/valgrind/memchecktool.cpp
@@ -128,7 +128,7 @@ public:
void start() override;
void stop() override;
- const QStringList suppressionFiles() const;
+ const Utils::FilePaths suppressionFiles() const;
signals:
void internalParserError(const QString &errorString);
@@ -212,8 +212,8 @@ QStringList MemcheckToolRunner::toolArguments() const
}
arguments << "--leak-check=" + leakCheckValue;
- for (const QString &file : m_settings.suppressions.value())
- arguments << QString("--suppressions=%1").arg(file);
+ for (const FilePath &file : m_settings.suppressions.value())
+ arguments << QString("--suppressions=%1").arg(file.path());
arguments << QString("--num-callers=%1").arg(m_settings.numCallers.value());
@@ -225,7 +225,7 @@ QStringList MemcheckToolRunner::toolArguments() const
return arguments;
}
-const QStringList MemcheckToolRunner::suppressionFiles() const
+const FilePaths MemcheckToolRunner::suppressionFiles() const
{
return m_settings.suppressions.value();
}
@@ -991,15 +991,15 @@ void MemcheckToolPrivate::setupRunner(MemcheckToolRunner *runTool)
clearErrorView();
m_loadExternalLogFile->setDisabled(true);
- QString dir = runControl->project()->projectDirectory().toString() + '/';
+ const FilePath dir = runControl->project()->projectDirectory();
const QString name = runTool->executable().fileName();
- m_errorView->setDefaultSuppressionFile(dir + name + ".supp");
+ m_errorView->setDefaultSuppressionFile(dir.pathAppended(name + ".supp"));
- const QStringList suppressionFiles = runTool->suppressionFiles();
- for (const QString &file : suppressionFiles) {
- QAction *action = m_filterMenu->addAction(FilePath::fromString(file).fileName());
- action->setToolTip(file);
+ const FilePaths suppressionFiles = runTool->suppressionFiles();
+ for (const FilePath &file : suppressionFiles) {
+ QAction *action = m_filterMenu->addAction(file.fileName());
+ action->setToolTip(file.toUserOutput());
connect(action, &QAction::triggered, this, [file] {
EditorManager::openEditorAt(file, 0);
});
@@ -1425,13 +1425,13 @@ void HeobDialog::updateProfile()
int leakRecording = settings->value(heobLeakRecordingC, 2).toInt();
bool attach = settings->value(heobAttachC, false).toBool();
const QString extraArgs = settings->value(heobExtraArgsC).toString();
- QString path = settings->value(heobPathC).toString();
+ FilePath path = FilePath::fromVariant(settings->value(heobPathC));
settings->endGroup();
if (path.isEmpty()) {
const QString heobPath = QStandardPaths::findExecutable("heob32.exe");
if (!heobPath.isEmpty())
- path = QFileInfo(heobPath).path();
+ path = FilePath::fromUserInput(heobPath);
}
m_xmlEdit->setText(xml);
@@ -1444,7 +1444,7 @@ void HeobDialog::updateProfile()
m_leakRecordingCombo->setCurrentIndex(leakRecording);
m_attachCheck->setChecked(attach);
m_extraArgsEdit->setText(extraArgs);
- m_pathChooser->setPath(path);
+ m_pathChooser->setFilePath(path);
}
void HeobDialog::updateEnabled()
diff --git a/src/plugins/valgrind/suppressiondialog.cpp b/src/plugins/valgrind/suppressiondialog.cpp
index 4f5b427f09..bde173edcc 100644
--- a/src/plugins/valgrind/suppressiondialog.cpp
+++ b/src/plugins/valgrind/suppressiondialog.cpp
@@ -51,6 +51,7 @@
#include <QPlainTextEdit>
#include <QPushButton>
+using namespace Utils;
using namespace Valgrind::XmlProtocol;
namespace Valgrind {
@@ -63,7 +64,7 @@ static QString suppressionText(const Error &error)
// workaround: https://bugs.kde.org/show_bug.cgi?id=255822
if (sup.frames().size() >= 24)
sup.setFrames(sup.frames().mid(0, 23));
- QTC_ASSERT(sup.frames().size() < 24, /**/);
+ QTC_CHECK(sup.frames().size() < 24);
// try to set some useful name automatically, instead of "insert_name_here"
// we take the last stack frame and append the suppression kind, e.g.:
@@ -117,7 +118,7 @@ SuppressionDialog::SuppressionDialog(MemcheckErrorView *view, const QList<Error>
m_settings(view->settings()),
m_cleanupIfCanceled(false),
m_errors(errors),
- m_fileChooser(new Utils::PathChooser(this)),
+ m_fileChooser(new PathChooser(this)),
m_suppressionEdit(new QPlainTextEdit(this))
{
setWindowTitle(tr("Save Suppression"));
@@ -140,27 +141,23 @@ SuppressionDialog::SuppressionDialog(MemcheckErrorView *view, const QList<Error>
formLayout->addRow(m_suppressionEdit);
formLayout->addRow(m_buttonBox);
- QFile defaultSuppFile(view->defaultSuppressionFile());
- if (!defaultSuppFile.exists()) {
- if (defaultSuppFile.open(QIODevice::WriteOnly)) {
- defaultSuppFile.close();
- m_cleanupIfCanceled = true;
- }
- }
+ const FilePath defaultSuppFile = view->defaultSuppressionFile();
+ if (!defaultSuppFile.exists() && defaultSuppFile.ensureExistingFile())
+ m_cleanupIfCanceled = true;
- m_fileChooser->setExpectedKind(Utils::PathChooser::File);
+ m_fileChooser->setExpectedKind(PathChooser::File);
m_fileChooser->setHistoryCompleter("Valgrind.Suppression.History");
m_fileChooser->setPath(defaultSuppFile.fileName());
m_fileChooser->setPromptDialogFilter("*.supp");
m_fileChooser->setPromptDialogTitle(tr("Select Suppression File"));
QString suppressions;
- foreach (const Error &error, m_errors)
+ for (const Error &error : qAsConst(m_errors))
suppressions += suppressionText(error);
m_suppressionEdit->setPlainText(suppressions);
- connect(m_fileChooser, &Utils::PathChooser::validChanged,
+ connect(m_fileChooser, &PathChooser::validChanged,
this, &SuppressionDialog::validate);
connect(m_suppressionEdit->document(), &QTextDocument::contentsChanged,
this, &SuppressionDialog::validate);
@@ -178,7 +175,7 @@ void SuppressionDialog::maybeShow(MemcheckErrorView *view)
indices.append(view->selectionModel()->currentIndex());
QList<XmlProtocol::Error> errors;
- foreach (const QModelIndex &index, indices) {
+ for (const QModelIndex &index : qAsConst(indices)) {
Error error = view->model()->data(index, ErrorListModel::ErrorRole).value<Error>();
if (!error.suppression().isNull())
errors.append(error);
@@ -193,11 +190,11 @@ void SuppressionDialog::maybeShow(MemcheckErrorView *view)
void SuppressionDialog::accept()
{
- const Utils::FilePath path = m_fileChooser->filePath();
+ const FilePath path = m_fileChooser->filePath();
QTC_ASSERT(!path.isEmpty(), return);
QTC_ASSERT(!m_suppressionEdit->toPlainText().trimmed().isEmpty(), return);
- Utils::FileSaver saver(path, QIODevice::Append);
+ FileSaver saver(path, QIODevice::Append);
if (!saver.hasError()) {
QTextStream stream(saver.file());
stream << m_suppressionEdit->toPlainText();
@@ -216,14 +213,14 @@ void SuppressionDialog::accept()
}
}
- m_settings->suppressions.addSuppressionFile(path.toString());
+ m_settings->suppressions.addSuppressionFile(path);
QModelIndexList indices = m_view->selectionModel()->selectedRows();
Utils::sort(indices, [](const QModelIndex &l, const QModelIndex &r) {
return l.row() > r.row();
});
QAbstractItemModel *model = m_view->model();
- foreach (const QModelIndex &index, indices) {
+ for (const QModelIndex &index : qAsConst(indices)) {
bool removed = model->removeRow(index.row());
QTC_ASSERT(removed, qt_noop());
Q_UNUSED(removed)
@@ -234,7 +231,7 @@ void SuppressionDialog::accept()
const Error rowError = model->data(
model->index(row, 0), ErrorListModel::ErrorRole).value<Error>();
- foreach (const Error &error, m_errors) {
+ for (const Error &error : qAsConst(m_errors)) {
if (equalSuppression(rowError, error)) {
bool removed = model->removeRow(row);
QTC_CHECK(removed);
@@ -254,7 +251,7 @@ void SuppressionDialog::accept()
void SuppressionDialog::reject()
{
if (m_cleanupIfCanceled)
- QFile::remove(m_view->defaultSuppressionFile());
+ m_view->defaultSuppressionFile().removeFile();
QDialog::reject();
}
diff --git a/src/plugins/valgrind/valgrindsettings.cpp b/src/plugins/valgrind/valgrindsettings.cpp
index 352ad060ef..21d056e3fd 100644
--- a/src/plugins/valgrind/valgrindsettings.cpp
+++ b/src/plugins/valgrind/valgrindsettings.cpp
@@ -73,9 +73,9 @@ public:
QStandardItemModel m_model; // The volatile value of this aspect.
};
-void SuppressionAspect::addSuppressionFile(const QString &suppression)
+void SuppressionAspect::addSuppressionFile(const FilePath &suppression)
{
- QStringList val = value();
+ FilePaths val = value();
val.append(suppression);
setValue(val);
}
@@ -141,14 +141,14 @@ SuppressionAspect::~SuppressionAspect()
delete d;
}
-QStringList SuppressionAspect::value() const
+FilePaths SuppressionAspect::value() const
{
- return BaseAspect::value().toStringList();
+ return Utils::transform(BaseAspect::value().toStringList(), &FilePath::fromString);
}
-void SuppressionAspect::setValue(const QStringList &val)
+void SuppressionAspect::setValue(const FilePaths &val)
{
- BaseAspect::setValue(val);
+ BaseAspect::setValue(Utils::transform<QStringList>(val, &FilePath::toString));
}
void SuppressionAspect::addToLayout(LayoutBuilder &builder)
@@ -180,7 +180,7 @@ void SuppressionAspect::addToLayout(LayoutBuilder &builder)
};
builder.addItem(Span { 2, group });
- setVolatileValue(value());
+ setVolatileValue(BaseAspect::value());
}
void SuppressionAspect::fromMap(const QVariantMap &map)
diff --git a/src/plugins/valgrind/valgrindsettings.h b/src/plugins/valgrind/valgrindsettings.h
index 79fc68136e..7d4f01f79e 100644
--- a/src/plugins/valgrind/valgrindsettings.h
+++ b/src/plugins/valgrind/valgrindsettings.h
@@ -46,8 +46,8 @@ public:
explicit SuppressionAspect(bool global);
~SuppressionAspect() final;
- QStringList value() const;
- void setValue(const QStringList &val);
+ Utils::FilePaths value() const;
+ void setValue(const Utils::FilePaths &val);
void addToLayout(Utils::LayoutBuilder &builder) final;
@@ -57,7 +57,7 @@ public:
QVariant volatileValue() const final;
void setVolatileValue(const QVariant &val) final;
- void addSuppressionFile(const QString &suppressionFile);
+ void addSuppressionFile(const Utils::FilePath &suppressionFile);
private:
friend class ValgrindBaseSettings;
diff --git a/src/shared/proparser/qmakebuiltins.cpp b/src/shared/proparser/qmakebuiltins.cpp
index a697133896..c47bc1b2e7 100644
--- a/src/shared/proparser/qmakebuiltins.cpp
+++ b/src/shared/proparser/qmakebuiltins.cpp
@@ -474,12 +474,8 @@ void QMakeEvaluator::runProcess(QProcess *proc, const QString &command) const
}
# endif
# ifdef PROEVALUATOR_THREAD_SAFE
- m_option->mutex.lock();
- if (m_option->canceled) {
- m_option->mutex.unlock();
+ if (m_option->canceled)
return;
- }
- m_option->runningProcs << proc;
# endif
# ifdef Q_OS_WIN
proc->setNativeArguments(QLatin1String("/v:off /s /c \"") + command + QLatin1Char('"'));
@@ -488,12 +484,21 @@ void QMakeEvaluator::runProcess(QProcess *proc, const QString &command) const
proc->start(QLatin1String("/bin/sh"), QStringList() << QLatin1String("-c") << command);
# endif
# ifdef PROEVALUATOR_THREAD_SAFE
- m_option->mutex.unlock();
-# endif
- proc->waitForFinished(-1);
-# ifdef PROEVALUATOR_THREAD_SAFE
- QMutexLocker(&m_option->mutex);
- m_option->runningProcs.removeOne(proc);
+ while (true) {
+ if (proc->waitForFinished(100))
+ break;
+ if (m_option->canceled) {
+ proc->terminate();
+ if (proc->waitForFinished(1000))
+ break;
+ proc->kill();
+ proc->waitForFinished(1000);
+ break;
+ }
+ }
+# else
+ proc->waitForFinished(-1); // If have have single thread we can't cancel it using
+ // synchronous API of QProcess
# endif
}
#endif
diff --git a/src/shared/proparser/qmakeglobals.cpp b/src/shared/proparser/qmakeglobals.cpp
index ea429145e3..ca22bdde8b 100644
--- a/src/shared/proparser/qmakeglobals.cpp
+++ b/src/shared/proparser/qmakeglobals.cpp
@@ -92,11 +92,7 @@ QMakeGlobals::~QMakeGlobals()
void QMakeGlobals::killProcesses()
{
#ifdef PROEVALUATOR_THREAD_SAFE
- QMutexLocker lock(&mutex);
canceled = true;
- for (QProcess * const proc : runningProcs)
- proc->kill();
- runningProcs.clear();
#endif
}
diff --git a/src/shared/proparser/qmakeglobals.h b/src/shared/proparser/qmakeglobals.h
index 2fa9a33213..2baeab4bf5 100644
--- a/src/shared/proparser/qmakeglobals.h
+++ b/src/shared/proparser/qmakeglobals.h
@@ -159,8 +159,7 @@ private:
#ifdef PROEVALUATOR_THREAD_SAFE
QMutex mutex;
- bool canceled = false;
- QList<QProcess *> runningProcs;
+ std::atomic_bool canceled = false;
#endif
QHash<QMakeBaseKey, QMakeBaseEnv *> baseEnvs;
diff --git a/src/shared/qbs b/src/shared/qbs
-Subproject 4592eff289852e3e5a81596d1cc6b0c2488e6bc
+Subproject a35ff56175cc8c993b54bb1d92ff71ba4532fc8
diff --git a/src/shared/shared.pro b/src/shared/shared.pro
index b4c64a02fb..4e4c860c82 100644
--- a/src/shared/shared.pro
+++ b/src/shared/shared.pro
@@ -6,9 +6,12 @@ QBS_DIRS = \
qbslibexec \
qbsmsbuildlib \
qbsplugins \
- qbsstatic
+ qbsstatic \
+ qbspkgconfig
+qbspkgconfig.subdir = qbs/src/lib/pkgconfig
qbscorelib.subdir = qbs/src/lib/corelib
+qbscorelib.depends = qbspkgconfig
qbsapps.subdir = qbs/src/app
qbsapps.depends = qbscorelib
qbslibexec.subdir = qbs/src/libexec
diff --git a/src/tools/qml2puppet/qml2puppet.qbs b/src/tools/qml2puppet/qml2puppet.qbs
index 7eff7ba02b..83acc77ba3 100644
--- a/src/tools/qml2puppet/qml2puppet.qbs
+++ b/src/tools/qml2puppet/qml2puppet.qbs
@@ -16,6 +16,7 @@ QtcTool {
]
}
Depends { name: "Qt.quick3d-private"; required: false }
+ Depends { name: "Qt.quick3dparticles-private"; required: false }
property bool useQuick3d: Utilities.versionCompare(Qt.core.version, "5.15") >= 0
&& Qt["quick3d-private"].present
property bool useParticle3d: Utilities.versionCompare(Qt.core.version, "6.2") >= 0
diff --git a/tests/manual/debugger/simple/simple_test_app.cpp b/tests/manual/debugger/simple/simple_test_app.cpp
index cbe4294389..d4df6f0411 100644
--- a/tests/manual/debugger/simple/simple_test_app.cpp
+++ b/tests/manual/debugger/simple/simple_test_app.cpp
@@ -4433,7 +4433,11 @@ namespace qvariant {
// FIXME: Known to break
//QString type = var.typeName();
var.setValue(my);
+#if QT_VERSION >= 0x051500
+ const char *name = QMetaType(var.userType()).name();
+#else
const char *name = QMetaType::typeName(var.userType());
+#endif
BREAK_HERE;
// Expand my my.0 my.0.value my.1 my.1.value var var.data var.data.0 var.data.0.value var.data.1 var.data.1.value.
// Check my <2 items> qvariant::MyType.
@@ -4706,6 +4710,7 @@ namespace noargs {
{
public:
Goo(const QString &str, const int n) : str_(str), n_(n) {}
+ int n() {return n_;}
private:
QString str_;
int n_;
@@ -5459,7 +5464,7 @@ namespace basic {
const int &b = a;
typedef int &Ref;
const int c = 44;
- const Ref d = a;
+ Ref d = a;
BREAK_HERE;
// Check a 43 int.
// Check b 43 int &.
@@ -5475,7 +5480,7 @@ namespace basic {
const QString &b = fooxx();
typedef QString &Ref;
const QString c = "world";
- const Ref d = a;
+ Ref d = a;
BREAK_HERE;
// Check a "hello" QString.
// Check b "bababa" QString &.
@@ -5489,7 +5494,7 @@ namespace basic {
{
const QString &b = a;
typedef QString &Ref;
- const Ref d = const_cast<Ref>(a);
+ Ref d = const_cast<Ref>(a);
BREAK_HERE;
// Check a "hello" QString &.
// Check b "hello" QString &.
diff --git a/tests/system/suite_general/tst_rename_file/test.py b/tests/system/suite_general/tst_rename_file/test.py
index 5f867f1f98..7d846d3013 100644
--- a/tests/system/suite_general/tst_rename_file/test.py
+++ b/tests/system/suite_general/tst_rename_file/test.py
@@ -87,14 +87,16 @@ def renameFile(projectDir, proFile, branch, oldname, newname):
oldFilePath = os.path.join(projectDir, oldname)
newFilePath = os.path.join(projectDir, newname)
oldFileText = readFile(oldFilePath)
- itemText = branch + "." + oldname.replace(".", "\\.")
+ oldItemText = branch + "." + oldname.replace(".", "\\.")
+ newItemText = branch + "." + newname.replace(".", "\\.")
treeview = waitForObject(":Qt Creator_Utils::NavigationTreeView")
try:
- openItemContextMenu(treeview, itemText, 5, 5, 0)
+ openItemContextMenu(treeview, oldItemText, 5, 5, 0)
except:
- itemWithWildcard = addBranchWildcardToRoot(itemText)
- waitForObjectItem(treeview, itemWithWildcard, 10000)
- openItemContextMenu(treeview, itemWithWildcard, 5, 5, 0)
+ oldItemText = addBranchWildcardToRoot(oldItemText)
+ newItemText = addBranchWildcardToRoot(newItemText)
+ waitForObjectItem(treeview, oldItemText, 10000)
+ openItemContextMenu(treeview, oldItemText, 5, 5, 0)
if oldname.lower().endswith(".qrc"):
menu = ":Qt Creator.Project.Menu.Folder_QMenu"
else:
@@ -111,10 +113,18 @@ def renameFile(projectDir, proFile, branch, oldname, newname):
" windowTitle='Rename More Files?'}}"))
test.verify(waitFor("os.path.exists(newFilePath)", 1000),
"Verify that file with new name exists: %s" % newFilePath)
- test.compare(readFile(newFilePath), oldFileText,
- "Comparing content of file before and after renaming")
test.verify(waitFor("' ' + newname in safeReadFile(proFile)", 2000),
"Verify that new filename '%s' was added to pro-file." % newname)
+ if oldname.endswith(".h"):
+ # Creator updates include guards in renamed header files and changes line breaks
+ oldFileText = oldFileText.replace("\r\n", "\n")
+ includeGuard = " " + newname.upper().replace(".", "_")
+ if not includeGuard.endswith("_H"):
+ includeGuard += "_H"
+ oldFileText = oldFileText.replace(" " + oldname.upper().replace(".", "_"), includeGuard)
+ waitFor("includeGuard in safeReadFile(newFilePath)", 2000)
+ test.compare(readFile(newFilePath), oldFileText,
+ "Comparing content of file before and after renaming")
if oldname not in newname:
test.verify(oldname not in readFile(proFile),
"Verify that old filename '%s' was removed from pro-file." % oldname)
@@ -122,6 +132,13 @@ def renameFile(projectDir, proFile, branch, oldname, newname):
test.verify(oldname not in os.listdir(projectDir),
"Verify that file with old name does not exist: %s" % oldFilePath)
+ if newItemText.endswith("\\.qml"):
+ newItemText = newItemText.replace(".Other files.", ".QML.")
+ else:
+ newItemText = newItemText.replace(".QML.", ".Other files.")
+ waitForObjectItem(treeview, newItemText)
+
+
def safeReadFile(filename):
text = ""
while text == "":
diff --git a/tests/unit/unittest/CMakeLists.txt b/tests/unit/unittest/CMakeLists.txt
index 5699475432..6252f4517d 100644
--- a/tests/unit/unittest/CMakeLists.txt
+++ b/tests/unit/unittest/CMakeLists.txt
@@ -481,8 +481,16 @@ get_filename_component(
ABSOLUTE
)
-if(EXISTS ${QMLDOM_STANDALONE_CMAKELISTS})
- add_subdirectory(${QMLDOM_STANDALONE_CMAKELISTS} qmldom_standalone)
+
+if (EXISTS ../../../../qmldom_standalone/src/qmldom/standalone)
+ add_subdirectory(
+ ../../../../qmldom_standalone/src/qmldom/standalone
+ ${CMAKE_CURRENT_BINARY_DIR}/qmldom_standalone)
+
+ set_target_properties(qmldomlib PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY "$<TARGET_PROPERTY:QmlJS,RUNTIME_OUTPUT_DIRECTORY>"
+ LIBRARY_OUTPUT_DIRECTORY "$<TARGET_PROPERTY:QmlJS,LIBRARY_OUTPUT_DIRECTORY>")
+
extend_qtc_test(unittest
DEPENDS qmldomlib
SOURCES