diff --git a/.github/workflows/build_cmake.yml b/.github/workflows/build_cmake.yml
index 25e087fb25f..f388524f114 100644
--- a/.github/workflows/build_cmake.yml
+++ b/.github/workflows/build_cmake.yml
@@ -8,12 +8,12 @@ on:
env:
QT_VERSION: 6.2.3
- CLANG_VERSION: 130
+ CLANG_VERSION: 140
ELFUTILS_VERSION: 0.175
CMAKE_VERSION: 3.21.1
NINJA_VERSION: 1.10.2
BUILD_TYPE: Release
- CCACHE_VERSION: 4.5
+ CCACHE_VERSION: 4.6
QT_MIRRORS: download.qt.io;mirrors.ocf.berkeley.edu/qt;ftp.fau.de/qtproject;mirror.bit.edu.cn/qtproject
jobs:
@@ -31,12 +31,14 @@ jobs:
environment_script: "C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/VC/Auxiliary/Build/vcvars64.bat",
is_msvc: true
}
-# - {
-# name: "Windows Latest MinGW", artifact: "Windows-MinGW",
-# os: windows-latest,
-# cc: "gcc", cxx: "g++",
-# is_msvc: false
-# }
+ - {
+ name: "Windows Latest MinGW", artifact: "Windows-MinGW",
+ os: windows-latest,
+ toolchain: "https://github.com/cristianadam/mingw-builds/releases/download/v11.2.0-rev1/x86_64-11.2.0-release-posix-seh-rt_v9-rev1.7z",
+ toolchain_path: "mingw64/bin",
+ cc: "gcc", cxx: "g++",
+ is_msvc: false
+ }
- {
name: "Ubuntu Latest GCC", artifact: "Linux",
os: ubuntu-latest,
@@ -111,6 +113,33 @@ jobs:
)
endif()
+ - name: Install system libs
+ shell: cmake -P {0}
+ run: |
+ if ("${{ runner.os }}" STREQUAL "Linux")
+ execute_process(
+ COMMAND sudo apt update
+ )
+ execute_process(
+ 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)
+ message(FATAL_ERROR "Failed to install dependencies")
+ endif()
+ endif()
+
+ if (NOT "x${{ matrix.config.toolchain }}" STREQUAL "x")
+ foreach(retry RANGE 10)
+ file(DOWNLOAD "${{ matrix.config.toolchain }}" ./toolchain.7z SHOW_PROGRESS)
+ file(SIZE ./toolchain.7z fileSize)
+ if (fileSize GREATER 0)
+ break()
+ endif()
+ endforeach()
+ execute_process(COMMAND ${CMAKE_COMMAND} -E tar xvf ./toolchain.7z)
+ endif()
+
- name: Download Qt
id: qt
shell: cmake -P {0}
@@ -121,8 +150,8 @@ jobs:
if ("${{ runner.os }}" STREQUAL "Windows")
set(url_os "windows_x86")
if ("x${{ matrix.config.environment_script }}" STREQUAL "x")
- set(qt_package_arch_suffix "win64_mingw81")
- set(qt_dir_prefix "${qt_version}/mingw81_64")
+ set(qt_package_arch_suffix "win64_mingw")
+ set(qt_dir_prefix "${qt_version}/mingw_64")
set(qt_package_suffix "-Windows-Windows_10_21H2-Mingw-Windows-Windows_10_21H2-X86_64")
elseif ("${{ matrix.config.environment_script }}" MATCHES "vcvars64.bat")
set(qt_package_arch_suffix "win64_msvc2019_64")
@@ -218,7 +247,7 @@ jobs:
if ("x${{ matrix.config.environment_script }}" STREQUAL "x")
# 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}"
+ file(INSTALL "$ENV{GITHUB_WORKSPACE}/${{ matrix.config.toolchain_path }}/${file}"
DESTINATION "qt6/${qt_dir_prefix}/bin"
USE_SOURCE_PERMISSIONS)
endforeach()
@@ -434,22 +463,6 @@ jobs:
endforeach()
endforeach()
- - name: Install system libs
- shell: cmake -P {0}
- run: |
- if ("${{ runner.os }}" STREQUAL "Linux")
- execute_process(
- COMMAND sudo apt update
- )
- execute_process(
- 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)
- message(FATAL_ERROR "Failed to install dependencies")
- endif()
- endif()
-
- name: Build
shell: cmake -P {0}
run: |
@@ -516,6 +529,14 @@ jobs:
unset(NO_DMG)
endif()
+ if (NOT "x${{ matrix.config.toolchain_path }}" STREQUAL "x")
+ set(path_separator ":")
+ if ("${{ runner.os }}" STREQUAL "Windows")
+ set(path_separator ";")
+ endif()
+ set(ENV{PATH} "$ENV{GITHUB_WORKSPACE}/${{ matrix.config.toolchain_path }}${path_separator}$ENV{PATH}")
+ endif()
+
execute_process(
COMMAND python
-u
@@ -673,10 +694,10 @@ jobs:
name: "Windows Latest MSVC", artifact: "Windows-MSVC",
os: ubuntu-latest
}
-# - {
-# name: "Windows Latest MinGW", artifact: "Windows-MinGW",
-# os: ubuntu-latest
-# }
+ - {
+ name: "Windows Latest MinGW", artifact: "Windows-MinGW",
+ os: ubuntu-latest
+ }
- {
name: "Ubuntu Latest GCC", artifact: "Linux",
os: ubuntu-latest
diff --git a/dist/installer/mac/disclaim.entitlements b/dist/installer/mac/disclaim.entitlements
new file mode 100644
index 00000000000..7760cac65c8
--- /dev/null
+++ b/dist/installer/mac/disclaim.entitlements
@@ -0,0 +1,8 @@
+
+
+
+
+ com.apple.security.cs.allow-dyld-environment-variables
+
+
+
diff --git a/doc/qtcreator/images/creator-qbs-project.png b/doc/qtcreator/images/creator-qbs-project.png
deleted file mode 100644
index fe8d7b5ecdd..00000000000
Binary files a/doc/qtcreator/images/creator-qbs-project.png and /dev/null differ
diff --git a/doc/qtcreator/images/creator-uwp-kits.png b/doc/qtcreator/images/creator-uwp-kits.png
deleted file mode 100644
index 2c8c29560db..00000000000
Binary files a/doc/qtcreator/images/creator-uwp-kits.png and /dev/null differ
diff --git a/doc/qtcreator/images/qml-link-debugging-library.png b/doc/qtcreator/images/qml-link-debugging-library.png
deleted file mode 100644
index 6a600c000df..00000000000
Binary files a/doc/qtcreator/images/qml-link-debugging-library.png and /dev/null differ
diff --git a/doc/qtcreator/images/qmldesigner-new-project.png b/doc/qtcreator/images/qmldesigner-new-project.png
deleted file mode 100644
index 7bab02c38f1..00000000000
Binary files a/doc/qtcreator/images/qmldesigner-new-project.png and /dev/null differ
diff --git a/doc/qtcreator/images/qtcreator-add-resource-wizard.png b/doc/qtcreator/images/qtcreator-add-resource-wizard.png
index 3b1b8b045e3..e76223f5b20 100644
Binary files a/doc/qtcreator/images/qtcreator-add-resource-wizard.png and b/doc/qtcreator/images/qtcreator-add-resource-wizard.png differ
diff --git a/doc/qtcreator/images/qtcreator-android-select-devices.png b/doc/qtcreator/images/qtcreator-android-select-devices.png
deleted file mode 100644
index 7c747666808..00000000000
Binary files a/doc/qtcreator/images/qtcreator-android-select-devices.png and /dev/null differ
diff --git a/doc/qtcreator/images/qtcreator-autotools-buildrun.png b/doc/qtcreator/images/qtcreator-autotools-buildrun.png
index 5795b536b5c..49018982346 100644
Binary files a/doc/qtcreator/images/qtcreator-autotools-buildrun.png and b/doc/qtcreator/images/qtcreator-autotools-buildrun.png differ
diff --git a/doc/qtcreator/images/qtcreator-autotools-buildsettings.png b/doc/qtcreator/images/qtcreator-autotools-buildsettings.png
index 879c0e8f5a4..bd302a0b923 100644
Binary files a/doc/qtcreator/images/qtcreator-autotools-buildsettings.png and b/doc/qtcreator/images/qtcreator-autotools-buildsettings.png differ
diff --git a/doc/qtcreator/images/qtcreator-cmake-run-cmake.png b/doc/qtcreator/images/qtcreator-cmake-run-cmake.png
deleted file mode 100644
index c778ea45116..00000000000
Binary files a/doc/qtcreator/images/qtcreator-cmake-run-cmake.png and /dev/null differ
diff --git a/doc/qtcreator/images/qtcreator-cmake-run-settings.png b/doc/qtcreator/images/qtcreator-cmake-run-settings.png
deleted file mode 100644
index 229bdae6dd1..00000000000
Binary files a/doc/qtcreator/images/qtcreator-cmake-run-settings.png and /dev/null differ
diff --git a/doc/qtcreator/images/qtcreator-deployment-steps-b2qt-58.png b/doc/qtcreator/images/qtcreator-deployment-steps-b2qt-58.png
deleted file mode 100644
index 8acc7c55c8d..00000000000
Binary files a/doc/qtcreator/images/qtcreator-deployment-steps-b2qt-58.png and /dev/null differ
diff --git a/doc/qtcreator/images/qtcreator-edit-mode.png b/doc/qtcreator/images/qtcreator-edit-mode.png
deleted file mode 100644
index a8781f1d5e0..00000000000
Binary files a/doc/qtcreator/images/qtcreator-edit-mode.png and /dev/null differ
diff --git a/doc/qtcreator/images/qtcreator-git-commit-actions.png b/doc/qtcreator/images/qtcreator-git-commit-actions.png
deleted file mode 100644
index 37295e48fca..00000000000
Binary files a/doc/qtcreator/images/qtcreator-git-commit-actions.png and /dev/null differ
diff --git a/doc/qtcreator/images/qtcreator-language-client-inspector-memory-usage.png b/doc/qtcreator/images/qtcreator-language-client-inspector-memory-usage.png
deleted file mode 100644
index 92ab52d28b9..00000000000
Binary files a/doc/qtcreator/images/qtcreator-language-client-inspector-memory-usage.png and /dev/null differ
diff --git a/doc/qtcreator/images/qtcreator-locator-url-template.png b/doc/qtcreator/images/qtcreator-locator-url-template.png
deleted file mode 100644
index e0932c7be64..00000000000
Binary files a/doc/qtcreator/images/qtcreator-locator-url-template.png and /dev/null differ
diff --git a/doc/qtcreator/images/qtcreator-meson-build-settings.png b/doc/qtcreator/images/qtcreator-meson-build-settings.png
index 66865550543..eee5c3d564d 100644
Binary files a/doc/qtcreator/images/qtcreator-meson-build-settings.png and b/doc/qtcreator/images/qtcreator-meson-build-settings.png differ
diff --git a/doc/qtcreator/images/qtcreator-meson-build-steps.png b/doc/qtcreator/images/qtcreator-meson-build-steps.png
index 410d6ae059b..73e7300a42f 100644
Binary files a/doc/qtcreator/images/qtcreator-meson-build-steps.png and b/doc/qtcreator/images/qtcreator-meson-build-steps.png differ
diff --git a/doc/qtcreator/images/qtcreator-meson-clean-steps.png b/doc/qtcreator/images/qtcreator-meson-clean-steps.png
index c84aa1d217a..f3e3dd2f660 100644
Binary files a/doc/qtcreator/images/qtcreator-meson-clean-steps.png and b/doc/qtcreator/images/qtcreator-meson-clean-steps.png differ
diff --git a/doc/qtcreator/images/qtcreator-new-opengl-file.png b/doc/qtcreator/images/qtcreator-new-opengl-file.png
index 529474b1e03..cad63fea844 100644
Binary files a/doc/qtcreator/images/qtcreator-new-opengl-file.png and b/doc/qtcreator/images/qtcreator-new-opengl-file.png differ
diff --git a/doc/qtcreator/images/qtcreator-new-project.png b/doc/qtcreator/images/qtcreator-new-project.png
new file mode 100644
index 00000000000..c1d2a371136
Binary files /dev/null and b/doc/qtcreator/images/qtcreator-new-project.png differ
diff --git a/doc/qtcreator/images/qtcreator-new-qt-gui-application.png b/doc/qtcreator/images/qtcreator-new-qt-gui-application.png
index 19d8e0c1ebc..d2b3496c301 100644
Binary files a/doc/qtcreator/images/qtcreator-new-qt-gui-application.png and b/doc/qtcreator/images/qtcreator-new-qt-gui-application.png differ
diff --git a/doc/qtcreator/images/qtcreator-new-qt-quick-project-wizard.png b/doc/qtcreator/images/qtcreator-new-qt-quick-project-wizard.png
deleted file mode 100644
index d9d7509a8ad..00000000000
Binary files a/doc/qtcreator/images/qtcreator-new-qt-quick-project-wizard.png and /dev/null differ
diff --git a/doc/qtcreator/images/qtcreator-new-qt-quick-project.png b/doc/qtcreator/images/qtcreator-new-qt-quick-project.png
new file mode 100644
index 00000000000..ec773f7f5fe
Binary files /dev/null and b/doc/qtcreator/images/qtcreator-new-qt-quick-project.png differ
diff --git a/doc/qtcreator/images/qtcreator-new-subproject.png b/doc/qtcreator/images/qtcreator-new-subproject.png
deleted file mode 100644
index 7bab02c38f1..00000000000
Binary files a/doc/qtcreator/images/qtcreator-new-subproject.png and /dev/null differ
diff --git a/doc/qtcreator/images/qtcreator-pprunsettings.png b/doc/qtcreator/images/qtcreator-pprunsettings.png
index 4b53a44d9ab..dab1d9e1cfb 100644
Binary files a/doc/qtcreator/images/qtcreator-pprunsettings.png and b/doc/qtcreator/images/qtcreator-pprunsettings.png differ
diff --git a/doc/qtcreator/images/qtquick-debugging-settings.png b/doc/qtcreator/images/qtquick-debugging-settings.png
index 0b3d896ca1a..c6b1f7bca2e 100644
Binary files a/doc/qtcreator/images/qtquick-debugging-settings.png and b/doc/qtcreator/images/qtquick-debugging-settings.png differ
diff --git a/doc/qtcreator/src/conan/creator-projects-conan.qdoc b/doc/qtcreator/src/conan/creator-projects-conan.qdoc
index fc20640b861..f76eaac6a17 100644
--- a/doc/qtcreator/src/conan/creator-projects-conan.qdoc
+++ b/doc/qtcreator/src/conan/creator-projects-conan.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Creator documentation.
@@ -52,7 +52,7 @@
To use Conan, install it by using the Qt installer or the tools provided by
your operating system. For example, on Windows, you can use the
- \c {choco install conan} or \{pip install conan} command.
+ \c {choco install conan} or \c {pip install conan} command.
To enable the experimental Conan plugin, select \uicontrol Help >
\uicontrol {About Plugins} > \uicontrol Utilities > \uicontrol Conan.
diff --git a/doc/qtcreator/src/editors/creator-code-syntax.qdoc b/doc/qtcreator/src/editors/creator-code-syntax.qdoc
index c320b192715..339b0f6f1b0 100644
--- a/doc/qtcreator/src/editors/creator-code-syntax.qdoc
+++ b/doc/qtcreator/src/editors/creator-code-syntax.qdoc
@@ -97,6 +97,8 @@
between lines can be useful if there is usually not enough space to
display annotations next to the text.
+ \image qtcreator-options-text-editor-display.png "Text Editor Display options"
+
If you hide the annotations by deselecting the check box, you can move the
mouse pointer over an icon to view them.
diff --git a/doc/qtcreator/src/editors/creator-editors-options.qdoc b/doc/qtcreator/src/editors/creator-editors-options.qdoc
index d84ecc7f4da..a2e9d740b4a 100644
--- a/doc/qtcreator/src/editors/creator-editors-options.qdoc
+++ b/doc/qtcreator/src/editors/creator-editors-options.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Creator documentation.
@@ -40,12 +40,18 @@
\title Configuring the Editor
- \QC allows you to configure the text editor to suit your specific
- needs. To configure the editor, select \uicontrol Tools >
- \uicontrol Options > \uicontrol{Text Editor}.
+ You can configure the text editor to suit your specific needs by selecting
+ \uicontrol Tools > \uicontrol Options > \uicontrol{Text Editor}.
+
+ \image qtcreator-font-colors.png "Text Editor options"
+
+ The settings you specify apply globally to all projects.
+
+ To specify editor behavior for an open project, select \uicontrol Projects >
+ \uicontrol Editor.
+
+ \image qtcreator-editor-settings.png "Editor settings"
- These settings apply to all projects. To specify editor behavior for an open
- project, select \uicontrol Projects > \uicontrol Editor.
\if defined(qtcreator)
For more information, see \l{Specifying Editor Settings}.
\endif
diff --git a/doc/qtcreator/src/meson/creator-projects-meson-building.qdoc b/doc/qtcreator/src/meson/creator-projects-meson-building.qdoc
index 288630105f4..b0f5ef2c7da 100644
--- a/doc/qtcreator/src/meson/creator-projects-meson-building.qdoc
+++ b/doc/qtcreator/src/meson/creator-projects-meson-building.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Creator documentation.
@@ -31,19 +31,22 @@
\image qtcreator-meson-build-settings.png "Meson build settings"
- Settings are grouped by category by Meson. All items are user modifiable
- except \c backend which is forced to Ninja, \c {buildtype}, \c debug as well
- as \c optimization to ensure a good compatibility with \QC.
+ Meson builds projects in the directory specified in the
+ \uicontrol {Build directory} field.
- Each setting type has its own editor. To modif any setting, double-click it,
- either edit the field, or select your choice depending on the control. To
- apply changes, select \uicontrol {Apply configuration changes}. This will
- trigger a \c {meson configure} command if there were any configuration
- changes. If for any reason the build directory configuration is broken,
- select \uicontrol {Wipe project}. This should fix any build directory.
+ Build settings are grouped by category. You can modify all settings,
+ except \c backend, which is forced to Ninja, \c {buildtype}, \c debug,
+ and \c optimization to ensure compatibility with \QC.
- \note Any modified setting will remain in bold until \uicontrol
- {Apply configuration changes} is selected.
+ To modify a setting, double-click it. Modified settings are formatted in
+ bold until you select \uicontrol {Apply configuration changes} to apply
+ them. This triggers \c {meson configure}. If problems arise, select
+ \uicontrol {Wipe Project} to fix the build directory configuration.
+
+ Meson supports cross-compiling in addition to native building. \QC
+ generates a native build file for you. To use a custom native file or a
+ cross file instead, specify the file name in \uicontrol Parameters.
+ For example, \c {--cross-file cross_file.txt}.
For more information about using Meson, see \l{Setting Up Meson}.
@@ -56,8 +59,7 @@
\image qtcreator-meson-build-steps.png "Meson build steps"
- The build errors and warnings are parsed and displayed in the
- \uicontrol Issues output pane.
+ The build errors and warnings are parsed and displayed in \l Issues.
\section1 Meson Clean Steps
diff --git a/doc/qtcreator/src/meson/creator-projects-meson.qdoc b/doc/qtcreator/src/meson/creator-projects-meson.qdoc
index f7865ee225b..664c444aa35 100644
--- a/doc/qtcreator/src/meson/creator-projects-meson.qdoc
+++ b/doc/qtcreator/src/meson/creator-projects-meson.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Creator documentation.
@@ -99,7 +99,6 @@
The following features are not supported yet:
\list
- \li Cross compilation.
\li Showing header files in project tree.
\li Configuration change detection, for example when building triggers a
Meson configuration first.
diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-creating.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-creating.qdoc
index 24b30b4a4c8..a3ad3bb15dd 100644
--- a/doc/qtcreator/src/projects/creator-only/creator-projects-creating.qdoc
+++ b/doc/qtcreator/src/projects/creator-only/creator-projects-creating.qdoc
@@ -80,7 +80,7 @@
In the first step, you select a template for the project. You can filter
templates (1) to view only those that apply to a particular target platform.
- \image qtcreator-new-qt-quick-project-wizard.png
+ \image qtcreator-new-project.png
Next, you select a location for the project and specify settings for it.
@@ -326,7 +326,7 @@
\uicontrol {New Subproject}. Follow the steps in the
\uicontrol {New Subproject} wizard to create a subproject.
- \image qtcreator-new-subproject.png
+ \image qtcreator-new-qt-quick-project.png
To add an existing project as a subproject, select
\uicontrol {Add Existing Projects} in the context menu.
diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-run-debug.qdocinc b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-run-debug.qdocinc
index ce5b73d07e7..f8c1b3bfd2a 100644
--- a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-run-debug.qdocinc
+++ b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-run-debug.qdocinc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Creator documentation.
@@ -38,6 +38,18 @@
execute any JavaScript functions. Therefore, you must make sure that the
port is properly protected by a firewall.
+ Optionally, in \uicontrol {Additional startup commands}, you can enter
+ additional settings for debugging C++:
+
+ \list
+ \li \l{Adding Custom Debugging Helpers}{Custom debugging helpers}
+ \li \l{Specifying GDB Settings}{GDB commands} to execute after GDB
+ has started, but before the debugged program is started or
+ attached, and before the debugging helpers are initialized
+ \endlist
+
+ However, you can usually leave this field empty.
+
For more information about debugging, see \l{Debugging}.
//! [run settings debugger]
diff --git a/doc/qtcreator/src/qtquick/creator-only/qtquick-creating.qdoc b/doc/qtcreator/src/qtquick/creator-only/qtquick-creating.qdoc
index c3f4d9d1bb3..7adfe570206 100644
--- a/doc/qtcreator/src/qtquick/creator-only/qtquick-creating.qdoc
+++ b/doc/qtcreator/src/qtquick/creator-only/qtquick-creating.qdoc
@@ -36,7 +36,7 @@
\title Creating Qt Quick Projects
- \image qmldesigner-new-project.png "New Project dialog"
+ \image qtcreator-new-qt-quick-project.png "New Project dialog"
The following table lists the wizard templates for creating a new
Qt Quick project from scratch.
diff --git a/doc/qtcreator/src/vcs/creator-vcs-git.qdoc b/doc/qtcreator/src/vcs/creator-vcs-git.qdoc
index 4f3842dd8c2..2f6236113ba 100644
--- a/doc/qtcreator/src/vcs/creator-vcs-git.qdoc
+++ b/doc/qtcreator/src/vcs/creator-vcs-git.qdoc
@@ -299,7 +299,7 @@
\uicontrol {Include Old Entries} and \uicontrol {Include Tags}.
To add a tag to a change in the change log, select \uicontrol Branches >
- \uicontrol Log. Select the change, and then select > \uicontrol {Add Tag
+ \uicontrol Log. Select the change, and then select \uicontrol {Add Tag
for Change} in the context menu.
If you checked out a specific commit, the list of branches displays a
diff --git a/doc/qtdesignstudio/images/icons/align-camera-on.png b/doc/qtdesignstudio/images/icons/align-camera-on.png
new file mode 100644
index 00000000000..382a2ac6b3b
Binary files /dev/null and b/doc/qtdesignstudio/images/icons/align-camera-on.png differ
diff --git a/doc/qtdesignstudio/images/icons/align-view-on.png b/doc/qtdesignstudio/images/icons/align-view-on.png
new file mode 100644
index 00000000000..3617416f6f9
Binary files /dev/null and b/doc/qtdesignstudio/images/icons/align-view-on.png differ
diff --git a/doc/qtdesignstudio/images/icons/particle-animation-off.png b/doc/qtdesignstudio/images/icons/particle-animation-off.png
new file mode 100644
index 00000000000..e3cd945edb2
Binary files /dev/null and b/doc/qtdesignstudio/images/icons/particle-animation-off.png differ
diff --git a/doc/qtdesignstudio/images/icons/particle-animation-on.png b/doc/qtdesignstudio/images/icons/particle-animation-on.png
new file mode 100644
index 00000000000..d240b34c2ab
Binary files /dev/null and b/doc/qtdesignstudio/images/icons/particle-animation-on.png differ
diff --git a/doc/qtdesignstudio/images/icons/particle-pause.png b/doc/qtdesignstudio/images/icons/particle-pause.png
new file mode 100644
index 00000000000..442a77211f0
Binary files /dev/null and b/doc/qtdesignstudio/images/icons/particle-pause.png differ
diff --git a/doc/qtdesignstudio/images/icons/particle-play.png b/doc/qtdesignstudio/images/icons/particle-play.png
new file mode 100644
index 00000000000..cc04c94897e
Binary files /dev/null and b/doc/qtdesignstudio/images/icons/particle-play.png differ
diff --git a/doc/qtdesignstudio/images/icons/particle-restart.png b/doc/qtdesignstudio/images/icons/particle-restart.png
new file mode 100644
index 00000000000..dc1c06bd49d
Binary files /dev/null and b/doc/qtdesignstudio/images/icons/particle-restart.png differ
diff --git a/doc/qtdesignstudio/images/icons/particles-seek.png b/doc/qtdesignstudio/images/icons/particles-seek.png
new file mode 100644
index 00000000000..c6c76fe4cae
Binary files /dev/null and b/doc/qtdesignstudio/images/icons/particles-seek.png differ
diff --git a/doc/qtdesignstudio/images/studio-3d-editor-axis-helper.png b/doc/qtdesignstudio/images/studio-3d-editor-axis-helper.png
index 0e0f690881b..788b4989915 100644
Binary files a/doc/qtdesignstudio/images/studio-3d-editor-axis-helper.png and b/doc/qtdesignstudio/images/studio-3d-editor-axis-helper.png differ
diff --git a/doc/qtdesignstudio/images/studio-3d-editor-move.png b/doc/qtdesignstudio/images/studio-3d-editor-move.png
index 6cc885655da..1e8be3f8651 100644
Binary files a/doc/qtdesignstudio/images/studio-3d-editor-move.png and b/doc/qtdesignstudio/images/studio-3d-editor-move.png differ
diff --git a/doc/qtdesignstudio/images/studio-3d-editor-rotate.png b/doc/qtdesignstudio/images/studio-3d-editor-rotate.png
index e53f38f9421..b6c4ce167a7 100644
Binary files a/doc/qtdesignstudio/images/studio-3d-editor-rotate.png and b/doc/qtdesignstudio/images/studio-3d-editor-rotate.png differ
diff --git a/doc/qtdesignstudio/images/studio-3d-editor-scale.png b/doc/qtdesignstudio/images/studio-3d-editor-scale.png
index 7902d021037..19d42582759 100644
Binary files a/doc/qtdesignstudio/images/studio-3d-editor-scale.png and b/doc/qtdesignstudio/images/studio-3d-editor-scale.png differ
diff --git a/doc/qtdesignstudio/images/studio-3d-editor.png b/doc/qtdesignstudio/images/studio-3d-editor.png
index 7ac86341d47..a62a6deb76a 100644
Binary files a/doc/qtdesignstudio/images/studio-3d-editor.png and b/doc/qtdesignstudio/images/studio-3d-editor.png differ
diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc
index ea415a20cc0..7b147fe684a 100644
--- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc
+++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Design Studio documentation.
@@ -26,7 +26,7 @@
/*!
\previouspage qtquick-form-editor.html
\page studio-3d-editor.html
- \nextpage quick-library.html
+ \nextpage quick-components-view.html
\title 3D Editor
@@ -64,11 +64,11 @@
affect only the local transformations of the component or whether they
transform with respect to the global space.
- Additional helpful features when editing 3D scenes are the \e {edit light},
- which is a quick way to light the scene, and the grid that helps you to
- navigate in 3D space. Select the \inlineimage grid_on.png
- (\uicontrol {Toggle Grid Visibility}) or press \key G to show or hide the
- grid.
+ Another helpful feature when editing 3D scenes is the \e {edit light},
+ which is a quick way to light the scene.
+
+ Additionally, you can toggle the visibility of the grid, selection boxes,
+ icon gizmos, and camera frustums in the 3D scene.
To refresh the contents of \uicontrol {3D Editor}, press \key P or
select the \inlineimage icons/reset.png
@@ -97,7 +97,7 @@
\li To pan, press \key Alt (or \key Option on \macos) and use the
middle mouse button to click and drag anywhere in the rendered
view to slide the view around.
- \note At the moment it is not possible to pan using Magic Mouse.
+ \note It is not possible to pan using Magic Mouse.
\li To orbit, press \key Alt and click and drag anywhere in the rendered
view to rotate the view.
\li To zoom, use the mouse wheel or press \key Alt and right-click
@@ -109,7 +109,7 @@
select \inlineimage fit_selected.png
(\uicontrol {Fit Selected}) or press \key F.
- The world axis helper (1) shows the direction of the world axes in view.
+ The world axis helper (1) shows the direction of the world axes in the view.
To point the camera at the currently selected component in the direction of
an axis, click the axis. Clicking the dot at the end of the axis will point
the camera at the opposite direction of the axis. If no component is
@@ -179,6 +179,7 @@
{keyboard shortcuts} applicable to your operating system, for example,
\key Ctrl+C and \key Ctrl+V on Windows to copy-paste components.
+ \target moving components 3d editor
\section1 Moving Components
\image studio-3d-editor-move.png "3D Editor in move mode"
@@ -233,6 +234,76 @@
gray handle at the center of the component.
\endlist
+ \section1 Aligning Views and Cameras
+
+ To align a camera to the \uicontrol {3D Editor} view:
+ \list 1
+ \li Select a camera in \uicontrol {3D Editor} or \uicontrol {Navigator}.
+ \li In \uicontrol {3D Editor},
+ select \inlineimage icons/align-camera-on.png
+ .
+ \endlist
+
+ This moves and rotates the camera so that the camera shows the same view
+ as the current view in \uicontrol {3D Editor}.
+
+ To align the \uicontrol {3D Editor} view to a camera:
+ \list 1
+ \li Select a camera in \uicontrol {3D Editor} or \uicontrol {Navigator}.
+ \li In \uicontrol {3D Editor},
+ select \inlineimage icons/align-view-on.png
+ .
+ \endlist
+
+ This copies the position as well as x and y rotation values from the
+ camera and applies them to \uicontrol {3D Editor}.
+
+ \section1 Toggling Visibility
+
+ To toggle the visibility of objects in \uicontrol {3D Editor}, select
+ \inlineimage icons/visibilityon.png
+ in the toolbar. This opens a menu with the following options:
+
+ \table
+ \row
+ \li Show Grid
+ \li Toggles the visibility of the helper grid.
+ \row
+ \li Show Selection Boxes
+ \li Toggles the visibility of selection boxes for selected 3D objects.
+ \row
+ \li Show Icon Gizmos
+ \li Toggles the visibility of icon gizmos for object such as cameras,
+ lights, and particle systems.
+ \row
+ \li Always Show Camera Frustums
+ \li Toggles between always showing the camera frustum and showing it
+ only for cameras selected in \uicontrol {3D Editor}.
+ \endtable
+
+ \section1 Particle Editor
+
+ The particle editor tools help you preview your particle systems in
+ \uicontrol {3D Editor}. You can select one particle system to preview at a
+ time.
+
+ To preview a particle system in \uicontrol{3D Editor}:
+
+ \list 1
+ \li Select a particle system in \uicontrol Navigator or
+ \uicontrol {3D Editor}.
+ \li In the \uicontrol {3D Editor}, select
+ \inlineimage icons/particle-animation-on.png
+ to activate particle animation. Now you can see the particle animation in
+ \uicontrol{3D Editor}.
+ \endlist
+
+ You can pause the particle animation by selecting
+ \inlineimage icons/particle-animation-on.png
+ . When the animation is paused, you can use
+ \inlineimage icons/particles-seek.png
+ to manually seek forward or backward in the particle animation.
+
\section1 Summary of the 3D Editor Toolbar Buttons
The \uicontrol {3D Editor} toolbar contains the following buttons:
@@ -254,7 +325,7 @@
\inlineimage move_on.png
\li Activate the Move Tool
\li \key W
- \li \l{Moving Components}
+ \li \l{moving components 3d editor}{Moving Components}
\row
\li \inlineimage rotate_off.png
\inlineimage rotate_on.png
@@ -291,17 +362,47 @@
\li \key U
\li \l{Using Edit Light}
\row
- \li \inlineimage grid_off.png
- \inlineimage grid_on.png
- \li Toggle Grid Visibility
- \li \key G
+ \li \inlineimage icons/align-camera-on.png
+ \li Align Selected Cameras to View
\li
+ \li\l{Aligning Views and Cameras}
+ \row
+ \li \inlineimage icons/align-view-on.png
+ \li Align View to Selected Camera
+ \li
+ \li \l{Aligning Views and Cameras}
+ \row
+ \li \inlineimage icons/visibilityon.png
+ \li Visibility Toggles
+ \li
+ \li \l{Toggling Visibility}
+ \row
+ \li \inlineimage icons/particles-seek.png
+ \li Seek Particle System Time
+ \li
+ \li \l{Particle Editor}
+ \row
+ \li \inlineimage icons/particle-animation-on.png
+ \inlineimage icons/particle-animation-off.png
+ \li Toggle Particle Animation
+ \li \key V
+ \li \l{Particle Editor}
+ \row
+ \li \inlineimage icons/particle-play.png
+ \inlineimage icons/particle-pause.png
+ \li Play/Pause Particles
+ \li \key ,
+ \li \l{Particle Editor}
+ \row
+ \li \inlineimage icons/particle-restart.png
+ \li Restart Particles
+ \li \key /
+ \li \l{Particle Editor}
\row
\li \inlineimage icons/reset.png
\li Reset View
- \li \key R
+ \li \key P
\li
\endtable
-
*/
diff --git a/scripts/common.py b/scripts/common.py
index a04beb27efc..6851b000e41 100644
--- a/scripts/common.py
+++ b/scripts/common.py
@@ -229,6 +229,16 @@ def codesign_call():
codesign_call.extend(signing_flags.split())
return codesign_call
+def codesign_executable(path):
+ codesign = codesign_call()
+ if not codesign:
+ return
+ entitlements_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', 'dist',
+ 'installer', 'mac', os.path.basename(path) + '.entitlements')
+ if os.path.exists(entitlements_path):
+ codesign.extend(['--entitlements', entitlements_path])
+ subprocess.check_call(codesign + [path])
+
def os_walk(path, filter, function):
for r, _, fs in os.walk(path):
for f in fs:
@@ -237,20 +247,21 @@ def os_walk(path, filter, function):
function(ff)
def conditional_sign_recursive(path, filter):
- codesign = codesign_call()
- if is_mac_platform() and codesign:
- os_walk(path, filter, lambda fp: subprocess.check_call(codesign + [fp]))
+ if is_mac_platform():
+ os_walk(path, filter, lambda fp: codesign_executable(fp))
def codesign(app_path):
+ codesign = codesign_call()
+ if not codesign or not is_mac_platform():
+ return
# sign all executables in Resources
conditional_sign_recursive(os.path.join(app_path, 'Contents', 'Resources'),
lambda ff: os.access(ff, os.X_OK))
# sign all libraries in Imports
conditional_sign_recursive(os.path.join(app_path, 'Contents', 'Imports'),
lambda ff: ff.endswith('.dylib'))
- codesign = codesign_call()
- if is_mac_platform() and codesign:
- entitlements_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', 'dist',
- 'installer', 'mac', 'entitlements.plist')
- # sign the whole bundle
- subprocess.check_call(codesign + ['--deep', app_path, '--entitlements', entitlements_path])
+
+ # sign the whole bundle
+ entitlements_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', 'dist',
+ 'installer', 'mac', 'entitlements.plist')
+ subprocess.check_call(codesign + ['--deep', app_path, '--entitlements', entitlements_path])
diff --git a/share/qtcreator/qml/qmlpuppet/commands/view3dactioncommand.h b/share/qtcreator/qml/qmlpuppet/commands/view3dactioncommand.h
index cb23b2c9ad5..cb34c253f9e 100644
--- a/share/qtcreator/qml/qmlpuppet/commands/view3dactioncommand.h
+++ b/share/qtcreator/qml/qmlpuppet/commands/view3dactioncommand.h
@@ -50,6 +50,7 @@ public:
ShowSelectionBox,
ShowIconGizmo,
ShowCameraFrustum,
+ ShowParticleEmitter,
Edit3DParticleModeToggle,
ParticlesPlay,
ParticlesRestart,
diff --git a/share/qtcreator/qml/qmlpuppet/editor3d_qt6.qrc b/share/qtcreator/qml/qmlpuppet/editor3d_qt6.qrc
index 065f9e32cf3..9831bec4be2 100644
--- a/share/qtcreator/qml/qmlpuppet/editor3d_qt6.qrc
+++ b/share/qtcreator/qml/qmlpuppet/editor3d_qt6.qrc
@@ -35,7 +35,6 @@
mockfiles/qt6/LightGizmo.qml
mockfiles/qt6/LightIconGizmo.qml
mockfiles/qt6/LightModel.qml
- mockfiles/qt6/ParticleSystemGizmo.qml
mockfiles/qt6/Line3D.qml
mockfiles/qt6/MaterialNodeView.qml
mockfiles/qt6/ModelNode2DImageView.qml
@@ -44,6 +43,8 @@
mockfiles/qt6/MoveGizmo.qml
mockfiles/qt6/NodeNodeView.qml
mockfiles/qt6/Overlay2D.qml
+ mockfiles/qt6/ParticleSystemGizmo.qml
+ mockfiles/qt6/ParticleEmitterGizmo.qml
mockfiles/qt6/PlanarDraggable.qml
mockfiles/qt6/PlanarMoveHandle.qml
mockfiles/qt6/PlanarScaleHandle.qml
diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/EditView3D.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/EditView3D.qml
index 0b15f190673..904a062a623 100644
--- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/EditView3D.qml
+++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/EditView3D.qml
@@ -385,14 +385,12 @@ Item {
lightIconGizmos[slotFound].targetNode = obj;
lightIconGizmos[slotFound].locked = _generalHelper.isLocked(obj);
lightIconGizmos[slotFound].hidden = _generalHelper.isHidden(obj);
- _generalHelper.registerGizmoTarget(obj);
return;
}
// No free gizmos available, create a new one
var gizmoComponent = Qt.createComponent("LightIconGizmo.qml");
if (gizmoComponent.status === Component.Ready) {
- _generalHelper.registerGizmoTarget(obj);
var gizmo = gizmoComponent.createObject(overlayView,
{"view3D": overlayView, "targetNode": obj,
"selectedNodes": selectedNodes, "scene": scene,
@@ -426,7 +424,6 @@ Item {
cameraGizmos[slotFound].targetNode = obj;
cameraGizmos[slotFound].locked = _generalHelper.isLocked(obj);
cameraGizmos[slotFound].hidden = _generalHelper.isHidden(obj);
- _generalHelper.registerGizmoTarget(obj);
return;
}
@@ -434,7 +431,6 @@ Item {
var gizmoComponent = Qt.createComponent("CameraGizmo.qml");
var frustumComponent = Qt.createComponent("CameraFrustum.qml");
if (gizmoComponent.status === Component.Ready && frustumComponent.status === Component.Ready) {
- _generalHelper.registerGizmoTarget(obj);
var geometryName = _generalHelper.generateUniqueName("CameraGeometry");
var frustum = frustumComponent.createObject(
overlayScene,
@@ -463,7 +459,6 @@ Item {
if (lightIconGizmos[i].targetNode === obj) {
lightIconGizmos[i].scene = null;
lightIconGizmos[i].targetNode = null;
- _generalHelper.unregisterGizmoTarget(obj);
return;
}
}
@@ -475,7 +470,6 @@ Item {
if (cameraGizmos[i].targetNode === obj) {
cameraGizmos[i].scene = null;
cameraGizmos[i].targetNode = null;
- _generalHelper.unregisterGizmoTarget(obj);
return;
}
}
diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/EditView3D.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/EditView3D.qml
index eb7d50d2e4f..25297f8fc67 100644
--- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/EditView3D.qml
+++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/EditView3D.qml
@@ -42,6 +42,7 @@ Item {
property bool showSelectionBox: true
property bool showIconGizmo: true
property bool showCameraFrustum: false
+ property bool showParticleEmitter: false
property bool usePerspective: true
property bool globalOrientation: false
property alias contentItem: contentItem
@@ -58,9 +59,10 @@ Item {
property var lightIconGizmos: []
property var cameraGizmos: []
property var particleSystemIconGizmos: []
+ property var particleEmitterGizmos: []
property var selectionBoxes: []
property rect viewPortRect: Qt.rect(0, 0, 1000, 1000)
-
+ property Node activeParticleSystem: null
property bool shuttingDown: false
property real fps: 0
@@ -70,15 +72,16 @@ Item {
signal changeObjectProperty(var objects, var propNames)
signal notifyActiveSceneChange()
- onUsePerspectiveChanged: _generalHelper.storeToolState(sceneId, "usePerspective", usePerspective)
- onShowEditLightChanged: _generalHelper.storeToolState(sceneId, "showEditLight", showEditLight)
- onGlobalOrientationChanged: _generalHelper.storeToolState(sceneId, "globalOrientation", globalOrientation)
- onShowGridChanged: _generalHelper.storeToolState(sceneId, "showGrid", showGrid);
- onShowSelectionBoxChanged: _generalHelper.storeToolState(sceneId, "showSelectionBox", showSelectionBox);
- onShowIconGizmoChanged: _generalHelper.storeToolState(sceneId, "showIconGizmo", showIconGizmo);
- onShowCameraFrustumChanged: _generalHelper.storeToolState(sceneId, "showCameraFrustum", showCameraFrustum);
- onSelectionModeChanged: _generalHelper.storeToolState(sceneId, "selectionMode", selectionMode);
- onTransformModeChanged: _generalHelper.storeToolState(sceneId, "transformMode", transformMode);
+ onUsePerspectiveChanged: _generalHelper.storeToolState(sceneId, "usePerspective", usePerspective)
+ onShowEditLightChanged: _generalHelper.storeToolState(sceneId, "showEditLight", showEditLight)
+ onGlobalOrientationChanged: _generalHelper.storeToolState(sceneId, "globalOrientation", globalOrientation)
+ onShowGridChanged: _generalHelper.storeToolState(sceneId, "showGrid", showGrid);
+ onShowSelectionBoxChanged: _generalHelper.storeToolState(sceneId, "showSelectionBox", showSelectionBox);
+ onShowIconGizmoChanged: _generalHelper.storeToolState(sceneId, "showIconGizmo", showIconGizmo);
+ onShowCameraFrustumChanged: _generalHelper.storeToolState(sceneId, "showCameraFrustum", showCameraFrustum);
+ onShowParticleEmitterChanged: _generalHelper.storeToolState(sceneId, "showParticleEmitter", showParticleEmitter);
+ onSelectionModeChanged: _generalHelper.storeToolState(sceneId, "selectionMode", selectionMode);
+ onTransformModeChanged: _generalHelper.storeToolState(sceneId, "transformMode", transformMode);
onActiveSceneChanged: updateActiveScene()
@@ -232,6 +235,11 @@ Item {
else if (resetToDefault)
showCameraFrustum = false;
+ if ("showParticleEmitter" in toolStates)
+ showParticleEmitter = toolStates.showParticleEmitter;
+ else if (resetToDefault)
+ showParticleEmitter = false;
+
if ("usePerspective" in toolStates)
usePerspective = toolStates.usePerspective;
else if (resetToDefault)
@@ -265,6 +273,7 @@ Item {
_generalHelper.storeToolState(sceneId, "showSelectionBox", showSelectionBox)
_generalHelper.storeToolState(sceneId, "showIconGizmo", showIconGizmo)
_generalHelper.storeToolState(sceneId, "showCameraFrustum", showCameraFrustum)
+ _generalHelper.storeToolState(sceneId, "showParticleEmitter", showParticleEmitter)
_generalHelper.storeToolState(sceneId, "usePerspective", usePerspective)
_generalHelper.storeToolState(sceneId, "globalOrientation", globalOrientation)
_generalHelper.storeToolState(sceneId, "selectionMode", selectionMode);
@@ -376,14 +385,12 @@ Item {
lightIconGizmos[slotFound].targetNode = obj;
lightIconGizmos[slotFound].locked = _generalHelper.isLocked(obj);
lightIconGizmos[slotFound].hidden = _generalHelper.isHidden(obj);
- _generalHelper.registerGizmoTarget(obj);
return;
}
// No free gizmos available, create a new one
var gizmoComponent = Qt.createComponent("LightIconGizmo.qml");
if (gizmoComponent.status === Component.Ready) {
- _generalHelper.registerGizmoTarget(obj);
var gizmo = gizmoComponent.createObject(overlayView,
{"view3D": overlayView, "targetNode": obj,
"selectedNodes": selectedNodes, "scene": scene,
@@ -417,7 +424,6 @@ Item {
cameraGizmos[slotFound].targetNode = obj;
cameraGizmos[slotFound].locked = _generalHelper.isLocked(obj);
cameraGizmos[slotFound].hidden = _generalHelper.isHidden(obj);
- _generalHelper.registerGizmoTarget(obj);
return;
}
@@ -425,7 +431,6 @@ Item {
var gizmoComponent = Qt.createComponent("CameraGizmo.qml");
var frustumComponent = Qt.createComponent("CameraFrustum.qml");
if (gizmoComponent.status === Component.Ready && frustumComponent.status === Component.Ready) {
- _generalHelper.registerGizmoTarget(obj);
var geometryName = _generalHelper.generateUniqueName("CameraGeometry");
var frustum = frustumComponent.createObject(
overlayScene,
@@ -466,26 +471,66 @@ Item {
particleSystemIconGizmos[slotFound].targetNode = obj;
particleSystemIconGizmos[slotFound].locked = _generalHelper.isLocked(obj);
particleSystemIconGizmos[slotFound].hidden = _generalHelper.isHidden(obj);
- _generalHelper.registerGizmoTarget(obj);
return;
}
// No free gizmos available, create a new one
var gizmoComponent = Qt.createComponent("ParticleSystemGizmo.qml");
if (gizmoComponent.status === Component.Ready) {
- _generalHelper.registerGizmoTarget(obj);
var gizmo = gizmoComponent.createObject(overlayView,
{"view3D": overlayView, "targetNode": obj,
"selectedNodes": selectedNodes, "scene": scene,
"activeScene": activeScene,
"locked": _generalHelper.isLocked(obj),
"hidden": _generalHelper.isHidden(obj),
- "globalShow": showIconGizmo});
+ "globalShow": showIconGizmo,
+ "activeParticleSystem": activeParticleSystem});
particleSystemIconGizmos[particleSystemIconGizmos.length] = gizmo;
gizmo.clicked.connect(handleObjectClicked);
gizmo.selectedNodes = Qt.binding(function() {return selectedNodes;});
gizmo.activeScene = Qt.binding(function() {return activeScene;});
gizmo.globalShow = Qt.binding(function() {return showIconGizmo;});
+ gizmo.activeParticleSystem = Qt.binding(function() {return activeParticleSystem;});
+ }
+ }
+
+ function addParticleEmitterGizmo(scene, obj)
+ {
+ // Insert into first available gizmo if we don't already have gizmo for this object
+ var slotFound = -1;
+ for (var i = 0; i < particleEmitterGizmos.length; ++i) {
+ if (!particleEmitterGizmos[i].targetNode) {
+ slotFound = i;
+ } else if (particleEmitterGizmos[i].targetNode === obj) {
+ particleEmitterGizmos[i].scene = scene;
+ return;
+ }
+ }
+
+ if (slotFound !== -1) {
+ particleEmitterGizmos[slotFound].scene = scene;
+ particleEmitterGizmos[slotFound].targetNode = obj;
+ particleEmitterGizmos[slotFound].hidden = _generalHelper.isHidden(obj);
+ particleEmitterGizmos[slotFound].systemHidden = _generalHelper.isHidden(obj.system);
+ return;
+ }
+
+ // No free gizmos available, create a new one
+ var gizmoComponent = Qt.createComponent("ParticleEmitterGizmo.qml");
+ if (gizmoComponent.status === Component.Ready) {
+ var gizmo = gizmoComponent.createObject(
+ overlayScene,
+ {"targetNode": obj, "selectedNodes": selectedNodes,
+ "activeParticleSystem": activeParticleSystem, "scene": scene,
+ "activeScene": activeScene, "hidden": _generalHelper.isHidden(obj),
+ "systemHidden": _generalHelper.isHidden(obj.system),
+ "globalShow": showParticleEmitter});
+
+ particleEmitterGizmos[particleEmitterGizmos.length] = gizmo;
+ gizmo.selectedNodes = Qt.binding(function() {return selectedNodes;});
+ gizmo.activeParticleSystem = Qt.binding(function() {return activeParticleSystem;});
+ gizmo.globalShow = Qt.binding(function() {return showParticleEmitter;});
+ gizmo.activeScene = Qt.binding(function() {return activeScene;});
}
}
@@ -495,7 +540,6 @@ Item {
if (lightIconGizmos[i].targetNode === obj) {
lightIconGizmos[i].scene = null;
lightIconGizmos[i].targetNode = null;
- _generalHelper.unregisterGizmoTarget(obj);
return;
}
}
@@ -507,7 +551,6 @@ Item {
if (cameraGizmos[i].targetNode === obj) {
cameraGizmos[i].scene = null;
cameraGizmos[i].targetNode = null;
- _generalHelper.unregisterGizmoTarget(obj);
return;
}
}
@@ -519,7 +562,17 @@ Item {
if (particleSystemIconGizmos[i].targetNode === obj) {
particleSystemIconGizmos[i].scene = null;
particleSystemIconGizmos[i].targetNode = null;
- _generalHelper.unregisterGizmoTarget(obj);
+ return;
+ }
+ }
+ }
+
+ function releaseParticleEmitterGizmo(obj)
+ {
+ for (var i = 0; i < particleEmitterGizmos.length; ++i) {
+ if (particleEmitterGizmos[i].targetNode === obj) {
+ particleEmitterGizmos[i].scene = null;
+ particleEmitterGizmos[i].targetNode = null;
return;
}
}
@@ -555,6 +608,16 @@ Item {
}
}
+ function updateParticleEmitterGizmoScene(scene, obj)
+ {
+ for (var i = 0; i < particleEmitterGizmos.length; ++i) {
+ if (particleEmitterGizmos[i].targetNode === obj) {
+ particleEmitterGizmos[i].scene = scene;
+ return;
+ }
+ }
+ }
+
Component.onCompleted: {
createEditView();
selectObjects([]);
@@ -588,7 +651,6 @@ Item {
return;
}
}
-
}
function onHiddenStateChanged(node)
{
@@ -610,6 +672,15 @@ Item {
return;
}
}
+ for (var i = 0; i < particleEmitterGizmos.length; ++i) {
+ if (particleEmitterGizmos[i].targetNode === node) {
+ particleEmitterGizmos[i].hidden = _generalHelper.isHidden(node);
+ return;
+ } else if (particleEmitterGizmos[i].targetNode && particleEmitterGizmos[i].targetNode.system === node) {
+ particleEmitterGizmos[i].systemHidden = _generalHelper.isHidden(node);
+ return;
+ }
+ }
}
}
@@ -810,9 +881,16 @@ Item {
onPressed: (mouse)=> {
if (viewRoot.editView) {
- var pickResult = _generalHelper.pickViewAt(viewRoot.editView, mouse.x, mouse.y);
- handleObjectClicked(_generalHelper.resolvePick(pickResult.objectHit),
- mouse.modifiers & Qt.ControlModifier);
+ // First pick overlay to check for hits there
+ var pickResult = _generalHelper.pickViewAt(overlayView, mouse.x, mouse.y);
+ var resolvedResult = _generalHelper.resolvePick(pickResult.objectHit);
+ if (!resolvedResult) {
+ // No hits from overlay view, pick the main scene
+ pickResult = _generalHelper.pickViewAt(viewRoot.editView, mouse.x, mouse.y);
+ resolvedResult = _generalHelper.resolvePick(pickResult.objectHit);
+ }
+
+ handleObjectClicked(resolvedResult, mouse.modifiers & Qt.ControlModifier);
if (pickResult.objectHit) {
if (transformMode === EditView3D.TransformMode.Move)
diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/IconGizmo.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/IconGizmo.qml
index 7752693d559..2b78dd04aac 100644
--- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/IconGizmo.qml
+++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/IconGizmo.qml
@@ -47,6 +47,7 @@ Item {
property bool locked: false
property bool globalShow: true
property bool canBeVisible: activeScene === scene && !hidden && (targetNode ? targetNode.visible : false)
+ property real iconOpacity: selected ? 0.2 : 1
property alias iconSource: iconImage.source
@@ -76,7 +77,7 @@ Item {
border.color: "#7777ff"
border.width: !iconGizmo.locked && iconGizmo.highlightOnHover && iconGizmo.hasMouse ? 2 : 0
radius: 5
- opacity: iconGizmo.selected ? 0.2 : 1
+ opacity: iconGizmo.iconOpacity
Image {
id: iconImage
fillMode: Image.Pad
diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ParticleEmitterGizmo.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ParticleEmitterGizmo.qml
new file mode 100644
index 00000000000..a06a38a2024
--- /dev/null
+++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ParticleEmitterGizmo.qml
@@ -0,0 +1,127 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick3D
+import QtQuick3D.Particles3D
+
+Node {
+ id: root
+
+ property Node targetNode: null
+ property var selectedNodes: []
+ property Node activeParticleSystem: null
+ property Node scene: null
+ property Node activeScene: null
+ property bool hidden: false
+ property bool systemHidden: false
+ property Node shapeModel: null
+ property bool globalShow: false
+ property bool canBeVisible: activeScene === scene && targetNode && !hidden && !systemHidden
+ property bool partOfActiveSystem: root.targetNode && root.targetNode.system === activeParticleSystem
+
+ opacity: 0.15
+
+ readonly property bool selected: selectedNodes.includes(targetNode)
+
+ visible: canBeVisible && (globalShow || selected)
+
+ position: targetNode ? targetNode.scenePosition : Qt.vector3d(0, 0, 0)
+ rotation: targetNode ? targetNode.sceneRotation : Qt.quaternion(1, 0, 0, 0)
+ scale: targetNode ? targetNode.sceneScale : Qt.vector3d(1, 1, 1)
+
+ function basicShape()
+ {
+ if (targetNode && targetNode.shape && targetNode.shape instanceof ParticleShape3D) {
+ if (targetNode.shape.type === ParticleShape3D.Cube)
+ return "#Cube";
+ else if (targetNode.shape.type === ParticleShape3D.Cylinder)
+ return "#Cylinder";
+ }
+ return "#Sphere";
+ }
+
+ function updateShape()
+ {
+ if (shapeModel)
+ shapeModel.destroy();
+
+ if (!targetNode)
+ return;
+
+ if (targetNode.shape instanceof ParticleModelShape3D) {
+ shapeModel = _generalHelper.createParticleEmitterGizmoModel(targetNode, defaultMaterial);
+ shapeModel.parent = root;
+ }
+ }
+
+ Component.onCompleted: {
+ updateShape();
+ }
+
+ Connections {
+ target: targetNode
+ function onSystemChanged() { systemHidden = _generalHelper.isHidden(system); }
+ }
+
+ Connections {
+ target: targetNode
+ function onShapeChanged() { updateShape(); }
+ }
+
+ Connections {
+ target: targetNode.shape instanceof ParticleModelShape3D ? targetNode.shape
+ :null
+ function onDelegateChanged() { updateShape(); }
+ }
+
+ Connections {
+ target: targetNode.shape instanceof ParticleModelShape3D ? targetNode.shape.delegate
+ : null
+ function onSourceChanged() { updateShape(); }
+ }
+
+ Model {
+ readonly property Node _pickTarget: root.targetNode
+ materials: [defaultMaterial]
+ source: basicShape()
+ scale: root.targetNode && root.targetNode.shape && targetNode.shape instanceof ParticleShape3D
+ ? root.targetNode.shape.extents.times(0.02) // default extent is 50
+ : autoScale.getScale(Qt.vector3d(0.1, 0.1, 0.1))
+ visible: !shapeModel
+ }
+
+ AutoScaleHelper {
+ id: autoScale
+ view3D: overlayView
+ }
+
+ DefaultMaterial {
+ id: defaultMaterial
+ diffuseColor: root.selected ? "#FF0000" : partOfActiveSystem ? "#FFFF00" : "#AAAAAA"
+ lighting: DefaultMaterial.NoLighting
+ cullMode: Material.NoCulling
+ }
+}
diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ParticleSystemGizmo.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ParticleSystemGizmo.qml
index de591fcd217..3d498ce335d 100644
--- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ParticleSystemGizmo.qml
+++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ParticleSystemGizmo.qml
@@ -28,5 +28,9 @@ import QtQuick3D 6.0
IconGizmo {
id: particleSystemGizmo
+
+ property Node activeParticleSystem: null
+
iconSource: "qrc:///qtquickplugin/mockfiles/images/editor_particlesystem.png"
+ iconOpacity: selected || activeParticleSystem == targetNode ? 0.2 : 1
}
diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/RotateRing.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/RotateRing.qml
index 8003839a894..79d31f45943 100644
--- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/RotateRing.qml
+++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/RotateRing.qml
@@ -54,6 +54,7 @@ Model {
Model {
id: pickModel
+ readonly property bool _edit3dLocked: true // Make this non-pickable in main picking handling
objectName: "PickModel for " + rotateRing.objectName
source: "../meshes/ringselect.mesh"
pickable: true
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp
index 9d7d4b05ce9..66cd2d80b8e 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp
@@ -46,6 +46,12 @@
#include
#include
+#ifdef QUICK3D_PARTICLES_MODULE
+#include
+#include
+#include
+#endif
+
#include
namespace QmlDesigner {
@@ -404,22 +410,6 @@ QQuick3DNode *GeneralHelper::resolvePick(QQuick3DNode *pickNode)
return pickNode;
}
-void GeneralHelper::registerGizmoTarget(QQuick3DNode *node)
-{
- if (!m_gizmoTargets.contains(node)) {
- m_gizmoTargets.insert(node);
- node->installEventFilter(this);
- }
-}
-
-void GeneralHelper::unregisterGizmoTarget(QQuick3DNode *node)
-{
- if (m_gizmoTargets.contains(node)) {
- m_gizmoTargets.remove(node);
- node->removeEventFilter(this);
- }
-}
-
bool GeneralHelper::isLocked(QQuick3DNode *node) const
{
if (node) {
@@ -460,6 +450,31 @@ bool GeneralHelper::isPickable(QQuick3DNode *node) const
return true;
}
+// Emitter gizmo model creation is done in C++ as creating dynamic properties and
+// assigning materials to dynamically created models is lot simpler in C++
+QQuick3DNode *GeneralHelper::createParticleEmitterGizmoModel(QQuick3DNode *emitter,
+ QQuick3DMaterial *material) const
+{
+#ifdef QUICK3D_PARTICLES_MODULE
+ auto e = qobject_cast(emitter);
+ if (!e || qobject_cast(e) || !material)
+ return nullptr;
+
+ auto shape = qobject_cast(e->shape());
+ if (shape && shape->delegate()) {
+ if (auto model = qobject_cast(
+ shape->delegate()->create(shape->delegate()->creationContext()))) {
+ QQmlEngine::setObjectOwnership(model, QQmlEngine::JavaScriptOwnership);
+ model->setProperty("_pickTarget", QVariant::fromValue(emitter));
+ QQmlListReference matRef(model, "materials");
+ matRef.append(material);
+ return model;
+ }
+ }
+#endif
+ return nullptr;
+}
+
void GeneralHelper::storeToolState(const QString &sceneId, const QString &tool, const QVariant &state,
int delay)
{
@@ -713,21 +728,6 @@ bool GeneralHelper::isRotationBlocked(QQuick3DNode *node) const
return m_rotationBlockedNodes.contains(node);
}
-bool GeneralHelper::eventFilter(QObject *obj, QEvent *event)
-{
- if (event->type() == QEvent::DynamicPropertyChange) {
- auto node = qobject_cast(obj);
- if (m_gizmoTargets.contains(node)) {
- auto de = static_cast(event);
- if (de->propertyName() == "_edit3dLocked")
- emit lockedStateChanged(node);
- else if (de->propertyName() == "_edit3dHidden")
- emit hiddenStateChanged(node);
- }
- }
- return QObject::eventFilter(obj, event);
-}
-
void GeneralHelper::handlePendingToolStateUpdate()
{
m_toolStateUpdateTimer.stop();
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h
index 46e6019dd58..ac67172ab32 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h
@@ -41,6 +41,7 @@ QT_BEGIN_NAMESPACE
class QQuick3DCamera;
class QQuick3DNode;
class QQuick3DViewport;
+class QQuick3DMaterial;
class QQuickItem;
QT_END_NAMESPACE
@@ -84,11 +85,11 @@ public:
Q_INVOKABLE QQuick3DPickResult pickViewAt(QQuick3DViewport *view, float posX, float posY);
Q_INVOKABLE QQuick3DNode *resolvePick(QQuick3DNode *pickNode);
- Q_INVOKABLE void registerGizmoTarget(QQuick3DNode *node);
- Q_INVOKABLE void unregisterGizmoTarget(QQuick3DNode *node);
Q_INVOKABLE bool isLocked(QQuick3DNode *node) const;
Q_INVOKABLE bool isHidden(QQuick3DNode *node) const;
Q_INVOKABLE bool isPickable(QQuick3DNode *node) const;
+ Q_INVOKABLE QQuick3DNode *createParticleEmitterGizmoModel(QQuick3DNode *emitter,
+ QQuick3DMaterial *material) const;
Q_INVOKABLE void storeToolState(const QString &sceneId, const QString &tool,
const QVariant &state, int delayEmit = 0);
@@ -123,9 +124,6 @@ signals:
void lockedStateChanged(QQuick3DNode *node);
void rotationBlocksChanged();
-protected:
- bool eventFilter(QObject *obj, QEvent *event) final;
-
private:
void handlePendingToolStateUpdate();
QVector3D pivotScenePosition(QQuick3DNode *node) const;
@@ -136,7 +134,6 @@ private:
QTimer m_toolStateUpdateTimer;
QHash m_toolStates;
QHash m_toolStatesPending;
- QSet m_gizmoTargets;
QSet m_rotationBlockedNodes;
struct MultiSelData {
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp
index e2bd56562d5..9656e0e5e89 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp
@@ -113,6 +113,7 @@
#include
#include
#include
+#include
#endif
#ifdef IMPORT_QUICK3D_ASSETS
@@ -427,16 +428,25 @@ void Qt5InformationNodeInstanceServer::resetParticleSystem()
void Qt5InformationNodeInstanceServer::handleParticleSystemSelected(QQuick3DParticleSystem* targetParticleSystem)
{
- if (!m_particleAnimationDriver || targetParticleSystem == m_targetParticleSystem)
+ if (targetParticleSystem == m_targetParticleSystem)
return;
- m_particleAnimationDriver->reset();
// stop the previously selected from animating
resetParticleSystem();
m_targetParticleSystem = targetParticleSystem;
+ if (m_editView3DData.rootItem) {
+ QQmlProperty systemProperty(m_editView3DData.rootItem, "activeParticleSystem", context());
+ systemProperty.write(objectToVariant(m_targetParticleSystem));
+ }
+
+ if (!m_particleAnimationDriver)
+ return;
+
+ // Ensure clean slate for newly selected system
resetParticleSystem();
+
#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 2)
QObject::disconnect(m_particleAnimationConnection);
m_particleAnimationConnection = connect(m_particleAnimationDriver, &AnimationDriver::advanced, [this] () {
@@ -507,9 +517,17 @@ static QQuick3DParticleSystem *parentParticleSystem(QObject *selectedObject)
return nullptr;
}
-void Qt5InformationNodeInstanceServer::handleParticleSystemDeselected(QObject *selectedObject)
+void Qt5InformationNodeInstanceServer::handleParticleSystemDeselected()
{
+ resetParticleSystem();
+
m_targetParticleSystem = nullptr;
+
+ if (m_editView3DData.rootItem) {
+ QQmlProperty systemProperty(m_editView3DData.rootItem, "activeParticleSystem", context());
+ systemProperty.write(objectToVariant(nullptr));
+ }
+
const auto anim = animations();
int i = 0;
for (auto a : anim) {
@@ -526,7 +544,7 @@ void Qt5InformationNodeInstanceServer::handleParticleSystemDeselected(QObject *s
void Qt5InformationNodeInstanceServer::handleSelectionChanged(const QVariant &objs)
{
#ifdef QUICK3D_PARTICLES_MODULE
- resetParticleSystem();
+ bool skipSystemDeselect = m_targetParticleSystem == nullptr;
#endif
QList instanceList;
const QVariantList varObjs = objs.value();
@@ -535,8 +553,20 @@ void Qt5InformationNodeInstanceServer::handleSelectionChanged(const QVariant &ob
if (obj) {
ServerNodeInstance instance = instanceForObject(obj);
instanceList << instance;
+#ifdef QUICK3D_PARTICLES_MODULE
+ if (!skipSystemDeselect) {
+ auto particleSystem = parentParticleSystem(instance.internalObject());
+ skipSystemDeselect = particleSystem == m_targetParticleSystem;
+ }
+#endif
}
}
+
+#ifdef QUICK3D_PARTICLES_MODULE
+ if (m_targetParticleSystem && !skipSystemDeselect)
+ handleParticleSystemDeselected();
+#endif
+
selectInstances(instanceList);
// Hold selection changes reflected back from designer for a bit
m_selectionChangeTimer.start(500);
@@ -745,6 +775,10 @@ void Qt5InformationNodeInstanceServer::handleNode3DDestroyed(QObject *obj)
} else if (qobject_cast(obj)) {
QMetaObject::invokeMethod(m_editView3DData.rootItem, "releaseParticleSystemGizmo",
Q_ARG(QVariant, objectToVariant(obj)));
+ } else if (qobject_cast(obj)
+ && !qobject_cast(obj)) {
+ QMetaObject::invokeMethod(m_editView3DData.rootItem, "releaseParticleEmitterGizmo",
+ Q_ARG(QVariant, objectToVariant(obj)));
#endif
}
removeNode3D(obj);
@@ -853,6 +887,11 @@ void Qt5InformationNodeInstanceServer::resolveSceneRoots()
QMetaObject::invokeMethod(m_editView3DData.rootItem, "updateParticleSystemGizmoScene",
Q_ARG(QVariant, objectToVariant(newRoot)),
Q_ARG(QVariant, objectToVariant(node)));
+ } else if (qobject_cast(node)
+ && !qobject_cast(node)) {
+ QMetaObject::invokeMethod(m_editView3DData.rootItem, "updateParticleEmitterGizmoScene",
+ Q_ARG(QVariant, objectToVariant(newRoot)),
+ Q_ARG(QVariant, objectToVariant(node)));
#endif
}
}
@@ -1415,15 +1454,19 @@ void Qt5InformationNodeInstanceServer::createCameraAndLightGizmos(
QHash cameras;
QHash lights;
QHash particleSystems;
+ QHash particleEmitters;
for (const ServerNodeInstance &instance : instanceList) {
- if (instance.isSubclassOf("QQuick3DCamera"))
+ if (instance.isSubclassOf("QQuick3DCamera")) {
cameras[find3DSceneRoot(instance)] << instance.internalObject();
- else if (instance.isSubclassOf("QQuick3DAbstractLight"))
+ } else if (instance.isSubclassOf("QQuick3DAbstractLight")) {
lights[find3DSceneRoot(instance)] << instance.internalObject();
- else if (instance.isSubclassOf("QQuick3DParticleSystem"))
+ } else if (instance.isSubclassOf("QQuick3DParticleSystem")) {
particleSystems[find3DSceneRoot(instance)] << instance.internalObject();
-
+ } else if (instance.isSubclassOf("QQuick3DParticleEmitter")
+ && !instance.isSubclassOf("QQuick3DParticleTrailEmitter")) {
+ particleEmitters[find3DSceneRoot(instance)] << instance.internalObject();
+ }
}
auto cameraIt = cameras.constBegin();
@@ -1456,6 +1499,17 @@ void Qt5InformationNodeInstanceServer::createCameraAndLightGizmos(
}
++particleIt;
}
+
+ auto emitterIt = particleEmitters.constBegin();
+ while (emitterIt != particleEmitters.constEnd()) {
+ const auto emitterObjs = emitterIt.value();
+ for (auto &obj : emitterObjs) {
+ QMetaObject::invokeMethod(m_editView3DData.rootItem, "addParticleEmitterGizmo",
+ Q_ARG(QVariant, objectToVariant(emitterIt.key())),
+ Q_ARG(QVariant, objectToVariant(obj)));
+ }
+ ++emitterIt;
+ }
}
void Qt5InformationNodeInstanceServer::add3DViewPorts(const QList &instanceList)
@@ -1924,6 +1978,9 @@ void Qt5InformationNodeInstanceServer::changeSelection(const ChangeSelectionComm
QVariantList selectedObjs;
QObject *firstSceneRoot = nullptr;
ServerNodeInstance firstInstance;
+#ifdef QUICK3D_PARTICLES_MODULE
+ QList selectedParticleSystems;
+#endif
for (qint32 id : instanceIds) {
if (hasInstanceForId(id)) {
ServerNodeInstance instance = instanceForId(id);
@@ -1937,17 +1994,12 @@ void Qt5InformationNodeInstanceServer::changeSelection(const ChangeSelectionComm
object = instance.internalObject();
#ifdef QUICK3D_PARTICLES_MODULE
- auto particlesystem = qobject_cast(instance.internalObject());
- if (particlesystem) {
- handleParticleSystemSelected(particlesystem);
- } else {
- particlesystem = parentParticleSystem(instance.internalObject());
- if (particlesystem) {
- if (particlesystem != m_targetParticleSystem)
- handleParticleSystemSelected(particlesystem);
- } else {
- handleParticleSystemDeselected(instance.internalObject());
- }
+ if (selectedParticleSystems.size() <= 1) {
+ auto particleSystem = qobject_cast(instance.internalObject());
+ if (!particleSystem)
+ particleSystem = parentParticleSystem(instance.internalObject());
+ if (particleSystem && !selectedParticleSystems.contains(particleSystem))
+ selectedParticleSystems.append(particleSystem);
}
#endif
auto isSelectableAsRoot = [&]() -> bool {
@@ -1957,6 +2009,8 @@ void Qt5InformationNodeInstanceServer::changeSelection(const ChangeSelectionComm
|| qobject_cast(object)
#ifdef QUICK3D_PARTICLES_MODULE
|| qobject_cast(object)
+ || qobject_cast(object)
+
#endif
) {
return true;
@@ -1978,6 +2032,14 @@ void Qt5InformationNodeInstanceServer::changeSelection(const ChangeSelectionComm
}
}
+#ifdef QUICK3D_PARTICLES_MODULE
+ // We only support exactly one active particle systems at a time
+ if (selectedParticleSystems.size() == 1)
+ handleParticleSystemSelected(selectedParticleSystems[0]);
+ else
+ handleParticleSystemDeselected();
+#endif
+
if (firstSceneRoot && m_active3DScene != firstSceneRoot) {
m_active3DScene = firstSceneRoot;
m_active3DView = findView3DForInstance(firstInstance);
@@ -2105,6 +2167,9 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c
updatedState.insert("showCameraFrustum", command.isEnabled());
break;
#ifdef QUICK3D_PARTICLES_MODULE
+ case View3DActionCommand::ShowParticleEmitter:
+ updatedState.insert("showParticleEmitter", command.isEnabled());
+ break;
case View3DActionCommand::ParticlesPlay:
m_particleAnimationPlaying = command.isEnabled();
updatedState.insert("particlePlay", command.isEnabled());
@@ -2220,8 +2285,12 @@ void Qt5InformationNodeInstanceServer::handleInstanceLocked(const ServerNodeInst
QObject *obj = instance.internalObject();
auto node = qobject_cast(obj);
- if (node)
+ if (node) {
node->setProperty("_edit3dLocked", edit3dLocked);
+ auto helper = qobject_cast(m_3dHelper);
+ if (helper)
+ emit helper->lockedStateChanged(node);
+ }
const auto children = obj->children();
for (auto child : children) {
if (hasInstanceForObject(child)) {
@@ -2276,6 +2345,9 @@ void Qt5InformationNodeInstanceServer::handleInstanceHidden(const ServerNodeInst
// as changes in the node tree (reparenting, adding new nodes) can make the previously set
// hide status based on ancestor unreliable.
node->setProperty("_edit3dHidden", edit3dHidden);
+ auto helper = qobject_cast(m_3dHelper);
+ if (helper)
+ emit helper->hiddenStateChanged(node);
#if QT_VERSION < QT_VERSION_CHECK(6, 2, 1)
if (auto model = qobject_cast(node))
model->setPickable(!edit3dHidden); // allow 3D objects to receive mouse clicks
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h
index 2f6331e8dd4..8f08dec3892 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h
@@ -151,7 +151,7 @@ private:
#ifdef QUICK3D_PARTICLES_MODULE
void handleParticleSystemSelected(QQuick3DParticleSystem* targetParticleSystem);
void resetParticleSystem();
- void handleParticleSystemDeselected(QObject *selectedObject);
+ void handleParticleSystemDeselected();
#endif
RenderViewData m_editView3DData;
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.h
index 6c97658466f..799d27054d4 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.h
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.h
@@ -49,7 +49,7 @@ protected:
private:
ServerNodeInstance m_currentState;
- QSize m_previewSize{160, 160};
+ QSize m_previewSize{320, 320};
};
} // namespace QmlDesigner
diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml
index ad4ec007ccd..5ac4d0fc6eb 100644
--- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml
+++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml
@@ -32,45 +32,67 @@ import StudioControls 1.0 as StudioControls
import StudioTheme 1.0 as StudioTheme
Item {
- id: rootItem
+ id: root
property var selectedAssets: ({})
property int allExpandedState: 0
property string contextFilePath: ""
property var contextDir: undefined
property bool isDirContextMenu: false
+ property var dropExtFiles: [] // array of supported externally dropped files
function clearSearchFilter()
{
searchBox.text = "";
}
- DropArea {
+ function updateDropExtFiles(drag)
+ {
+ root.dropExtFiles = []
+ for (const u of drag.urls) {
+ var url = u.toString();
+ if (url.startsWith("file:///")) // remove file scheme (happens on Windows)
+ url = url.substr(8)
+
+ var ext = url.slice(url.lastIndexOf('.') + 1).toLowerCase()
+ if (rootView.supportedDropSuffixes().includes('*.' + ext))
+ root.dropExtFiles.push(url)
+ }
+
+ drag.accepted = root.dropExtFiles.length > 0
+ }
+
+ DropArea { // handles external drop on empty area of the view (goes to root folder)
id: dropArea
-
- property var files // list of supported dropped files
-
- enabled: true
- anchors.fill: parent
+ y: assetsView.y + assetsView.contentHeight + 5
+ width: parent.width
+ height: parent.height - y
onEntered: (drag)=> {
- files = []
- for (var i = 0; i < drag.urls.length; ++i) {
- var url = drag.urls[i].toString();
- if (url.startsWith("file:///")) // remove file scheme (happens on Windows)
- url = url.substr(8)
- var ext = url.slice(url.lastIndexOf('.') + 1).toLowerCase()
- if (rootView.supportedDropSuffixes().includes('*.' + ext))
- files.push(url)
- }
-
- if (files.length === 0)
- drag.accepted = false;
+ root.updateDropExtFiles(drag)
}
onDropped: {
- if (files.length > 0)
- rootView.handleFilesDrop(files)
+ rootView.handleExtFilesDrop(root.dropExtFiles, assetsModel.rootDir().dirPath)
+ }
+
+ Canvas { // marker for the drop area
+ id: dropCanvas
+ anchors.fill: parent
+ visible: dropArea.containsDrag
+
+ onWidthChanged: dropCanvas.requestPaint()
+ onHeightChanged: dropCanvas.requestPaint()
+
+ onPaint: {
+ var ctx = getContext("2d")
+ ctx.reset()
+ ctx.strokeStyle = StudioTheme.Values.themeInteraction
+ ctx.lineWidth = 2
+ ctx.setLineDash([4, 4])
+ ctx.rect(5, 5, dropCanvas.width - 10, dropCanvas.height - 10)
+ ctx.stroke()
+ }
}
}
@@ -79,9 +101,9 @@ Item {
acceptedButtons: Qt.RightButton
onClicked: {
if (!assetsModel.isEmpty) {
- contextFilePath = ""
- contextDir = assetsModel.rootDir()
- isDirContextMenu = false
+ root.contextFilePath = ""
+ root.contextDir = assetsModel.rootDir()
+ root.isDirContextMenu = false
contextMenu.popup()
}
}
@@ -91,8 +113,8 @@ Item {
function handleViewFocusOut()
{
contextMenu.close()
- selectedAssets = {}
- selectedAssetsChanged()
+ root.selectedAssets = {}
+ root.selectedAssetsChanged()
}
StudioControls.Menu {
@@ -100,42 +122,50 @@ Item {
closePolicy: Popup.CloseOnPressOutside | Popup.CloseOnEscape
+ onOpened: {
+ var numSelected = Object.values(root.selectedAssets).filter(p => p).length
+ deleteFileItem.text = numSelected > 1 ? qsTr("Delete Files") : qsTr("Delete File")
+ }
+
StudioControls.MenuItem {
text: qsTr("Expand All")
- enabled: allExpandedState !== 1
- visible: isDirContextMenu
+ enabled: root.allExpandedState !== 1
+ visible: root.isDirContextMenu
height: visible ? implicitHeight : 0
onTriggered: assetsModel.toggleExpandAll(true)
}
StudioControls.MenuItem {
text: qsTr("Collapse All")
- enabled: allExpandedState !== 2
- visible: isDirContextMenu
+ enabled: root.allExpandedState !== 2
+ visible: root.isDirContextMenu
height: visible ? implicitHeight : 0
onTriggered: assetsModel.toggleExpandAll(false)
}
StudioControls.MenuSeparator {
- visible: isDirContextMenu
+ visible: root.isDirContextMenu
height: visible ? StudioTheme.Values.border : 0
}
StudioControls.MenuItem {
+ id: deleteFileItem
text: qsTr("Delete File")
- visible: contextFilePath
- height: visible ? implicitHeight : 0
- onTriggered: assetsModel.deleteFile(contextFilePath)
+ visible: root.contextFilePath
+ height: deleteFileItem.visible ? deleteFileItem.implicitHeight : 0
+ onTriggered: {
+ assetsModel.deleteFiles(Object.keys(root.selectedAssets).filter(p => root.selectedAssets[p]))
+ }
}
StudioControls.MenuSeparator {
- visible: contextFilePath
+ visible: root.contextFilePath
height: visible ? StudioTheme.Values.border : 0
}
StudioControls.MenuItem {
text: qsTr("Rename Folder")
- visible: isDirContextMenu
+ visible: root.isDirContextMenu
height: visible ? implicitHeight : 0
onTriggered: renameFolderDialog.open()
}
@@ -147,14 +177,14 @@ Item {
StudioControls.MenuItem {
text: qsTr("Delete Folder")
- visible: isDirContextMenu
+ visible: root.isDirContextMenu
height: visible ? implicitHeight : 0
onTriggered: {
- var dirEmpty = !(contextDir.dirsModel && contextDir.dirsModel.rowCount() > 0)
- && !(contextDir.filesModel && contextDir.filesModel.rowCount() > 0);
+ var dirEmpty = !(root.contextDir.dirsModel && root.contextDir.dirsModel.rowCount() > 0)
+ && !(root.contextDir.filesModel && root.contextDir.filesModel.rowCount() > 0);
if (dirEmpty)
- assetsModel.deleteFolder(contextDir.dirPath)
+ assetsModel.deleteFolder(root.contextDir.dirPath)
else
confirmDeleteFolderDialog.open()
}
@@ -235,7 +265,7 @@ Item {
text: qsTr("Rename")
enabled: folderRename.text !== ""
onClicked: {
- var success = assetsModel.renameFolder(contextDir.dirPath, folderRename.text)
+ var success = assetsModel.renameFolder(root.contextDir.dirPath, folderRename.text)
if (success)
renameFolderDialog.accept()
@@ -251,7 +281,7 @@ Item {
}
onOpened: {
- folderRename.text = contextDir.dirName
+ folderRename.text = root.contextDir.dirName
folderRename.selectAll()
folderRename.forceActiveFocus()
renameFolderDialog.renameError = false
@@ -309,7 +339,7 @@ Item {
text: qsTr("Create")
enabled: folderName.text !== ""
onClicked: {
- assetsModel.addNewFolder(contextDir.dirPath + '/' + folderName.text)
+ assetsModel.addNewFolder(root.contextDir.dirPath + '/' + folderName.text)
newFolderDialog.accept()
}
}
@@ -345,7 +375,7 @@ Item {
id: folderNotEmpty
text: qsTr("Folder \"%1\" is not empty. Delete it anyway?")
- .arg(contextDir ? contextDir.dirName : "")
+ .arg(root.contextDir ? root.contextDir.dirName : "")
color: StudioTheme.Values.themeTextColor
wrapMode: Text.WordWrap
width: confirmDeleteFolderDialog.width
@@ -373,7 +403,7 @@ Item {
text: qsTr("Delete")
onClicked: {
- assetsModel.deleteFolder(contextDir.dirPath)
+ assetsModel.deleteFolder(root.contextDir.dirPath)
confirmDeleteFolderDialog.accept()
}
}
@@ -433,7 +463,7 @@ Item {
spacing: 20
x: 20
- width: rootItem.width - 2 * x
+ width: root.width - 2 * x
anchors.verticalCenter: parent.verticalCenter
Text {
@@ -492,6 +522,8 @@ Item {
id: dirSection
Section {
+ id: section
+
width: assetsView.width -
(assetsView.verticalScrollBarVisible ? assetsView.verticalThickness : 0) - 5
caption: dirName
@@ -506,16 +538,31 @@ Item {
visible: dirVisible
expandOnClick: false
useDefaulContextMenu: false
+ dropEnabled: true
onToggleExpand: {
dirExpanded = !dirExpanded
}
+ onDropEnter: (drag)=> {
+ root.updateDropExtFiles(drag)
+ section.highlight = drag.accepted
+ }
+
+ onDropExit: {
+ section.highlight = false
+ }
+
+ onDrop: {
+ section.highlight = false
+ rootView.handleExtFilesDrop(root.dropExtFiles, dirPath)
+ }
+
onShowContextMenu: {
- contextFilePath = ""
- contextDir = model
- isDirContextMenu = true
- allExpandedState = assetsModel.getAllExpandedState()
+ root.contextFilePath = ""
+ root.contextDir = model
+ root.isDirContextMenu = true
+ root.allExpandedState = assetsModel.getAllExpandedState()
contextMenu.popup()
}
@@ -544,9 +591,9 @@ Item {
anchors.fill: parent
acceptedButtons: Qt.RightButton
onClicked: {
- contextFilePath = ""
- contextDir = model
- isDirContextMenu = true
+ root.contextFilePath = ""
+ root.contextDir = model
+ root.isDirContextMenu = true
contextMenu.popup()
}
}
@@ -562,9 +609,10 @@ Item {
width: assetsView.width -
(assetsView.verticalScrollBarVisible ? assetsView.verticalThickness : 0)
height: img.height
- color: selectedAssets[filePath] ? StudioTheme.Values.themeInteraction
- : (mouseArea.containsMouse ? StudioTheme.Values.themeSectionHeadBackground
- : "transparent")
+ color: root.selectedAssets[filePath]
+ ? StudioTheme.Values.themeInteraction
+ : (mouseArea.containsMouse ? StudioTheme.Values.themeSectionHeadBackground
+ : "transparent")
Row {
spacing: 5
@@ -601,28 +649,31 @@ Item {
onPositionChanged: tooltipBackend.reposition()
onPressed: (mouse)=> {
forceActiveFocus()
+ var ctrlDown = mouse.modifiers & Qt.ControlModifier
if (mouse.button === Qt.LeftButton) {
- var ctrlDown = mouse.modifiers & Qt.ControlModifier
- if (!selectedAssets[filePath] && !ctrlDown)
- selectedAssets = {}
- currFileSelected = ctrlDown ? !selectedAssets[filePath] : true
- selectedAssets[filePath] = currFileSelected
- selectedAssetsChanged()
+ if (!root.selectedAssets[filePath] && !ctrlDown)
+ root.selectedAssets = {}
+ currFileSelected = ctrlDown ? !root.selectedAssets[filePath] : true
+ root.selectedAssets[filePath] = currFileSelected
+ root.selectedAssetsChanged()
- var selectedAssetsArr = []
- for (var assetPath in selectedAssets) {
- if (selectedAssets[assetPath])
- selectedAssetsArr.push(assetPath)
+ if (currFileSelected) {
+ rootView.startDragAsset(
+ Object.keys(root.selectedAssets).filter(p => root.selectedAssets[p]),
+ mapToGlobal(mouse.x, mouse.y))
}
-
- if (currFileSelected)
- rootView.startDragAsset(selectedAssetsArr, mapToGlobal(mouse.x, mouse.y))
} else {
- contextFilePath = filePath
- contextDir = model.fileDir
+ if (!root.selectedAssets[filePath] && !ctrlDown)
+ root.selectedAssets = {}
+ currFileSelected = root.selectedAssets[filePath] || !ctrlDown
+ root.selectedAssets[filePath] = currFileSelected
+ root.selectedAssetsChanged()
+
+ root.contextFilePath = filePath
+ root.contextDir = model.fileDir
+ root.isDirContextMenu = false
tooltipBackend.hideTooltip()
- isDirContextMenu = false
contextMenu.popup()
}
}
@@ -630,9 +681,9 @@ Item {
onReleased: (mouse)=> {
if (mouse.button === Qt.LeftButton) {
if (!(mouse.modifiers & Qt.ControlModifier))
- selectedAssets = {}
- selectedAssets[filePath] = currFileSelected
- selectedAssetsChanged()
+ root.selectedAssets = {}
+ root.selectedAssets[filePath] = currFileSelected
+ root.selectedAssetsChanged()
}
}
diff --git a/share/qtcreator/qmldesigner/newprojectdialog/NewProjectDialog.qml b/share/qtcreator/qmldesigner/newprojectdialog/NewProjectDialog.qml
index 9176af9e34d..4cc82bc79cc 100644
--- a/share/qtcreator/qmldesigner/newprojectdialog/NewProjectDialog.qml
+++ b/share/qtcreator/qmldesigner/newprojectdialog/NewProjectDialog.qml
@@ -32,6 +32,7 @@ import StudioTheme as StudioTheme
import StudioControls as SC
import NewProjectDialog
+import BackendApi
Item {
id: rootDialog
@@ -161,17 +162,43 @@ Item {
readonly property int animDur: 500
id: tabBar
x: 10 // left padding
- width: parent.width - 64 // right padding
- height: DialogValues.projectViewHeaderHeight
+ width: parent.width - 20 // right padding
+ height: DialogValues.presetViewHeaderHeight
color: DialogValues.lightPaneColor
+ function selectTab(tabIndex, selectLast = false) {
+ var item = repeater.itemAt(tabIndex)
+ tabBarRow.currIndex = tabIndex
+
+ presetView.selectLast = selectLast
+ BackendApi.presetModel.setPage(tabIndex) // NOTE: it resets preset model
+ }
+
+ Connections {
+ target: BackendApi
+
+ function onUserPresetSaved() {
+ var customTabIndex = repeater.count - 1
+ tabBar.selectTab(customTabIndex, true)
+ }
+
+ function onLastUserPresetRemoved() {
+ tabBar.selectTab(0, false)
+ }
+ }
+
Row {
id: tabBarRow
spacing: 20
property int currIndex: 0
+ readonly property string currentTabName:
+ repeater.count > 0 && repeater.itemAt(currIndex)
+ ? repeater.itemAt(currIndex).text
+ : ''
Repeater {
- model: categoryModel
+ id: repeater
+ model: BackendApi.categoryModel
Text {
text: name
font.weight: Font.DemiBold
@@ -184,13 +211,7 @@ Item {
MouseArea {
anchors.fill: parent
onClicked: {
- tabBarRow.currIndex = index
- presetModel.setPage(index)
- projectView.currentIndex = 0
- projectView.currentIndexChanged()
-
- strip.x = parent.x
- strip.width = parent.width
+ tabBar.selectTab(index)
}
}
@@ -199,8 +220,19 @@ Item {
} // tabBarRow
Rectangle {
+ function computeX() {
+ var item = tabBarRow.children[tabBarRow.currIndex] ?? tabBarRow.children[0]
+ return item.x;
+ }
+
+ function computeWidth() {
+ var item = tabBarRow.children[tabBarRow.currIndex] ?? tabBarRow.children[0]
+ return item.width;
+ }
+
id: strip
- width: tabBarRow.children[0].width
+ x: computeX()
+ width: computeWidth()
height: 5
radius: 2
color: DialogValues.textColorInteraction
@@ -209,35 +241,40 @@ Item {
Behavior on x { SmoothedAnimation { duration: tabBar.animDur } }
Behavior on width { SmoothedAnimation { duration: strip.width === 0 ? 0 : tabBar.animDur } } // do not animate initial width
}
-
- Connections {
- target: rootDialog
- function onWidthChanged() {
- if (rootDialog.width < 1200) { // 1200 = the width threshold
- tabBar.width = tabBar.parent.width - 20
- projectView.width = projectView.parent.width - 20
- } else {
- tabBar.width = tabBar.parent.width - 64
- projectView.width = projectView.parent.width - 64
- }
- }
- }
} // Rectangle
- NewProjectView {
- id: projectView
+ Rectangle {
+ id: presetViewFrame
x: 10 // left padding
- width: parent.width - 64 // right padding
- height: DialogValues.projectViewHeight
- loader: projectDetailsLoader
+ width: parent.width - 20 // right padding
+ height: DialogValues.presetViewHeight
+ color: DialogValues.darkPaneColor
- Connections {
- target: rootDialog
- function onHeightChanged() {
- if (rootDialog.height < 700) { // 700 = minimum height big dialog
- projectView.height = DialogValues.projectViewHeight / 2
- } else {
- projectView.height = DialogValues.projectViewHeight
+ Item {
+ anchors.fill: parent
+ anchors.margins: DialogValues.gridMargins
+
+ NewProjectView {
+ id: presetView
+ anchors.fill: parent
+
+ loader: projectDetailsLoader
+ currentTabName: tabBarRow.currentTabName
+
+ Connections {
+ target: rootDialog
+ function onHeightChanged() {
+ if (rootDialog.height < 720) { // 720 = minimum height big dialog
+ DialogValues.presetViewHeight =
+ DialogValues.presetItemHeight
+ + 2 * DialogValues.gridMargins
+ } else {
+ DialogValues.presetViewHeight =
+ DialogValues.presetItemHeight * 2
+ + DialogValues.gridSpacing
+ + 2 * DialogValues.gridMargins
+ }
+ }
}
}
}
@@ -247,12 +284,12 @@ Item {
Text {
id: descriptionText
- text: dialogBox.projectDescription
+ text: BackendApi.projectDescription
font.pixelSize: DialogValues.defaultPixelSize
lineHeight: DialogValues.defaultLineHeight
lineHeightMode: Text.FixedHeight
leftPadding: 14
- width: projectView.width
+ width: presetViewFrame.width
color: DialogValues.textColor
wrapMode: Text.WordWrap
maximumLineCount: 4
@@ -298,7 +335,7 @@ Item {
iconFont: StudioTheme.Constants.font
onClicked: {
- dialogBox.reject();
+ BackendApi.reject();
}
}
@@ -310,11 +347,11 @@ Item {
visible: true
buttonIcon: qsTr("Create")
iconSize: DialogValues.defaultPixelSize
- enabled: dialogBox.fieldsValid
+ enabled: BackendApi.fieldsValid
iconFont: StudioTheme.Constants.font
onClicked: {
- dialogBox.accept();
+ BackendApi.accept();
}
}
} // RowLayout
diff --git a/share/qtcreator/qmldesigner/newprojectdialog/image/style-basic.png b/share/qtcreator/qmldesigner/newprojectdialog/image/style-basic.png
index 389a4eab873..d139d6e0266 100644
Binary files a/share/qtcreator/qmldesigner/newprojectdialog/image/style-basic.png and b/share/qtcreator/qmldesigner/newprojectdialog/image/style-basic.png differ
diff --git a/share/qtcreator/qmldesigner/newprojectdialog/image/style-basic@2x.png b/share/qtcreator/qmldesigner/newprojectdialog/image/style-basic@2x.png
index 3636791da1e..03f87cc4b7d 100644
Binary files a/share/qtcreator/qmldesigner/newprojectdialog/image/style-basic@2x.png and b/share/qtcreator/qmldesigner/newprojectdialog/image/style-basic@2x.png differ
diff --git a/share/qtcreator/qmldesigner/newprojectdialog/image/style-default.png b/share/qtcreator/qmldesigner/newprojectdialog/image/style-default.png
index 6c50e4e1fa9..ceb573b339f 100644
Binary files a/share/qtcreator/qmldesigner/newprojectdialog/image/style-default.png and b/share/qtcreator/qmldesigner/newprojectdialog/image/style-default.png differ
diff --git a/share/qtcreator/qmldesigner/newprojectdialog/image/style-default@2x.png b/share/qtcreator/qmldesigner/newprojectdialog/image/style-default@2x.png
index 09ea1ce94d5..dd86707377b 100644
Binary files a/share/qtcreator/qmldesigner/newprojectdialog/image/style-default@2x.png and b/share/qtcreator/qmldesigner/newprojectdialog/image/style-default@2x.png differ
diff --git a/share/qtcreator/qmldesigner/newprojectdialog/image/style-fusion.png b/share/qtcreator/qmldesigner/newprojectdialog/image/style-fusion.png
index 745828c1f7e..d196646855f 100644
Binary files a/share/qtcreator/qmldesigner/newprojectdialog/image/style-fusion.png and b/share/qtcreator/qmldesigner/newprojectdialog/image/style-fusion.png differ
diff --git a/share/qtcreator/qmldesigner/newprojectdialog/image/style-fusion@2x.png b/share/qtcreator/qmldesigner/newprojectdialog/image/style-fusion@2x.png
index c2232f3a754..6bdb9952a4b 100644
Binary files a/share/qtcreator/qmldesigner/newprojectdialog/image/style-fusion@2x.png and b/share/qtcreator/qmldesigner/newprojectdialog/image/style-fusion@2x.png differ
diff --git a/share/qtcreator/qmldesigner/newprojectdialog/image/style-imagine.png b/share/qtcreator/qmldesigner/newprojectdialog/image/style-imagine.png
index 40c96993a0c..76612e8a8e8 100644
Binary files a/share/qtcreator/qmldesigner/newprojectdialog/image/style-imagine.png and b/share/qtcreator/qmldesigner/newprojectdialog/image/style-imagine.png differ
diff --git a/share/qtcreator/qmldesigner/newprojectdialog/image/style-imagine@2x.png b/share/qtcreator/qmldesigner/newprojectdialog/image/style-imagine@2x.png
index c4ef9b7854d..34c41c3587f 100644
Binary files a/share/qtcreator/qmldesigner/newprojectdialog/image/style-imagine@2x.png and b/share/qtcreator/qmldesigner/newprojectdialog/image/style-imagine@2x.png differ
diff --git a/share/qtcreator/qmldesigner/newprojectdialog/image/style-macOs@2x.png b/share/qtcreator/qmldesigner/newprojectdialog/image/style-macOs@2x.png
deleted file mode 100644
index 09ea1ce94d5..00000000000
Binary files a/share/qtcreator/qmldesigner/newprojectdialog/image/style-macOs@2x.png and /dev/null differ
diff --git a/share/qtcreator/qmldesigner/newprojectdialog/image/style-macos.png b/share/qtcreator/qmldesigner/newprojectdialog/image/style-macos.png
index 6c50e4e1fa9..ceb573b339f 100644
Binary files a/share/qtcreator/qmldesigner/newprojectdialog/image/style-macos.png and b/share/qtcreator/qmldesigner/newprojectdialog/image/style-macos.png differ
diff --git a/share/qtcreator/qmldesigner/newprojectdialog/image/style-macos@2x.png b/share/qtcreator/qmldesigner/newprojectdialog/image/style-macos@2x.png
new file mode 100644
index 00000000000..dd86707377b
Binary files /dev/null and b/share/qtcreator/qmldesigner/newprojectdialog/image/style-macos@2x.png differ
diff --git a/share/qtcreator/qmldesigner/newprojectdialog/image/style-material_dark.png b/share/qtcreator/qmldesigner/newprojectdialog/image/style-material_dark.png
index 515f5e0b8e4..cf7891232a9 100644
Binary files a/share/qtcreator/qmldesigner/newprojectdialog/image/style-material_dark.png and b/share/qtcreator/qmldesigner/newprojectdialog/image/style-material_dark.png differ
diff --git a/share/qtcreator/qmldesigner/newprojectdialog/image/style-material_dark@2x.png b/share/qtcreator/qmldesigner/newprojectdialog/image/style-material_dark@2x.png
index 1967a363dc0..b353dfa6d9d 100644
Binary files a/share/qtcreator/qmldesigner/newprojectdialog/image/style-material_dark@2x.png and b/share/qtcreator/qmldesigner/newprojectdialog/image/style-material_dark@2x.png differ
diff --git a/share/qtcreator/qmldesigner/newprojectdialog/image/style-material_light.png b/share/qtcreator/qmldesigner/newprojectdialog/image/style-material_light.png
index d41b3c5ff88..e826ad00811 100644
Binary files a/share/qtcreator/qmldesigner/newprojectdialog/image/style-material_light.png and b/share/qtcreator/qmldesigner/newprojectdialog/image/style-material_light.png differ
diff --git a/share/qtcreator/qmldesigner/newprojectdialog/image/style-material_light@2x.png b/share/qtcreator/qmldesigner/newprojectdialog/image/style-material_light@2x.png
index 03c1ae66037..2d3c7c53b6b 100644
Binary files a/share/qtcreator/qmldesigner/newprojectdialog/image/style-material_light@2x.png and b/share/qtcreator/qmldesigner/newprojectdialog/image/style-material_light@2x.png differ
diff --git a/share/qtcreator/qmldesigner/newprojectdialog/image/style-universal_dark.png b/share/qtcreator/qmldesigner/newprojectdialog/image/style-universal_dark.png
index 339342bd913..9a9988e91d6 100644
Binary files a/share/qtcreator/qmldesigner/newprojectdialog/image/style-universal_dark.png and b/share/qtcreator/qmldesigner/newprojectdialog/image/style-universal_dark.png differ
diff --git a/share/qtcreator/qmldesigner/newprojectdialog/image/style-universal_dark@2x.png b/share/qtcreator/qmldesigner/newprojectdialog/image/style-universal_dark@2x.png
index 2ec60f0453c..bd778aaa071 100644
Binary files a/share/qtcreator/qmldesigner/newprojectdialog/image/style-universal_dark@2x.png and b/share/qtcreator/qmldesigner/newprojectdialog/image/style-universal_dark@2x.png differ
diff --git a/share/qtcreator/qmldesigner/newprojectdialog/image/style-universal_light.png b/share/qtcreator/qmldesigner/newprojectdialog/image/style-universal_light.png
index 7b457bae8f4..169581e05dd 100644
Binary files a/share/qtcreator/qmldesigner/newprojectdialog/image/style-universal_light.png and b/share/qtcreator/qmldesigner/newprojectdialog/image/style-universal_light.png differ
diff --git a/share/qtcreator/qmldesigner/newprojectdialog/image/style-universal_light@2x.png b/share/qtcreator/qmldesigner/newprojectdialog/image/style-universal_light@2x.png
index 22e200c0d70..80239a95a80 100644
Binary files a/share/qtcreator/qmldesigner/newprojectdialog/image/style-universal_light@2x.png and b/share/qtcreator/qmldesigner/newprojectdialog/image/style-universal_light@2x.png differ
diff --git a/share/qtcreator/qmldesigner/newprojectdialog/image/style-universal_system.png b/share/qtcreator/qmldesigner/newprojectdialog/image/style-universal_system.png
index 65fbae504ff..08872abd229 100644
Binary files a/share/qtcreator/qmldesigner/newprojectdialog/image/style-universal_system.png and b/share/qtcreator/qmldesigner/newprojectdialog/image/style-universal_system.png differ
diff --git a/share/qtcreator/qmldesigner/newprojectdialog/image/style-universal_system@2x.png b/share/qtcreator/qmldesigner/newprojectdialog/image/style-universal_system@2x.png
index 5d05168537d..5b06ca29874 100644
Binary files a/share/qtcreator/qmldesigner/newprojectdialog/image/style-universal_system@2x.png and b/share/qtcreator/qmldesigner/newprojectdialog/image/style-universal_system@2x.png differ
diff --git a/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/Details.qml b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/Details.qml
index d5c37651dce..b348a6da350 100644
--- a/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/Details.qml
+++ b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/Details.qml
@@ -31,16 +31,13 @@ import QtQuick.Layouts
import StudioControls as SC
import StudioTheme as StudioTheme
+import BackendApi
+
Item {
width: DialogValues.detailsPaneWidth
- Component.onCompleted: {
- dialogBox.detailsLoaded = true;
- }
-
- Component.onDestruction: {
- dialogBox.detailsLoaded = false;
- }
+ Component.onCompleted: BackendApi.detailsLoaded = true
+ Component.onDestruction: BackendApi.detailsLoaded = false
Rectangle {
color: DialogValues.darkPaneColor
@@ -53,13 +50,13 @@ Item {
Column {
anchors.fill: parent
- spacing: DialogValues.defaultPadding
+ spacing: 5
Text {
id: detailsHeading
text: qsTr("Details")
height: DialogValues.paneTitleTextHeight
- width: parent.width;
+ width: parent.width
font.weight: Font.DemiBold
font.pixelSize: DialogValues.paneTitlePixelSize
lineHeight: DialogValues.paneTitleLineHeight
@@ -71,39 +68,36 @@ Item {
Flickable {
width: parent.width
height: parent.height - detailsHeading.height - DialogValues.defaultPadding
-
+ - savePresetButton.height
contentWidth: parent.width
contentHeight: scrollContent.height
boundsBehavior: Flickable.StopAtBounds
clip: true
- ScrollBar.vertical: SC.VerticalScrollBar {
- }
+ ScrollBar.vertical: SC.VerticalScrollBar {}
Column {
id: scrollContent
width: parent.width - DialogValues.detailsPanePadding
- height: DialogValues.detailsScrollableContentHeight
spacing: DialogValues.defaultPadding
SC.TextField {
id: projectNameTextField
actionIndicatorVisible: false
translationIndicatorVisible: false
- text: dialogBox.projectName
+ text: BackendApi.projectName
width: parent.width
color: DialogValues.textColor
selectByMouse: true
+ font.pixelSize: DialogValues.defaultPixelSize
onEditingFinished: {
text = text.charAt(0).toUpperCase() + text.slice(1)
}
-
- font.pixelSize: DialogValues.defaultPixelSize
}
Binding {
- target: dialogBox
+ target: BackendApi
property: "projectName"
value: projectNameTextField.text
}
@@ -118,14 +112,14 @@ Item {
id: projectLocationTextField
actionIndicatorVisible: false
translationIndicatorVisible: false
- text: dialogBox.projectLocation
+ text: BackendApi.projectLocation
color: DialogValues.textColor
selectByMouse: true
font.pixelSize: DialogValues.defaultPixelSize
}
Binding {
- target: dialogBox
+ target: BackendApi
property: "projectLocation"
value: projectLocationTextField.text
}
@@ -138,7 +132,7 @@ Item {
iconFont: StudioTheme.Constants.font
onClicked: {
- var newLocation = dialogBox.chooseProjectLocation()
+ var newLocation = BackendApi.chooseProjectLocation()
if (newLocation)
projectLocationTextField.text = newLocation
}
@@ -159,7 +153,7 @@ Item {
Text {
id: statusMessage
- text: dialogBox.statusMessage
+ text: BackendApi.statusMessage
font.pixelSize: DialogValues.defaultPixelSize
lineHeight: DialogValues.defaultLineHeight
lineHeightMode: Text.FixedHeight
@@ -172,7 +166,7 @@ Item {
states: [
State {
name: "warning"
- when: dialogBox.statusType === "warning"
+ when: BackendApi.statusType === "warning"
PropertyChanges {
target: statusMessage
color: DialogValues.textWarning
@@ -185,7 +179,7 @@ Item {
State {
name: "error"
- when: dialogBox.statusType === "error"
+ when: BackendApi.statusType === "error"
PropertyChanges {
target: statusMessage
color: DialogValues.textError
@@ -208,7 +202,7 @@ Item {
}
Binding {
- target: dialogBox
+ target: BackendApi
property: "saveAsDefaultLocation"
value: defaultLocationCheckbox.checked
}
@@ -219,25 +213,25 @@ Item {
id: screenSizeComboBox
actionIndicatorVisible: false
currentIndex: -1
- model: screenSizeModel
+ model: BackendApi.screenSizeModel
textRole: "display"
width: parent.width
font.pixelSize: DialogValues.defaultPixelSize
onActivated: (index) => {
- dialogBox.setScreenSizeIndex(index);
+ BackendApi.setScreenSizeIndex(index);
- var size = screenSizeModel.screenSizes(index);
+ var size = BackendApi.screenSizeModel.screenSizes(index);
widthField.realValue = size.width;
heightField.realValue = size.height;
}
Connections {
- target: screenSizeModel
+ target: BackendApi.screenSizeModel
function onModelReset() {
var newIndex = screenSizeComboBox.currentIndex > -1
? screenSizeComboBox.currentIndex
- : dialogBox.screenSizeIndex()
+ : BackendApi.screenSizeIndex()
screenSizeComboBox.currentIndex = newIndex
screenSizeComboBox.activated(newIndex)
@@ -248,10 +242,8 @@ Item {
GridLayout { // orientation + width + height
width: parent.width
height: 85
-
columns: 4
rows: 2
-
columnSpacing: 10
rowSpacing: 10
@@ -295,10 +287,7 @@ Item {
font.pixelSize: DialogValues.defaultPixelSize
onRealValueChanged: {
- var height = heightField.realValue
- var width = realValue
-
- if (width >= height)
+ if (widthField.realValue >= heightField.realValue)
orientationButton.setHorizontal()
else
orientationButton.setVertical()
@@ -306,7 +295,7 @@ Item {
} // Width Text Field
Binding {
- target: dialogBox
+ target: BackendApi
property: "customWidth"
value: widthField.realValue
}
@@ -323,10 +312,7 @@ Item {
font.pixelSize: DialogValues.defaultPixelSize
onRealValueChanged: {
- var height = realValue
- var width = widthField.realValue
-
- if (width >= height)
+ if (widthField.realValue >= heightField.realValue)
orientationButton.setHorizontal()
else
orientationButton.setVertical()
@@ -334,7 +320,7 @@ Item {
} // Height Text Field
Binding {
- target: dialogBox
+ target: BackendApi
property: "customHeight"
value: heightField.realValue
}
@@ -345,7 +331,6 @@ Item {
id: orientationButton
implicitWidth: 100
implicitHeight: 50
-
checked: false
hoverEnabled: false
background: Rectangle {
@@ -384,19 +369,22 @@ Item {
onClicked: {
if (widthField.realValue && heightField.realValue) {
- [widthField.realValue, heightField.realValue] = [heightField.realValue, widthField.realValue];
- checked = !checked
+ [widthField.realValue, heightField.realValue] = [heightField.realValue, widthField.realValue]
+ orientationButton.checked = !orientationButton.checked
+
+ if (widthField.realValue === heightField.realValue)
+ orientationButton.checked ? setVertical() : setHorizontal()
}
}
function setHorizontal() {
- checked = false
+ orientationButton.checked = false
horizontalBar.color = DialogValues.textColorInteraction
verticalBar.color = "white"
}
function setVertical() {
- checked = true
+ orientationButton.checked = true
horizontalBar.color = "white"
verticalBar.color = DialogValues.textColorInteraction
}
@@ -404,23 +392,27 @@ Item {
} // GridLayout: orientation + width + height
- Rectangle { width: parent.width; height: 1; color: DialogValues.dividerlineColor }
+ Rectangle {
+ width: parent.width
+ height: 1
+ color: DialogValues.dividerlineColor
+ }
SC.CheckBox {
id: useQtVirtualKeyboard
actionIndicatorVisible: false
text: qsTr("Use Qt Virtual Keyboard")
font.pixelSize: DialogValues.defaultPixelSize
- checked: dialogBox.useVirtualKeyboard
- visible: dialogBox.haveVirtualKeyboard
+ checked: BackendApi.useVirtualKeyboard
+ visible: BackendApi.haveVirtualKeyboard
}
RowLayout { // Target Qt Version
width: parent.width
- visible: dialogBox.haveTargetQtVersion
+ visible: BackendApi.haveTargetQtVersion
Text {
- text: "Target Qt Version:"
+ text: qsTr("Target Qt Version:")
font.pixelSize: DialogValues.defaultPixelSize
lineHeight: DialogValues.defaultLineHeight
lineHeightMode: Text.FixedHeight
@@ -432,33 +424,98 @@ Item {
actionIndicatorVisible: false
implicitWidth: 70
Layout.alignment: Qt.AlignRight
- currentIndex: 1
+ currentIndex: BackendApi.targetQtVersionIndex
font.pixelSize: DialogValues.defaultPixelSize
model: ListModel {
- ListElement {
- name: "Qt 5"
- }
- ListElement {
- name: "Qt 6"
- }
+ ListElement { name: "Qt 5" }
+ ListElement { name: "Qt 6" }
}
onActivated: (index) => {
- dialogBox.setTargetQtVersion(index)
+ BackendApi.targetQtVersionIndex = index
}
} // Target Qt Version ComboBox
+
+ Binding {
+ target: BackendApi
+ property: "targetQtVersionIndex"
+ value: qtVersionComboBox.currentIndex
+ }
+
} // RowLayout
Binding {
- target: dialogBox
+ target: BackendApi
property: "useVirtualKeyboard"
value: useQtVirtualKeyboard.checked
}
} // ScrollContent Column
} // ScrollView
-
} // Column
+
+ SC.AbstractButton {
+ id: savePresetButton
+ width: StudioTheme.Values.singleControlColumnWidth
+ buttonIcon: qsTr("Save Custom Preset")
+ iconFont: StudioTheme.Constants.font
+ iconSize: DialogValues.defaultPixelSize
+ anchors.bottom: parent.bottom
+ anchors.horizontalCenter: parent.horizontalCenter
+
+ onClicked: savePresetDialog.open()
+ }
+
+ PopupDialog {
+ id: savePresetDialog
+ title: qsTr("Save Preset")
+ standardButtons: Dialog.Save | Dialog.Cancel
+ modal: true
+ closePolicy: Popup.CloseOnEscape
+ anchors.centerIn: parent
+ width: DialogValues.popupDialogWidth
+
+ onAccepted: BackendApi.savePresetDialogAccept()
+
+ onOpened: {
+ presetNameTextField.selectAll()
+ presetNameTextField.forceActiveFocus()
+ }
+
+ ColumnLayout {
+ width: parent.width
+ spacing: 10
+
+ Text {
+ text: qsTr("Preset name")
+ font.pixelSize: DialogValues.defaultPixelSize
+ color: DialogValues.textColor
+ }
+
+ SC.TextField {
+ id: presetNameTextField
+ actionIndicatorVisible: false
+ translationIndicatorVisible: false
+ text: qsTr("MyPreset")
+ color: DialogValues.textColor
+ font.pixelSize: DialogValues.defaultPixelSize
+ Layout.fillWidth: true
+ maximumLength: 30
+ validator: RegularExpressionValidator { regularExpression: /\w[\w ]*/ }
+
+ onEditingFinished: {
+ presetNameTextField.text = text.trim()
+ presetNameTextField.text = text.replace(/\s+/g, " ")
+ }
+ }
+
+ Binding {
+ target: BackendApi
+ property: "presetName"
+ value: presetNameTextField.text
+ }
+ }
+ }
} // Item
- }
-}
+ } // Rectangle
+} // root Item
diff --git a/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/DialogValues.qml b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/DialogValues.qml
index ae26727e07b..d85093738d8 100644
--- a/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/DialogValues.qml
+++ b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/DialogValues.qml
@@ -29,39 +29,57 @@ import QtQml
import StudioTheme as StudioTheme
QtObject {
+ id: root
+
readonly property int dialogWidth: 1522
readonly property int dialogHeight: 940
- readonly property int projectViewMinimumWidth: 600
- readonly property int projectViewMinimumHeight: projectViewHeight
- readonly property int dialogContentHeight: projectViewHeight + 300 // i.e. dialog without header and footer
- readonly property int loadedPanesWidth: detailsPaneWidth + stylesPaneWidth
- readonly property int detailsPaneWidth: 330 + detailsPanePadding * 2
+ readonly property int presetViewMinimumWidth: 600
+ readonly property int presetViewMinimumHeight: root.gridCellHeight
+ readonly property int dialogContentHeight: root.presetViewHeight + 300 // i.e. dialog without header and footer
+ readonly property int loadedPanesWidth: root.detailsPaneWidth + root.stylesPaneWidth
+ readonly property int detailsPaneWidth: 330 + root.detailsPanePadding * 2
readonly property int dialogTitleTextHeight: 85
readonly property int paneTitleTextHeight: 47
readonly property int logoWidth: 85
readonly property int logoHeight: 85
- /* detailsScrollableContentHeight - the full height that may need to be scrolled to be fully
- visible, if the dialog box is too small. */
- readonly property int detailsScrollableContentHeight: 428
- readonly property int stylesPaneWidth: styleImageWidth + stylesPanePadding * 2 + styleImageBorderWidth * 2 // i.e. 240px
+ readonly property int stylesPaneWidth: root.styleImageWidth + root.stylesPanePadding * 2
+ + root.styleImageBorderWidth * 2 // i.e. 240px
readonly property int detailsPanePadding: 18
readonly property int stylesPanePadding: 18
readonly property int defaultPadding: 18
readonly property int dialogLeftPadding: 35
+ readonly property int styleListItemHeight: root.styleImageHeight + root.styleTextHeight
+ + 2 * root.styleImageBorderWidth
+ + root.styleListItemBottomMargin
+ + root.styleListItemSpacing
+ readonly property int styleListItemBottomMargin: 10
+ readonly property int styleListItemSpacing: 4
readonly property int styleImageWidth: 200
+ readonly property int styleImageHeight: 262
readonly property int styleImageBorderWidth: 2
+ readonly property int styleTextHeight: 18
+
readonly property int footerHeight: 73
- readonly property int projectItemWidth: 90
- readonly property int projectItemHeight: 144
- readonly property int projectViewHeight: projectItemHeight * 2
- readonly property int projectViewHeaderHeight: 38
+ readonly property int presetItemWidth: 136
+ readonly property int presetItemHeight: 110
+ property int presetViewHeight: root.presetItemHeight * 2 + root.gridSpacing + root.gridMargins * 2
+ readonly property int presetViewHeaderHeight: 38
+
+ readonly property int gridMargins: 20
+ readonly property int gridCellWidth: root.presetItemWidth + root.gridSpacing
+ readonly property int gridCellHeight: root.presetItemHeight + root.gridSpacing
+ readonly property int gridSpacing: 2
readonly property int dialogButtonWidth: 100
- readonly property int loadedPanesHeight: dialogContentHeight
- readonly property int detailsPaneHeight: dialogContentHeight
+ // This is for internal popup dialogs
+ readonly property int popupDialogWidth: 270
+ readonly property int popupDialogPadding: 12
+
+ readonly property int loadedPanesHeight: root.dialogContentHeight
+ readonly property int detailsPaneHeight: root.dialogContentHeight
readonly property string darkPaneColor: StudioTheme.Values.themeBackgroundColorNormal
readonly property string lightPaneColor: StudioTheme.Values.themeBackgroundColorAlternate
@@ -71,6 +89,8 @@ QtObject {
readonly property string dividerlineColor: StudioTheme.Values.themeTextColorDisabled
readonly property string textError: StudioTheme.Values.themeError
readonly property string textWarning: StudioTheme.Values.themeWarning
+ readonly property string presetItemBackgroundHover: StudioTheme.Values.themeControlBackgroundGlobalHover
+ readonly property string presetItemBackgroundHoverInteraction: StudioTheme.Values.themeControlBackgroundInteraction
readonly property real defaultPixelSize: 14
readonly property real defaultLineHeight: 21
@@ -91,6 +111,6 @@ QtObject {
item and spacing after it). So we have to subtract 2 x layout spacing before setting
our own, narrower, spacing.
*/
- return -layoutSpacing -layoutSpacing + value
+ return -layoutSpacing - layoutSpacing + value
}
}
diff --git a/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/NewProjectView.qml b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/NewProjectView.qml
index e011057892b..e8bb58cfa76 100644
--- a/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/NewProjectView.qml
+++ b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/NewProjectView.qml
@@ -28,111 +28,252 @@ import QtQuick.Controls
import QtQuick
import QtQuick.Layouts
+import StudioControls as SC
import StudioTheme as StudioTheme
-GridView {
- id: projectView
+import BackendApi
+
+ScrollView {
+ id: scrollView
required property Item loader
+ required property string currentTabName
- cellWidth: DialogValues.projectItemWidth
- cellHeight: DialogValues.projectItemHeight
- clip: true
+ property string backgroundHoverColor: DialogValues.presetItemBackgroundHover
- boundsBehavior: Flickable.StopAtBounds
+ // selectLast: if true, it will select last item in the model after a model reset.
+ property bool selectLast: false
- children: [
- Rectangle {
- color: DialogValues.darkPaneColor
- anchors.fill: parent
- z: -1
- }
- ]
-
- model: presetModel
-
- // called by onModelReset and when user clicks on an item, or when the header item is changed.
- onCurrentIndexChanged: {
- dialogBox.selectedPreset = projectView.currentIndex
- var source = dialogBox.currentPresetQmlPath()
- loader.source = source
+ ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
+ ScrollBar.vertical: SC.VerticalScrollBar {
+ parent: scrollView
+ x: scrollView.width + (DialogValues.gridMargins
+ - StudioTheme.Values.scrollBarThickness) * 0.5
+ y: scrollView.topPadding
+ height: scrollView.availableHeight
}
- Connections {
- target: presetModel
+ contentWidth: gridView.contentItem.childrenRect.width
+ contentHeight: gridView.contentItem.childrenRect.height
- // called when data is set (setWizardFactories)
- function onModelReset() {
- currentIndex = 0
- currentIndexChanged()
- }
- }
+ GridView {
+ id: gridView
- delegate: ItemDelegate {
- id: delegate
+ clip: true
+ anchors.fill: parent
+ cellWidth: DialogValues.gridCellWidth
+ cellHeight: DialogValues.gridCellHeight
+ rightMargin: -DialogValues.gridSpacing
+ bottomMargin: -DialogValues.gridSpacing
+ boundsBehavior: Flickable.StopAtBounds
+ model: BackendApi.presetModel
- width: DialogValues.projectItemWidth
- height: DialogValues.projectItemHeight
- background: null
-
- function fontIconCode(index) {
- var code = presetModel.fontIconCode(index)
- return code ? code : StudioTheme.Constants.wizardsUnknown
+ // called by onModelReset and when user clicks on an item, or when the header item is changed.
+ onCurrentIndexChanged: {
+ BackendApi.selectedPreset = gridView.currentIndex
+ var source = BackendApi.currentPresetQmlPath()
+ scrollView.loader.source = source
}
- Column {
- width: parent.width
- height: parent.height
+ Connections {
+ target: BackendApi.presetModel
- Label {
- id: projectTypeIcon
- text: fontIconCode(index)
- color: DialogValues.textColor
- width: parent.width
- height: DialogValues.projectItemHeight / 2
- horizontalAlignment: Text.AlignHCenter
- verticalAlignment: Text.AlignBottom
- renderType: Text.NativeRendering
- font.pixelSize: 65
- font.family: StudioTheme.Constants.iconFont.family
+ // called when data is set (setWizardFactories)
+ function onModelReset() {
+ if (scrollView.selectLast) {
+ gridView.currentIndex = BackendApi.presetModel.rowCount() - 1
+ scrollView.selectLast = false
+ } else {
+ gridView.currentIndex = 0
+ }
+
+ // This will load Details.qml and Styles.qml by setting "source" on the Loader.
+ gridView.currentIndexChanged()
}
+ }
+
+ delegate: ItemDelegate {
+ id: delegate
+
+ property bool hover: delegate.hovered || removeMouseArea.containsMouse
+
+ width: DialogValues.presetItemWidth
+ height: DialogValues.presetItemHeight
+
+ onClicked: delegate.GridView.view.currentIndex = index
+
+ background: Rectangle {
+ id: delegateBackground
+ width: parent.width
+ height: parent.height
+ color: delegate.hover ? scrollView.backgroundHoverColor : "transparent"
+ border.color: delegate.hover ? presetName.color : "transparent"
+ }
+
+ function fontIconCode(index) {
+ var code = BackendApi.presetModel.fontIconCode(index)
+ return code ? code : StudioTheme.Constants.wizardsUnknown
+ }
+
+ contentItem: Item {
+ anchors.fill: parent
+
+ ColumnLayout {
+ spacing: 0
+ anchors.top: parent.top
+ anchors.topMargin: -1
+ anchors.horizontalCenter: parent.horizontalCenter
+
+ Label {
+ id: presetIcon
+ text: delegate.fontIconCode(index)
+ color: DialogValues.textColor
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignBottom
+ renderType: Text.NativeRendering
+ font.pixelSize: 65
+ font.family: StudioTheme.Constants.iconFont.family
+ Layout.alignment: Qt.AlignHCenter
+ } // Preset type icon Label
+
+ Text {
+ id: presetName
+ color: DialogValues.textColor
+ text: name
+ font.pixelSize: DialogValues.defaultPixelSize
+ lineHeight: DialogValues.defaultLineHeight
+ lineHeightMode: Text.FixedHeight
+ width: DialogValues.presetItemWidth - 16
+ elide: Text.ElideRight
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignTop
+ Layout.alignment: Qt.AlignHCenter
+ Layout.preferredWidth: presetName.width
+ Layout.minimumWidth: presetName.width
+ Layout.maximumWidth: presetName.width
+
+ ToolTip {
+ id: toolTip
+ y: -toolTip.height
+ visible: delegate.hovered && presetName.truncated
+ text: name
+ delay: 1000
+ height: 20
+
+ background: Rectangle {
+ color: StudioTheme.Values.themeToolTipBackground
+ border.color: StudioTheme.Values.themeToolTipOutline
+ border.width: StudioTheme.Values.border
+ }
+
+ contentItem: Text {
+ color: StudioTheme.Values.themeToolTipText
+ text: toolTip.text
+ font.pixelSize: DialogValues.defaultPixelSize
+ verticalAlignment: Text.AlignVCenter
+ }
+ }
+ }
+
+ Text {
+ id: presetResolution
+ color: DialogValues.textColor
+ text: resolution
+ font.pixelSize: DialogValues.defaultPixelSize
+ lineHeight: DialogValues.defaultLineHeight
+ lineHeightMode: Text.FixedHeight
+ wrapMode: Text.Wrap
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignTop
+ Layout.alignment: Qt.AlignHCenter
+ }
+
+ } // ColumnLayout
+
+ Item {
+ id: removePresetButton
+ width: 20
+ height: 20
+ anchors.top: parent.top
+ anchors.right: parent.right
+ anchors.margins: 4
+ visible: isUserPreset === true
+ && delegate.hover
+ && scrollView.currentTabName !== BackendApi.recentsTabName()
+
+ Text {
+ anchors.fill: parent
+ text: StudioTheme.Constants.closeCross
+ color: DialogValues.textColor
+ horizontalAlignment: Qt.AlignHCenter
+ verticalAlignment: Qt.AlignVCenter
+ font.family: StudioTheme.Constants.iconFont.family
+ font.pixelSize: StudioTheme.Values.myIconFontSize
+ }
+
+ MouseArea {
+ id: removeMouseArea
+ anchors.fill: parent
+ hoverEnabled: true
+ cursorShape: removeMouseArea.containsMouse ? Qt.PointingHandCursor
+ : Qt.ArrowCursor
+
+ onClicked: {
+ removePresetDialog.presetName = presetName.text
+ removePresetDialog.open()
+ }
+ }
+ } // Delete preset button Item
+ } // Item
+
+ states: [
+ State {
+ name: "current"
+ when: delegate.GridView.isCurrentItem
+
+ PropertyChanges {
+ target: presetName
+ color: DialogValues.textColorInteraction
+ }
+ PropertyChanges {
+ target: presetResolution
+ color: DialogValues.textColorInteraction
+ }
+ PropertyChanges {
+ target: presetIcon
+ color: DialogValues.textColorInteraction
+ }
+ PropertyChanges {
+ target: scrollView
+ backgroundHoverColor: DialogValues.presetItemBackgroundHoverInteraction
+ }
+ } // State
+ ]
+ } // ItemDelegate
+
+ PopupDialog {
+ id: removePresetDialog
+
+ property string presetName
+
+ title: qsTr("Delete Custom Preset")
+ standardButtons: Dialog.Yes | Dialog.No
+ modal: true
+ closePolicy: Popup.CloseOnEscape
+ anchors.centerIn: parent
+ width: DialogValues.popupDialogWidth
+
+ onAccepted: BackendApi.removeCurrentPreset()
Text {
- id: projectTypeLabel
+ text: qsTr("Are you sure you want to delete \"" + removePresetDialog.presetName + "\" ?")
color: DialogValues.textColor
-
- text: name
font.pixelSize: DialogValues.defaultPixelSize
- lineHeight: DialogValues.defaultLineHeight
- lineHeightMode: Text.FixedHeight
- width: parent.width
- height: DialogValues.projectItemHeight / 2
- wrapMode: Text.Wrap
- horizontalAlignment: Text.AlignHCenter
- verticalAlignment: Text.AlignTop
+ wrapMode: Text.WordWrap
+
+ width: DialogValues.popupDialogWidth - 2 * DialogValues.popupDialogPadding
}
- } // Column
- MouseArea {
- anchors.fill: parent
- onClicked: {
- delegate.GridView.view.currentIndex = index
- }
- }
-
- states: [
- State {
- when: delegate.GridView.isCurrentItem
- PropertyChanges {
- target: projectTypeLabel
- color: DialogValues.textColorInteraction
- }
-
- PropertyChanges {
- target: projectTypeIcon
- color: DialogValues.textColorInteraction
- }
- } // State
- ]
- } // ItemDelegate
-} // GridView
+ } // Dialog
+ } // GridView
+} // ScrollView
diff --git a/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/PopupDialog.qml b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/PopupDialog.qml
new file mode 100644
index 00000000000..2c6b3084617
--- /dev/null
+++ b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/PopupDialog.qml
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+import StudioTheme as StudioTheme
+
+import BackendApi
+
+Dialog {
+ id: root
+ padding: DialogValues.popupDialogPadding
+
+ background: Rectangle {
+ color: DialogValues.darkPaneColor
+ border.color: StudioTheme.Values.themeInteraction
+ border.width: StudioTheme.Values.border
+ }
+
+ header: Label {
+ text: root.title
+ visible: root.title
+ elide: Label.ElideRight
+ font.bold: true
+ font.pixelSize: DialogValues.defaultPixelSize
+ padding: DialogValues.popupDialogPadding
+ color: DialogValues.textColor
+ horizontalAlignment: Text.AlignHCenter
+
+ background: Rectangle {
+ x: 1
+ y: 1
+ width: parent.width - 2
+ height: parent.height - 1
+ color: DialogValues.darkPaneColor
+ }
+ }
+
+ footer: PopupDialogButtonBox {
+ visible: count > 0
+ }
+}
diff --git a/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/PopupDialogButton.qml b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/PopupDialogButton.qml
new file mode 100644
index 00000000000..ee64eb90783
--- /dev/null
+++ b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/PopupDialogButton.qml
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.Controls
+import StudioTheme as StudioTheme
+
+Button {
+ id: root
+
+ implicitWidth: Math.max(
+ background ? background.implicitWidth : 0,
+ textItem.implicitWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(
+ background ? background.implicitHeight : 0,
+ textItem.implicitHeight + topPadding + bottomPadding)
+ leftPadding: 4
+ rightPadding: 4
+
+ background: Rectangle {
+ id: background
+ implicitWidth: 80
+ implicitHeight: StudioTheme.Values.height
+ color: StudioTheme.Values.themeControlBackground
+ border.color: StudioTheme.Values.themeControlOutline
+ anchors.fill: parent
+ }
+
+ contentItem: Text {
+ id: textItem
+ text: root.text
+ font.family: StudioTheme.Constants.font.family
+ font.pixelSize: DialogValues.defaultPixelSize
+ color: StudioTheme.Values.themeTextColor
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ renderType: Text.QtRendering
+ anchors.fill: parent
+ }
+
+ states: [
+ State {
+ name: "default"
+ when: !root.hovered && !root.checked && !root.pressed
+
+ PropertyChanges {
+ target: background
+ color: StudioTheme.Values.themeControlBackground
+ border.color: StudioTheme.Values.themeControlOutline
+ }
+ PropertyChanges {
+ target: textItem
+ color: StudioTheme.Values.themeTextColor
+ }
+ },
+ State {
+ name: "hover"
+ when: root.hovered && !root.checked && !root.pressed
+
+ PropertyChanges {
+ target: background
+ color: StudioTheme.Values.themeControlBackgroundHover
+ border.color: StudioTheme.Values.themeControlOutline
+ }
+ PropertyChanges {
+ target: textItem
+ color: StudioTheme.Values.themeTextColor
+ }
+ },
+ State {
+ name: "press"
+ when: root.hovered && root.pressed
+
+ PropertyChanges {
+ target: background
+ color: StudioTheme.Values.themeInteraction
+ border.color: StudioTheme.Values.themeInteraction
+ }
+ PropertyChanges {
+ target: textItem
+ color: StudioTheme.Values.themeIconColor
+ }
+ }
+ ]
+}
diff --git a/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/PopupDialogButtonBox.qml b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/PopupDialogButtonBox.qml
new file mode 100644
index 00000000000..d4d867f862d
--- /dev/null
+++ b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/PopupDialogButtonBox.qml
@@ -0,0 +1,46 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.Controls
+
+DialogButtonBox {
+ id: root
+ padding: DialogValues.popupDialogPadding
+ alignment: Qt.AlignRight | Qt.AlignBottom
+
+ background: Rectangle {
+ implicitHeight: 40
+ x: 1
+ y: 1
+ width: parent.width - 2
+ height: parent.height - 2
+ color: DialogValues.darkPaneColor
+ }
+
+ delegate: PopupDialogButton {
+ width: root.count === 1 ? root.availableWidth / 2 : undefined
+ }
+}
diff --git a/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/Styles.qml b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/Styles.qml
index e6955bb21b7..72be983e33a 100644
--- a/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/Styles.qml
+++ b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/Styles.qml
@@ -23,20 +23,21 @@
**
****************************************************************************/
+import QtQuick
import QtQuick.Window
import QtQuick.Controls
-
-import QtQuick
import QtQuick.Layouts
import StudioControls as SC
import StudioTheme as StudioTheme
+import BackendApi
+
Item {
width: DialogValues.stylesPaneWidth
Component.onCompleted: {
- dialogBox.stylesLoaded = true;
+ BackendApi.stylesLoaded = true
/*
* TODO: roleNames is called before the backend model (in the proxy class StyleModel) is
@@ -47,7 +48,7 @@ Item {
}
Component.onDestruction: {
- dialogBox.stylesLoaded = false;
+ BackendApi.stylesLoaded = false
}
Rectangle {
@@ -57,116 +58,120 @@ Item {
Item {
x: DialogValues.stylesPanePadding
- width: parent.width - DialogValues.stylesPanePadding * 2 + styleScrollBar.width
+ width: parent.width - DialogValues.stylesPanePadding * 2
height: parent.height
- ColumnLayout {
- anchors.fill: parent
- spacing: 5
+ Text {
+ id: styleTitleText
+ text: qsTr("Style")
+ height: DialogValues.paneTitleTextHeight
+ width: parent.width
+ font.weight: Font.DemiBold
+ font.pixelSize: DialogValues.paneTitlePixelSize
+ lineHeight: DialogValues.paneTitleLineHeight
+ lineHeightMode: Text.FixedHeight
+ color: DialogValues.textColor
+ verticalAlignment: Qt.AlignVCenter
+ anchors.top: parent.top
+ anchors.left: parent.left
+ anchors.right: parent.right
- Text {
- id: styleTitleText
- text: qsTr("Style")
- Layout.minimumHeight: DialogValues.paneTitleTextHeight
- font.weight: Font.DemiBold
- font.pixelSize: DialogValues.paneTitlePixelSize
- lineHeight: DialogValues.paneTitleLineHeight
- lineHeightMode: Text.FixedHeight
- color: DialogValues.textColor
- verticalAlignment: Qt.AlignVCenter
+ function refresh() {
+ styleTitleText.text = qsTr("Style") + " (" + BackendApi.styleModel.rowCount() + ")"
+ }
+ }
- function refresh() {
- text = qsTr("Style") + " (" + styleModel.rowCount() + ")"
- }
+ SC.ComboBox { // Style Filter ComboBox
+ id: styleComboBox
+ actionIndicatorVisible: false
+ currentIndex: 0
+ textRole: "text"
+ valueRole: "value"
+ font.pixelSize: DialogValues.defaultPixelSize
+ width: parent.width
+
+ anchors.top: styleTitleText.bottom
+ anchors.topMargin: 5
+
+ model: ListModel {
+ ListElement { text: qsTr("All"); value: "all" }
+ ListElement { text: qsTr("Light"); value: "light" }
+ ListElement { text: qsTr("Dark"); value: "dark" }
}
- SC.ComboBox { // Style Filter ComboBox
- actionIndicatorVisible: false
- currentIndex: 0
- textRole: "text"
- valueRole: "value"
- font.pixelSize: DialogValues.defaultPixelSize
+ onActivated: (index) => {
+ BackendApi.styleModel.filter(currentValue.toLowerCase())
+ styleTitleText.refresh()
+ }
+ } // Style Filter ComboBox
- model: ListModel {
- ListElement { text: qsTr("All"); value: "all" }
- ListElement { text: qsTr("Light"); value: "light" }
- ListElement { text: qsTr("Dark"); value: "dark" }
- }
+ ScrollView {
+ id: scrollView
- implicitWidth: parent.width
+ anchors.top: styleComboBox.bottom
+ anchors.topMargin: 11
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: DialogValues.stylesPanePadding
+ width: parent.width
- onActivated: (index) => {
- styleModel.filter(currentValue.toLowerCase());
- styleTitleText.refresh();
- }
- } // Style Filter ComboBox
-
- Item { implicitWidth: 1; implicitHeight: 9 }
+ ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
+ ScrollBar.vertical: SC.VerticalScrollBar {
+ id: styleScrollBar
+ x: stylesList.width + (DialogValues.stylesPanePadding
+ - StudioTheme.Values.scrollBarThickness) * 0.5
+ }
ListView {
id: stylesList
- Layout.fillWidth: true
- Layout.fillHeight: true
- clip: true
- model: styleModel
-
- MouseArea {
- id: listViewMouseArea
- anchors.fill: parent
- hoverEnabled: true
- propagateComposedEvents: true
- }
-
+ anchors.fill: parent
focus: true
+ clip: true
+ model: BackendApi.styleModel
boundsBehavior: Flickable.StopAtBounds
-
highlightFollowsCurrentItem: false
-
- ScrollBar.vertical: SC.VerticalScrollBar {
- id: styleScrollBar
- property int extraPadding: 0
- bottomInset: extraPadding
- bottomPadding: bottomInset + 16
- viewMouseArea: listViewMouseArea
- } // ScrollBar
+ bottomMargin: -DialogValues.styleListItemBottomMargin
onCurrentIndexChanged: {
- if (styleModel.rowCount() > 0)
- dialogBox.styleIndex = stylesList.currentIndex;
+ if (BackendApi.styleModel.rowCount() > 0)
+ BackendApi.styleIndex = stylesList.currentIndex
}
delegate: ItemDelegate {
id: delegateId
- height: styleImage.height + DialogValues.styleImageBorderWidth + styleText.height + extraPadding.height + 1
- width: stylesList.width - styleScrollBar.width
+ width: stylesList.width
+ height: DialogValues.styleListItemHeight
- Component.onCompleted: {
- styleScrollBar.extraPadding = styleText.height + extraPadding.height
- }
+ onClicked: stylesList.currentIndex = index
- Rectangle {
+ background: Rectangle {
anchors.fill: parent
color: DialogValues.lightPaneColor
+ }
+
+ contentItem: Item {
+ anchors.fill: parent
Column {
- spacing: 0
anchors.fill: parent
+ spacing: DialogValues.styleListItemSpacing
Rectangle {
- border.color: index == stylesList.currentIndex ? DialogValues.textColorInteraction : "transparent"
- border.width: index == stylesList.currentIndex ? DialogValues.styleImageBorderWidth : 0
+ width: DialogValues.styleImageWidth
+ + 2 * DialogValues.styleImageBorderWidth
+ height: DialogValues.styleImageHeight
+ + 2 * DialogValues.styleImageBorderWidth
+ border.color: index === stylesList.currentIndex ? DialogValues.textColorInteraction : "transparent"
+ border.width: index === stylesList.currentIndex ? DialogValues.styleImageBorderWidth : 0
color: "transparent"
- width: parent.width
- height: parent.height - styleText.height - extraPadding.height
Image {
id: styleImage
- asynchronous: false
- source: "image://newprojectdialog_library/" + styleModel.iconId(model.index)
- width: 200
- height: 262
x: DialogValues.styleImageBorderWidth
y: DialogValues.styleImageBorderWidth
+ width: DialogValues.styleImageWidth
+ height: DialogValues.styleImageHeight
+ asynchronous: false
+ source: "image://newprojectdialog_library/" + BackendApi.styleModel.iconId(model.index)
}
} // Rectangle
@@ -175,35 +180,26 @@ Item {
text: model.display
font.pixelSize: DialogValues.defaultPixelSize
lineHeight: DialogValues.defaultLineHeight
- height: 18
+ height: DialogValues.styleTextHeight
lineHeightMode: Text.FixedHeight
horizontalAlignment: Text.AlignHCenter
width: parent.width
color: DialogValues.textColor
}
-
- Item { id: extraPadding; width: 1; height: 10 }
} // Column
- } // Rectangle
-
- MouseArea {
- anchors.fill: parent
- onClicked: {
- stylesList.currentIndex = index
- }
}
}
Connections {
- target: styleModel
+ target: BackendApi.styleModel
function onModelReset() {
- stylesList.currentIndex = dialogBox.styleIndex;
- stylesList.currentIndexChanged();
- styleTitleText.refresh();
+ stylesList.currentIndex = BackendApi.styleIndex
+ stylesList.currentIndexChanged()
+ styleTitleText.refresh()
}
}
} // ListView
- } // ColumnLayout
+ } // ScrollView
} // Parent Item
} // Rectangle
}
diff --git a/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/qmldir b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/qmldir
index d7c1562164f..996b9ec3561 100644
--- a/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/qmldir
+++ b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/qmldir
@@ -1,4 +1,7 @@
-singleton DialogValues 1.0 DialogValues.qml
+PopupDialog 1.0 PopupDialog.qml
+PopupDialogButton 1.0 PopupDialogButton.qml
+PopupDialogButtonBox 1.0 PopupDialogButtonBox.qml
Details 1.0 Details.qml
-Styles 1.0 Styles.qml
+singleton DialogValues 1.0 DialogValues.qml
NewProjectView 1.0 NewProjectView.qml
+Styles 1.0 Styles.qml
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ExpressionTextField.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ExpressionTextField.qml
index 87e9a7e3c02..7828e9b4272 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ExpressionTextField.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ExpressionTextField.qml
@@ -140,7 +140,7 @@ StudioControls.TextField {
+ 2 : 0)
height: StudioTheme.Values.height - 2 * StudioTheme.Values.border
color: StudioTheme.Values.themeInteraction
- y: listView.currentItem.y
+ y: listView.currentItem?.y ?? 0
}
highlightFollowsCurrentItem: false
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml
index 3f8a3311894..143ec349d04 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml
@@ -54,6 +54,8 @@ Item {
property bool expandOnClick: true // if false, toggleExpand signal will be emitted instead
property bool addTopPadding: true
property bool addBottomPadding: true
+ property bool dropEnabled: false
+ property bool highlight: false
property bool useDefaulContextMenu: true
@@ -72,9 +74,22 @@ Item {
function onExpandAll() { section.expanded = true }
}
+ signal drop(var drag)
+ signal dropEnter(var drag)
+ signal dropExit()
signal showContextMenu()
signal toggleExpand()
+ DropArea {
+ id: dropArea
+
+ enabled: section.dropEnabled
+ anchors.fill: parent
+
+ onEntered: (drag)=> section.dropEnter(drag)
+ onDropped: (drag)=> section.drop(drag)
+ onExited: section.dropExit()
+ }
Rectangle {
id: header
@@ -82,7 +97,9 @@ Item {
visible: !section.hideHeader
anchors.left: parent.left
anchors.right: parent.right
- color: Qt.lighter(StudioTheme.Values.themeSectionHeadBackground, 1.0 + (0.2 * section.level))
+ color: section.highlight ? StudioTheme.Values.themeInteraction
+ : Qt.lighter(StudioTheme.Values.themeSectionHeadBackground, 1.0
+ + (0.2 * section.level))
Item {
StudioControls.Menu {
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/VerticalScrollBar.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/VerticalScrollBar.qml
index 4b344130959..b0e6f82bc17 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/VerticalScrollBar.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/VerticalScrollBar.qml
@@ -35,37 +35,26 @@ ScrollBar {
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
implicitContentHeight + topPadding + bottomPadding)
- property bool scrollBarVisible: parent.childrenRect.height > parent.height
- // viewMouseArea: if set, the scrollbar will be visible only on hover over the view containing
- // the mouse area item.
- property MouseArea viewMouseArea: null
-
- minimumSize: orientation == Qt.Horizontal ? height / width : width / height
+ property bool scrollBarVisible: parent.contentHeight > scrollBar.height
+ minimumSize: scrollBar.width / scrollBar.height
orientation: Qt.Vertical
- policy: computePolicy()
- x: parent.width - width
- y: 0
+ policy: scrollBar.scrollBarVisible ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff
+
height: parent.availableHeight
- (parent.bothVisible ? parent.horizontalThickness : 0)
- padding: 0
+ padding: scrollBar.active ? StudioTheme.Values.scrollBarActivePadding
+ : StudioTheme.Values.scrollBarInactivePadding
background: Rectangle {
+ implicitWidth: StudioTheme.Values.scrollBarThickness
+ implicitHeight: StudioTheme.Values.scrollBarThickness
color: StudioTheme.Values.themeScrollBarTrack
}
contentItem: Rectangle {
- implicitWidth: StudioTheme.Values.scrollBarThickness
+ implicitWidth: StudioTheme.Values.scrollBarThickness - 2 * scrollBar.padding
+ implicitHeight: StudioTheme.Values.scrollBarThickness - 2 * scrollBar.padding
color: StudioTheme.Values.themeScrollBarHandle
}
-
- function computePolicy() {
- if (!scrollBar.scrollBarVisible)
- return ScrollBar.AlwaysOff;
-
- if (scrollBar.viewMouseArea)
- return scrollBar.viewMouseArea.containsMouse ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff
- else
- return ScrollBar.AlwaysOn;
- }
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml
index 29d0edb14db..f63aa36ab14 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml
@@ -89,6 +89,8 @@ QtObject {
property real typeLabelVerticalShift: Math.round(6 * values.scaleFactor)
property real scrollBarThickness: 10
+ property real scrollBarActivePadding: 1
+ property real scrollBarInactivePadding: 2
property real toolTipHeight: 25
property int toolTipDelay: 1000
diff --git a/share/qtcreator/themes/design-light.creatortheme b/share/qtcreator/themes/design-light.creatortheme
index 0490ffa8ca1..b5e54f2a34b 100644
--- a/share/qtcreator/themes/design-light.creatortheme
+++ b/share/qtcreator/themes/design-light.creatortheme
@@ -374,7 +374,7 @@ QmlDesigner_ScrollBarHandleColor=ff8b8e8f
PaletteWindow=selectedBackground
PaletteWindowText=text
PaletteBase=normalBackground
-PaletteAlternateBase=alternateBackground
+PaletteAlternateBase=normalBackground
PaletteButton=selectedBackground
PaletteBrightText=selectedBackgroundText
PaletteText=text
diff --git a/share/qtcreator/themes/design.creatortheme b/share/qtcreator/themes/design.creatortheme
index 3dcfb3e1d0d..613d89de6b6 100644
--- a/share/qtcreator/themes/design.creatortheme
+++ b/share/qtcreator/themes/design.creatortheme
@@ -430,8 +430,8 @@ PaletteBase=normalBackground
-;can't see where this is used.
-PaletteAlternateBase=alternateBackground
+;can't see where this is used. ;Used in plugin dialog
+PaletteAlternateBase=normalBackground
;PaletteAlternateBase=ffd3299a
diff --git a/src/libs/languageserverprotocol/lsptypes.h b/src/libs/languageserverprotocol/lsptypes.h
index dbec3b96de5..5dd3f586049 100644
--- a/src/libs/languageserverprotocol/lsptypes.h
+++ b/src/libs/languageserverprotocol/lsptypes.h
@@ -142,6 +142,12 @@ public:
{ return JsonObject::contains(startKey) && JsonObject::contains(endKey); }
};
+inline bool operator==(const Range &r1, const Range &r2)
+{
+ return r1.contains(r2) && r2.contains(r1);
+}
+inline bool operator!=(const Range &r1, const Range &r2) { return !(r1 == r2); }
+
class LANGUAGESERVERPROTOCOL_EXPORT Location : public JsonObject
{
public:
diff --git a/src/libs/utils/elfreader.h b/src/libs/utils/elfreader.h
index 052703f7c0e..2f52938b6f1 100644
--- a/src/libs/utils/elfreader.h
+++ b/src/libs/utils/elfreader.h
@@ -146,7 +146,7 @@ public:
ElfType elftype;
ElfMachine elfmachine;
ElfClass elfclass;
- quint64 entryPoint;
+ quint64 entryPoint = 0;
QByteArray debugLink;
QByteArray buildId;
DebugSymbolsType symbolsType = UnknownSymbols;
diff --git a/src/plugins/android/androidmanager.cpp b/src/plugins/android/androidmanager.cpp
index e909752f78d..0ac99f89c9b 100644
--- a/src/plugins/android/androidmanager.cpp
+++ b/src/plugins/android/androidmanager.cpp
@@ -517,6 +517,8 @@ QString AndroidManager::androidNameForApiLevel(int x)
return QLatin1String("Android 12");
case 32:
return QLatin1String("Android 12L");
+ case 33:
+ return QLatin1String("Android 13");
default:
return tr("Unknown Android version. API Level: %1").arg(x);
}
diff --git a/src/plugins/android/androidmanifesteditorwidget.cpp b/src/plugins/android/androidmanifesteditorwidget.cpp
index 90da79c08f7..58868778158 100644
--- a/src/plugins/android/androidmanifesteditorwidget.cpp
+++ b/src/plugins/android/androidmanifesteditorwidget.cpp
@@ -1229,8 +1229,15 @@ static void addServiceArgumentsAndLibName(const AndroidServiceData &service, QXm
writeMetadataElement("android.app.lib_name", "android:value", "-- %%INSERT_APP_LIB_NAME%% --", writer);
}
-static void addServiceMetadata(QXmlStreamWriter &writer)
+void AndroidManifestEditorWidget::addServiceMetadata(QXmlStreamWriter &writer)
{
+ // The values below are no longer needed in Qt 6.2+, don't add them
+ const Target *target = androidTarget(m_textEditorWidget->textDocument()->filePath());
+ if (target) {
+ const QtSupport::QtVersion *qt = QtSupport::QtKitAspect::qtVersion(target->kit());
+ if (qt && qt->qtVersion() >= QtSupport::QtVersionNumber(6, 2))
+ return;
+ }
writeMetadataElement("android.app.qt_sources_resource_id", "android:resource", "@array/qt_sources", writer);
writeMetadataElement("android.app.repository", "android:value", "default", writer);
writeMetadataElement("android.app.qt_libs_resource_id", "android:resource", "@array/qt_libs", writer);
diff --git a/src/plugins/android/androidmanifesteditorwidget.h b/src/plugins/android/androidmanifesteditorwidget.h
index 1dd134b08c2..6a99a321a75 100644
--- a/src/plugins/android/androidmanifesteditorwidget.h
+++ b/src/plugins/android/androidmanifesteditorwidget.h
@@ -160,6 +160,8 @@ private:
QGroupBox *createApplicationGroupBox(QWidget *parent);
QGroupBox *createAdvancedGroupBox(QWidget *parent);
+ void addServiceMetadata(QXmlStreamWriter &writer);
+
bool m_dirty; // indicates that we need to call syncToEditor()
bool m_stayClean;
int m_errorLine;
diff --git a/src/plugins/android/avddialog.cpp b/src/plugins/android/avddialog.cpp
index 0f9f25e2295..b44af13720b 100644
--- a/src/plugins/android/avddialog.cpp
+++ b/src/plugins/android/avddialog.cpp
@@ -131,6 +131,10 @@ bool AvdDialog::isValid() const
ProjectExplorer::IDevice::Ptr AvdDialog::device() const
{
+ if (!m_createdAvdInfo.systemImage) {
+ qCWarning(avdDialogLog) << "System image of the created AVD is nullptr";
+ return IDevice::Ptr();
+ }
AndroidDevice *dev = new AndroidDevice();
const Utils::Id deviceId = AndroidDevice::idFromAvdInfo(m_createdAvdInfo);
using namespace ProjectExplorer;
@@ -140,10 +144,6 @@ ProjectExplorer::IDevice::Ptr AvdDialog::device() const
dev->setDeviceState(IDevice::DeviceConnected);
dev->setExtraData(Constants::AndroidAvdName, m_createdAvdInfo.name);
dev->setExtraData(Constants::AndroidCpuAbi, {m_createdAvdInfo.abi});
- if (!m_createdAvdInfo.systemImage) {
- qCWarning(avdDialogLog) << "System image of the created AVD is nullptr";
- return IDevice::Ptr();
- }
dev->setExtraData(Constants::AndroidSdk, m_createdAvdInfo.systemImage->apiLevel());
dev->setExtraData(Constants::AndroidAvdSdcard, QString("%1 MB")
.arg(m_createdAvdInfo.sdcardSize));
diff --git a/src/plugins/android/avdmanageroutputparser.cpp b/src/plugins/android/avdmanageroutputparser.cpp
index 803d4416e20..892405c33e4 100644
--- a/src/plugins/android/avdmanageroutputparser.cpp
+++ b/src/plugins/android/avdmanageroutputparser.cpp
@@ -181,6 +181,8 @@ int platformNameToApiLevel(const QString &platformName)
apiLevel = 30;
else if (apiLevelStr == 'S')
apiLevel = 31;
+ else if (apiLevelStr == "Tiramisu")
+ apiLevel = 33;
}
}
return apiLevel;
diff --git a/src/plugins/clangcodemodel/clangdclient.cpp b/src/plugins/clangcodemodel/clangdclient.cpp
index dc8e5d32663..7525fbf160d 100644
--- a/src/plugins/clangcodemodel/clangdclient.cpp
+++ b/src/plugins/clangcodemodel/clangdclient.cpp
@@ -1416,7 +1416,12 @@ ClangdClient::ClangdClient(Project *project, const Utils::FilePath &jsonDbDir)
return new ClangdTextMark(filePath, diag, isProjectFile, this);
};
const auto hideDiagsHandler = []{ ClangDiagnosticManager::clearTaskHubIssues(); };
- setDiagnosticsHandlers(textMarkCreator, hideDiagsHandler);
+ static const auto diagsFilter = [](const Diagnostic &diag) {
+ const Diagnostic::Code code = diag.code().value_or(Diagnostic::Code());
+ const QString * const codeString = Utils::get_if(&code);
+ return !codeString || *codeString != "drv_unknown_argument";
+ };
+ setDiagnosticsHandlers(textMarkCreator, hideDiagsHandler, diagsFilter);
setSymbolStringifier(displayNameFromDocumentSymbol);
setSemanticTokensHandler([this](TextDocument *doc, const QList &tokens,
int version, bool force) {
@@ -2815,7 +2820,8 @@ static void semanticHighlighter(QFutureInterface &future,
if (detail.startsWith("operator")) {
return !detail.contains('=')
&& !detail.contains("++") && !detail.contains("--")
- && !detail.contains("<<") && !detail.contains(">>");
+ && !detail.contains("<<") && !detail.contains(">>")
+ && !detail.contains("*");
}
firstChildTree << n.children().value_or(QList());
}
@@ -2826,6 +2832,19 @@ static void semanticHighlighter(QFutureInterface &future,
return false;
if (it->hasConstType())
return false;
+
+ if (it->kind() == "CXXMemberCall") {
+ if (it == path.rbegin())
+ return false;
+ const QList children = it->children().value_or(QList());
+ QTC_ASSERT(!children.isEmpty(), return false);
+
+ // The called object is never displayed as an output parameter.
+ // TODO: A good argument can be made to display objects on which a non-const
+ // operator or function is called as output parameters.
+ return (it - 1)->range() != children.first().range();
+ }
+
if (it->kind() == "Member" && it->arcanaContains("(")
&& !it->arcanaContains("bound member function type")) {
return false;
@@ -2911,8 +2930,11 @@ static void semanticHighlighter(QFutureInterface &future,
}
if (token.modifiers.contains(QLatin1String("declaration")))
styles.mixinStyles.push_back(C_DECLARATION);
- if (token.modifiers.contains(QLatin1String("static")))
- styles.mixinStyles.push_back(C_STATIC_MEMBER);
+ if (token.modifiers.contains(QLatin1String("static"))) {
+ if (styles.mainStyle != C_FIELD && styles.mainStyle != C_TEXT)
+ styles.mixinStyles.push_back(styles.mainStyle);
+ styles.mainStyle = C_STATIC_MEMBER;
+ }
if (isOutputParameter(token))
styles.mixinStyles.push_back(C_OUTPUT_ARGUMENT);
qCDebug(clangdLogHighlight) << "adding highlighting result"
diff --git a/src/plugins/clangcodemodel/test/clangdtests.cpp b/src/plugins/clangcodemodel/test/clangdtests.cpp
index adaa705cb85..b672dec291d 100644
--- a/src/plugins/clangcodemodel/test/clangdtests.cpp
+++ b/src/plugins/clangcodemodel/test/clangdtests.cpp
@@ -998,6 +998,10 @@ void ClangdTestHighlighting::test_data()
<< QList{C_PUNCTUATION} << int(CppEditor::SemanticHighlighter::AngleBracketOpen);
QTest::newRow("class template instantiation (closing angle bracket)") << 384 << 22 << 384 << 23
<< QList{C_PUNCTUATION} << int(CppEditor::SemanticHighlighter::AngleBracketClose);
+ QTest::newRow("static member function decl") << 395 << 17 << 395 << 30
+ << QList{C_STATIC_MEMBER, C_DECLARATION, C_FUNCTION} << 0;
+ QTest::newRow("static member function call") << 400 << 17 << 400 << 30
+ << QList{C_STATIC_MEMBER, C_FUNCTION} << 0;
QTest::newRow("namespace in declaration") << 413 << 4 << 413 << 26
<< QList{C_NAMESPACE} << 0;
QTest::newRow("namespaced class in declaration") << 413 << 28 << 413 << 41
@@ -1122,9 +1126,9 @@ void ClangdTestHighlighting::test_data()
QTest::newRow("local variable captured by lambda") << 442 << 24 << 442 << 27
<< QList{C_LOCAL} << 0;
QTest::newRow("static protected member") << 693 << 16 << 693 << 30
- << QList{C_FIELD, C_DECLARATION, C_STATIC_MEMBER} << 0;
+ << QList{C_STATIC_MEMBER, C_DECLARATION} << 0;
QTest::newRow("static private member") << 696 << 16 << 696 << 28
- << QList{C_FIELD, C_DECLARATION, C_STATIC_MEMBER} << 0;
+ << QList{C_STATIC_MEMBER, C_DECLARATION} << 0;
QTest::newRow("alias template declaration (opening angle bracket)") << 700 << 10 << 700 << 11
<< QList{C_PUNCTUATION} << int(CppEditor::SemanticHighlighter::AngleBracketOpen);
QTest::newRow("alias template declaration (closing angle bracket)") << 700 << 16 << 700 << 17
@@ -1308,6 +1312,9 @@ void ClangdTestHighlighting::test_data()
<< QList{C_PREPROCESSOR} << 0;
QTest::newRow("built-in define 3") << 952 << 21 << 952 << 40
<< QList{C_PREPROCESSOR} << 0;
+ QTest::newRow("deref operator (object)") << 960 << 10 << 960 << 11 << QList{C_LOCAL} << 0;
+ QTest::newRow("deref operator (member)") << 960 << 12 << 960 << 13 << QList{C_FIELD} << 0;
+ QTest::newRow("nested call") << 979 << 20 << 979 << 21 << QList{C_LOCAL} << 0;
}
void ClangdTestHighlighting::test()
@@ -1395,6 +1402,8 @@ void ClangdTestHighlighting::test()
QEXPECT_FAIL("non-final virtual function call via pointer",
"clangd < 14 does not send virtual modifier", Continue);
}
+ QEXPECT_FAIL("non-const reference via member function call as output argument (object)",
+ "See below", Continue);
QEXPECT_FAIL("non-const reference via member function call as output argument (function)",
"Without punctuation and comment tokens from clangd, it's not possible "
"to highlight entire expressions. But do we really want this? What about nested "
diff --git a/src/plugins/clangcodemodel/test/data/completion/completion.pro b/src/plugins/clangcodemodel/test/data/completion/completion.pro
index da641f22378..832b9aac07f 100644
--- a/src/plugins/clangcodemodel/test/data/completion/completion.pro
+++ b/src/plugins/clangcodemodel/test/data/completion/completion.pro
@@ -27,5 +27,7 @@ SOURCES = \
privateFuncDefCompletion.cpp \
signalCompletion.cpp
+QMAKE_CXXFLAGS += -ffoo
+
HEADERS = mainwindow.h
FORMS = mainwindow.ui
diff --git a/src/plugins/clangcodemodel/test/data/find-usages/find-usages.pro b/src/plugins/clangcodemodel/test/data/find-usages/find-usages.pro
index c4474855308..cf511fee40a 100644
--- a/src/plugins/clangcodemodel/test/data/find-usages/find-usages.pro
+++ b/src/plugins/clangcodemodel/test/data/find-usages/find-usages.pro
@@ -2,3 +2,4 @@ TEMPLATE = app
QT = core
HEADERS = defs.h
SOURCES = main.cpp
+QMAKE_CXXFLAGS += -ffoo
diff --git a/src/plugins/clangcodemodel/test/data/follow-symbol/follow-symbol.pro b/src/plugins/clangcodemodel/test/data/follow-symbol/follow-symbol.pro
index 7d2c3147a3e..8b0d6d1879b 100644
--- a/src/plugins/clangcodemodel/test/data/follow-symbol/follow-symbol.pro
+++ b/src/plugins/clangcodemodel/test/data/follow-symbol/follow-symbol.pro
@@ -2,3 +2,4 @@ TEMPLATE = app
CONFIG -= qt
HEADERS = cursor.h header.h
SOURCES = cursor.cpp main.cpp
+QMAKE_CXXFLAGS += -ffoo
diff --git a/src/plugins/clangcodemodel/test/data/highlighting/highlighting.cpp b/src/plugins/clangcodemodel/test/data/highlighting/highlighting.cpp
index 2ef28c11ba5..7275ddd3307 100644
--- a/src/plugins/clangcodemodel/test/data/highlighting/highlighting.cpp
+++ b/src/plugins/clangcodemodel/test/data/highlighting/highlighting.cpp
@@ -951,3 +951,30 @@ void builtinDefines()
const auto f2 = __FUNCTION__;
const auto f3 = __PRETTY_FUNCTION__;
}
+
+void derefOperator()
+{
+ struct S { bool operator*(); };
+ struct S2 { S s; };
+ S2 s;
+ if (*s.s)
+ return;
+}
+
+struct my_struct
+{
+ void* method(int dummy);
+};
+
+my_struct* get_my_struct();
+
+struct my_struct2
+{
+ my_struct2(void* p);
+};
+
+void nestedCall()
+{
+ my_struct* s = get_my_struct();
+ new my_struct2(s->method(0));
+}
diff --git a/src/plugins/clangcodemodel/test/data/highlighting/highlighting.pro b/src/plugins/clangcodemodel/test/data/highlighting/highlighting.pro
index 7dd57583ea9..32196b9724f 100644
--- a/src/plugins/clangcodemodel/test/data/highlighting/highlighting.pro
+++ b/src/plugins/clangcodemodel/test/data/highlighting/highlighting.pro
@@ -1,3 +1,4 @@
TEMPLATE = app
CONFIG -= qt
SOURCES = highlighting.cpp
+QMAKE_CXXFLAGS += -ffoo
diff --git a/src/plugins/clangcodemodel/test/data/local-references/local-references.pro b/src/plugins/clangcodemodel/test/data/local-references/local-references.pro
index c3b335aad8d..f47c1c68154 100644
--- a/src/plugins/clangcodemodel/test/data/local-references/local-references.pro
+++ b/src/plugins/clangcodemodel/test/data/local-references/local-references.pro
@@ -1,5 +1,4 @@
TEMPLATE = app
-
CONFIG -= qt
-
SOURCES = references.cpp
+QMAKE_CXXFLAGS += -ffoo
diff --git a/src/plugins/clangformat/clangformatutils.cpp b/src/plugins/clangformat/clangformatutils.cpp
index e59a151f0b2..83aa58c1674 100644
--- a/src/plugins/clangformat/clangformatutils.cpp
+++ b/src/plugins/clangformat/clangformatutils.cpp
@@ -376,18 +376,27 @@ static std::string readFile(const QString &path)
if (!file.open(QFile::ReadOnly))
return defaultStyle;
- const QByteArray content = file.readAll();
+ const std::string content = file.readAll().toStdString();
file.close();
clang::format::FormatStyle style;
style.Language = clang::format::FormatStyle::LK_Cpp;
- const std::error_code error = clang::format::parseConfiguration(content.toStdString(), &style);
-
+ const std::error_code error = clang::format::parseConfiguration(content, &style);
QTC_ASSERT(error.value() == static_cast(ParseError::Success), return defaultStyle);
addQtcStatementMacros(style);
+ std::string settings = clang::format::configurationAsText(style);
- return clang::format::configurationAsText(style);
+ // Needed workaround because parseConfiguration remove BasedOnStyle field
+ // ToDo: standardize this behavior for future
+ const size_t index = content.find("BasedOnStyle");
+ if (index != std::string::npos) {
+ const size_t size = content.find("\n", index) - index;
+ const size_t insert_index = settings.find("\n");
+ settings.insert(insert_index, "\n" + content.substr(index, size));
+ }
+
+ return settings;
}
std::string currentProjectConfigText()
diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp
index 2e056176046..817098b8f65 100644
--- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp
@@ -1739,14 +1739,14 @@ const QStringList InitialCMakeArgumentsAspect::allValues() const
void InitialCMakeArgumentsAspect::setAllValues(const QString &values, QStringList &additionalOptions)
{
QStringList arguments = values.split('\n', Qt::SkipEmptyParts);
+ QString cmakeGenerator;
for (QString &arg: arguments) {
if (arg.startsWith("-G")) {
const QString strDash(" - ");
const int idxDash = arg.indexOf(strDash);
if (idxDash > 0) {
// -GCodeBlocks - Ninja
- QString generator = "-DCMAKE_GENERATOR:STRING=" + arg.mid(idxDash + strDash.length());
- arguments.append(generator);
+ cmakeGenerator = "-DCMAKE_GENERATOR:STRING=" + arg.mid(idxDash + strDash.length());
arg = arg.left(idxDash);
arg.replace("-G", "-DCMAKE_EXTRA_GENERATOR:STRING=");
@@ -1761,6 +1761,9 @@ void InitialCMakeArgumentsAspect::setAllValues(const QString &values, QStringLis
if (arg.startsWith("-T"))
arg.replace("-T", "-DCMAKE_GENERATOR_TOOLSET:STRING=");
}
+ if (!cmakeGenerator.isEmpty())
+ arguments.append(cmakeGenerator);
+
m_cmakeConfiguration = CMakeConfig::fromArguments(arguments, additionalOptions);
for (CMakeConfigItem &ci : m_cmakeConfiguration)
ci.isInitial = true;
diff --git a/src/plugins/cmakeprojectmanager/cmaketool.cpp b/src/plugins/cmakeprojectmanager/cmaketool.cpp
index 2e66e0c48be..6e3f73358cb 100644
--- a/src/plugins/cmakeprojectmanager/cmaketool.cpp
+++ b/src/plugins/cmakeprojectmanager/cmaketool.cpp
@@ -234,7 +234,7 @@ FilePath CMakeTool::qchFilePath() const
FilePath CMakeTool::cmakeExecutable(const FilePath &path)
{
- if (HostOsInfo::isMacHost()) {
+ if (path.osType() == OsTypeMac) {
const QString executableString = path.toString();
const int appIndex = executableString.lastIndexOf(".app");
const int appCutIndex = appIndex + 4;
@@ -251,7 +251,7 @@ FilePath CMakeTool::cmakeExecutable(const FilePath &path)
const FilePath resolvedPath = path.canonicalPath();
// Evil hack to make snap-packages of CMake work. See QTCREATORBUG-23376
- if (HostOsInfo::isLinuxHost() && resolvedPath.fileName() == "snap")
+ if (path.osType() == OsTypeLinux && resolvedPath.fileName() == "snap")
return path;
return resolvedPath;
diff --git a/src/plugins/cmakeprojectmanager/cmaketoolsettingsaccessor.cpp b/src/plugins/cmakeprojectmanager/cmaketoolsettingsaccessor.cpp
index acd9422fc74..0629273d968 100644
--- a/src/plugins/cmakeprojectmanager/cmaketoolsettingsaccessor.cpp
+++ b/src/plugins/cmakeprojectmanager/cmaketoolsettingsaccessor.cpp
@@ -239,9 +239,10 @@ CMakeToolSettingsAccessor::cmakeTools(const QVariantMap &data, bool fromSdk) con
const QVariantMap dbMap = data.value(key).toMap();
auto item = std::make_unique(dbMap, fromSdk);
- if (item->isAutoDetected() && !item->cmakeExecutable().isExecutableFile()) {
- qWarning() << QString::fromLatin1("CMakeTool \"%1\" (%2) dropped since the command is not executable.")
- .arg(item->cmakeExecutable().toUserOutput(), item->id().toString());
+ const FilePath cmakeExecutable = item->cmakeExecutable();
+ if (item->isAutoDetected() && !cmakeExecutable.needsDevice() && !cmakeExecutable.isExecutableFile()) {
+ qWarning() << QString("CMakeTool \"%1\" (%2) dropped since the command is not executable.")
+ .arg(cmakeExecutable.toUserOutput(), item->id().toString());
continue;
}
diff --git a/src/plugins/coreplugin/foldernavigationwidget.cpp b/src/plugins/coreplugin/foldernavigationwidget.cpp
index 52cae412877..1c7d82a1452 100644
--- a/src/plugins/coreplugin/foldernavigationwidget.cpp
+++ b/src/plugins/coreplugin/foldernavigationwidget.cpp
@@ -85,6 +85,7 @@ const char kHiddenFilesKey[] = ".HiddenFilesFilter";
const char kSyncKey[] = ".SyncWithEditor";
const char kShowBreadCrumbs[] = ".ShowBreadCrumbs";
const char kSyncRootWithEditor[] = ".SyncRootWithEditor";
+const char kShowFoldersOnTop[] = ".ShowFoldersOnTop";
const char ADDNEWFILE[] = "QtCreator.FileSystem.AddNewFile";
const char RENAMEFILE[] = "QtCreator.FileSystem.RenameFile";
@@ -834,6 +835,7 @@ const bool kHiddenFilesDefault = false;
const bool kAutoSyncDefault = true;
const bool kShowBreadCrumbsDefault = true;
const bool kRootAutoSyncDefault = true;
+const bool kShowFoldersOnTopDefault = true;
void FolderNavigationWidgetFactory::saveSettings(Utils::QtcSettings *settings,
int position,
@@ -852,6 +854,9 @@ void FolderNavigationWidgetFactory::saveSettings(Utils::QtcSettings *settings,
settings->setValueWithDefault(base + kSyncRootWithEditor,
fnw->rootAutoSynchronization(),
kRootAutoSyncDefault);
+ settings->setValueWithDefault(base + kShowFoldersOnTop,
+ fnw->isShowingFoldersOnTop(),
+ kShowFoldersOnTopDefault);
}
void FolderNavigationWidgetFactory::restoreSettings(QSettings *settings, int position, QWidget *widget)
@@ -865,6 +870,8 @@ void FolderNavigationWidgetFactory::restoreSettings(QSettings *settings, int pos
settings->value(base + kShowBreadCrumbs, kShowBreadCrumbsDefault).toBool());
fnw->setRootAutoSynchronization(
settings->value(base + kSyncRootWithEditor, kRootAutoSyncDefault).toBool());
+ fnw->setShowFoldersOnTop(
+ settings->value(base + kShowFoldersOnTop, kShowFoldersOnTopDefault).toBool());
}
void FolderNavigationWidgetFactory::insertRootDirectory(const RootDirectory &directory)
diff --git a/src/plugins/debugger/debuggersourcepathmappingwidget.cpp b/src/plugins/debugger/debuggersourcepathmappingwidget.cpp
index 54c8318a075..c34b01d7c4f 100644
--- a/src/plugins/debugger/debuggersourcepathmappingwidget.cpp
+++ b/src/plugins/debugger/debuggersourcepathmappingwidget.cpp
@@ -100,7 +100,8 @@ QStringList qtBuildPaths()
return {"Q:/qt5_workdir/w/s",
"C:/work/build/qt5_workdir/w/s",
"c:/users/qt/work/qt",
- "c:/Users/qt/work/install"};
+ "c:/Users/qt/work/install",
+ "/Users/qt/work/qt"};
} else if (HostOsInfo::isMacHost()) {
return { "/Users/qt/work/qt" };
} else {
diff --git a/src/plugins/help/helpplugin.cpp b/src/plugins/help/helpplugin.cpp
index b25fe0aec20..99b1dca1654 100644
--- a/src/plugins/help/helpplugin.cpp
+++ b/src/plugins/help/helpplugin.cpp
@@ -607,12 +607,14 @@ void HelpPluginPrivate::showContextHelp(const HelpItem &contextHelp)
void HelpPluginPrivate::activateIndex()
{
activateHelpMode();
+ showHelpUrl(LocalHelpManager::homePage(), Core::HelpManager::HelpModeAlways);
m_centralWidget->activateSideBarItem(Constants::HELP_INDEX);
}
void HelpPluginPrivate::activateContents()
{
activateHelpMode();
+ showHelpUrl(LocalHelpManager::homePage(), Core::HelpManager::HelpModeAlways);
m_centralWidget->activateSideBarItem(Constants::HELP_CONTENTS);
}
diff --git a/src/plugins/imageviewer/imageviewerfile.cpp b/src/plugins/imageviewer/imageviewerfile.cpp
index 0fb4e22a101..0133cce05c6 100644
--- a/src/plugins/imageviewer/imageviewerfile.cpp
+++ b/src/plugins/imageviewer/imageviewerfile.cpp
@@ -111,7 +111,7 @@ Core::IDocument::OpenResult ImageViewerFile::openImpl(QString *errorString,
if (format.startsWith("svg")) {
m_tempSvgItem = new QGraphicsSvgItem(fileName);
QRectF bound = m_tempSvgItem->boundingRect();
- if (qFuzzyIsNull(bound.width()) && qFuzzyIsNull(bound.height())) {
+ if (!bound.isValid() || (qFuzzyIsNull(bound.width()) && qFuzzyIsNull(bound.height()))) {
delete m_tempSvgItem;
m_tempSvgItem = nullptr;
if (errorString)
@@ -123,8 +123,17 @@ Core::IDocument::OpenResult ImageViewerFile::openImpl(QString *errorString,
} else
#endif
if (QMovie::supportedFormats().contains(format)) {
- m_type = TypeMovie;
m_movie = new QMovie(fileName, QByteArray(), this);
+ // force reading movie/image data, so we can catch completely invalid movies/images early:
+ m_movie->jumpToNextFrame();
+ if (!m_movie->isValid()) {
+ if (errorString)
+ *errorString = tr("Failed to read image.");
+ delete m_movie;
+ m_movie = nullptr;
+ return OpenResult::CannotHandle;
+ }
+ m_type = TypeMovie;
m_movie->setCacheMode(QMovie::CacheAll);
connect(
m_movie,
diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp
index a8ec473fc35..d88c0ae86d2 100644
--- a/src/plugins/languageclient/client.cpp
+++ b/src/plugins/languageclient/client.cpp
@@ -1085,9 +1085,10 @@ bool Client::hasDiagnostic(const LanguageServerProtocol::DocumentUri &uri,
}
void Client::setDiagnosticsHandlers(const TextMarkCreator &textMarkCreator,
- const HideDiagnosticsHandler &hideHandler)
+ const HideDiagnosticsHandler &hideHandler,
+ const DiagnosticsFilter &filter)
{
- m_diagnosticManager.setDiagnosticsHandlers(textMarkCreator, hideHandler);
+ m_diagnosticManager.setDiagnosticsHandlers(textMarkCreator, hideHandler, filter);
}
void Client::setSemanticTokensHandler(const SemanticTokensHandler &handler)
diff --git a/src/plugins/languageclient/client.h b/src/plugins/languageclient/client.h
index 4a7356e7c50..3327437d41f 100644
--- a/src/plugins/languageclient/client.h
+++ b/src/plugins/languageclient/client.h
@@ -189,7 +189,7 @@ public:
bool hasDiagnostic(const LanguageServerProtocol::DocumentUri &uri,
const LanguageServerProtocol::Diagnostic &diag) const;
void setDiagnosticsHandlers(const TextMarkCreator &textMarkCreator,
- const HideDiagnosticsHandler &hideHandler);
+ const HideDiagnosticsHandler &hideHandler, const DiagnosticsFilter &filter);
void setSemanticTokensHandler(const SemanticTokensHandler &handler);
void setSymbolStringifier(const LanguageServerProtocol::SymbolStringifier &stringifier);
LanguageServerProtocol::SymbolStringifier symbolStringifier() const;
diff --git a/src/plugins/languageclient/diagnosticmanager.cpp b/src/plugins/languageclient/diagnosticmanager.cpp
index 2b1d66c477d..a1b729ce9bf 100644
--- a/src/plugins/languageclient/diagnosticmanager.cpp
+++ b/src/plugins/languageclient/diagnosticmanager.cpp
@@ -90,7 +90,9 @@ void DiagnosticManager::setDiagnostics(const LanguageServerProtocol::DocumentUri
const Utils::optional &version)
{
hideDiagnostics(uri.toFilePath());
- m_diagnostics[uri] = {version, diagnostics};
+ const QList filteredDiags = m_filter
+ ? Utils::filtered(diagnostics, m_filter) : diagnostics;
+ m_diagnostics[uri] = {version, filteredDiags};
}
void DiagnosticManager::hideDiagnostics(const Utils::FilePath &filePath)
@@ -201,10 +203,12 @@ bool DiagnosticManager::hasDiagnostic(const LanguageServerProtocol::DocumentUri
}
void DiagnosticManager::setDiagnosticsHandlers(const TextMarkCreator &textMarkCreator,
- const HideDiagnosticsHandler &removalHandler)
+ const HideDiagnosticsHandler &removalHandler,
+ const DiagnosticsFilter &filter)
{
m_textMarkCreator = textMarkCreator;
m_hideHandler = removalHandler;
+ m_filter = filter;
}
} // namespace LanguageClient
diff --git a/src/plugins/languageclient/diagnosticmanager.h b/src/plugins/languageclient/diagnosticmanager.h
index c4bfabf71a3..f201aaf50c2 100644
--- a/src/plugins/languageclient/diagnosticmanager.h
+++ b/src/plugins/languageclient/diagnosticmanager.h
@@ -45,6 +45,7 @@ class Client;
using TextMarkCreator = std::function;
using HideDiagnosticsHandler = std::function;
+using DiagnosticsFilter = std::function;
class DiagnosticManager
{
@@ -70,7 +71,8 @@ public:
const LanguageServerProtocol::Diagnostic &diag) const;
void setDiagnosticsHandlers(const TextMarkCreator &shownHandler,
- const HideDiagnosticsHandler &removalHandler);
+ const HideDiagnosticsHandler &removalHandler,
+ const DiagnosticsFilter &filter);
private:
TextEditor::TextMark *createTextMark(const Utils::FilePath &filePath,
@@ -83,6 +85,7 @@ private:
QMap> m_marks;
TextMarkCreator m_textMarkCreator;
HideDiagnosticsHandler m_hideHandler;
+ DiagnosticsFilter m_filter;
Client *m_client;
};
diff --git a/src/plugins/marketplace/qtmarketplacewelcomepage.cpp b/src/plugins/marketplace/qtmarketplacewelcomepage.cpp
index 1abb06dacc9..bd1c112a56a 100644
--- a/src/plugins/marketplace/qtmarketplacewelcomepage.cpp
+++ b/src/plugins/marketplace/qtmarketplacewelcomepage.cpp
@@ -132,8 +132,9 @@ public:
void onTagClicked(const QString &tag)
{
- QString text = m_searcher->text();
- m_searcher->setText(text + QString("tag:\"%1\" ").arg(tag));
+ const QString text = m_searcher->text();
+ m_searcher->setText((text.startsWith("tag:\"") ? text.trimmed() + " " : QString())
+ + QString("tag:\"%1\" ").arg(tag));
}
private:
diff --git a/src/plugins/mcusupport/mcupackage.cpp b/src/plugins/mcusupport/mcupackage.cpp
index a41138e1cd0..171f326fa9b 100644
--- a/src/plugins/mcusupport/mcupackage.cpp
+++ b/src/plugins/mcusupport/mcupackage.cpp
@@ -103,7 +103,7 @@ FilePath McuPackage::basePath() const
FilePath McuPackage::path() const
{
- return basePath().resolvePath(m_relativePathModifier).absoluteFilePath();
+ return basePath().pathAppended(m_relativePathModifier.path()).absoluteFilePath();
}
FilePath McuPackage::defaultPath() const
@@ -126,7 +126,7 @@ void McuPackage::updatePath()
void McuPackage::updateStatus()
{
bool validPath = !m_path.isEmpty() && m_path.exists();
- const FilePath detectionPath = basePath().resolvePath(m_detectionPath);
+ const FilePath detectionPath = basePath().pathAppended(m_detectionPath.path());
const bool validPackage = m_detectionPath.isEmpty() || detectionPath.exists();
m_detectedVersion = validPath && validPackage && m_versionDetector
? m_versionDetector->parseVersion(basePath().toString())
diff --git a/src/plugins/mcusupport/mcusupportversiondetection.cpp b/src/plugins/mcusupport/mcusupportversiondetection.cpp
index b5d2dc18a20..b0db7638015 100644
--- a/src/plugins/mcusupport/mcusupportversiondetection.cpp
+++ b/src/plugins/mcusupport/mcusupportversiondetection.cpp
@@ -58,7 +58,7 @@ QString McuPackageExecutableVersionDetector::parseVersion(const QString &package
if (m_detectionPath.isEmpty() || m_detectionRegExp.isEmpty())
return QString();
- const Utils::FilePath binaryPath = Utils::FilePath::fromString(packagePath).resolvePath(m_detectionPath);
+ const Utils::FilePath binaryPath = Utils::FilePath::fromString(packagePath).pathAppended(m_detectionPath.path());
if (!binaryPath.exists())
return QString();
diff --git a/src/plugins/projectexplorer/buildconfiguration.cpp b/src/plugins/projectexplorer/buildconfiguration.cpp
index c0907d6d737..481c16be811 100644
--- a/src/plugins/projectexplorer/buildconfiguration.cpp
+++ b/src/plugins/projectexplorer/buildconfiguration.cpp
@@ -58,6 +58,7 @@
#include
#include
#include
+#include
#include
using namespace Utils;
@@ -69,6 +70,8 @@ const char USER_ENVIRONMENT_CHANGES_KEY[] = "ProjectExplorer.BuildConfiguration.
const char CUSTOM_PARSERS_KEY[] = "ProjectExplorer.BuildConfiguration.CustomParsers";
const char PARSE_STD_OUT_KEY[] = "ProjectExplorer.BuildConfiguration.ParseStandardOutput";
+Q_LOGGING_CATEGORY(bcLog, "qtc.buildconfig", QtWarningMsg)
+
namespace ProjectExplorer {
namespace Internal {
@@ -608,6 +611,8 @@ FilePath BuildConfiguration::buildDirectoryFromTemplate(const FilePath &projectD
{
MacroExpander exp;
+ qCDebug(bcLog) << Q_FUNC_INFO << projectDir << mainFilePath << projectName << bcName;
+
// TODO: Remove "Current" variants in ~4.16
exp.registerFileVariables(Constants::VAR_CURRENTPROJECT_PREFIX,
QCoreApplication::translate("ProjectExplorer", "Main file of current project"),
@@ -638,7 +643,9 @@ FilePath BuildConfiguration::buildDirectoryFromTemplate(const FilePath &projectD
exp.registerSubProvider([kit] { return kit->macroExpander(); });
QString buildDir = ProjectExplorerPlugin::buildDirectoryTemplate();
+ qCDebug(bcLog) << "build dir template:" << buildDir;
buildDir = exp.expand(buildDir);
+ qCDebug(bcLog) << "expanded build:" << buildDir;
if (spaceHandling == ReplaceSpaces)
buildDir.replace(" ", "-");
diff --git a/src/plugins/projectexplorer/customparser.cpp b/src/plugins/projectexplorer/customparser.cpp
index 3efa046b994..0229a5287ce 100644
--- a/src/plugins/projectexplorer/customparser.cpp
+++ b/src/plugins/projectexplorer/customparser.cpp
@@ -271,7 +271,7 @@ OutputLineParser::Result CustomParser::parseLine(
CustomParserExpression::CustomParserChannel channel
)
{
- const QString line = rawLine.trimmed();
+ const QString line = rightTrimmed(rawLine);
const Result res = hasMatch(line, channel, m_error, Task::Error);
if (res.status != Status::NotHandled)
return res;
@@ -629,6 +629,55 @@ void ProjectExplorerPlugin::testCustomOutputParsers_data()
<< QString() << QString()
<< Tasks({CompileTask(Task::Error, unitTestMessage, unitTestFileName, unitTestLineNumber)})
<< QString();
+
+ const QString leadingSpacesPattern = "^ MESSAGE:(.+)";
+ const QString leadingSpacesMessage = " MESSAGE:Error";
+ const QString noLeadingSpacesMessage = "MESSAGE:Error";
+ QTest::newRow("leading spaces: match")
+ << leadingSpacesMessage
+ << QString()
+ << OutputParserTester::STDOUT
+ << CustomParserExpression::ParseBothChannels
+ << CustomParserExpression::ParseBothChannels
+ << leadingSpacesPattern << 2 << 3 << 1
+ << QString() << 1 << 2 << 3
+ << QString() << QString()
+ << Tasks({CompileTask(Task::Error, "Error", {}, -1)})
+ << QString();
+ QTest::newRow("leading spaces: no match")
+ << noLeadingSpacesMessage
+ << QString()
+ << OutputParserTester::STDOUT
+ << CustomParserExpression::ParseBothChannels
+ << CustomParserExpression::ParseBothChannels
+ << leadingSpacesPattern << 2 << 3 << 1
+ << QString() << 1 << 2 << 3
+ << (noLeadingSpacesMessage + '\n') << QString()
+ << Tasks()
+ << QString();
+ const QString noLeadingSpacesPattern = "^MESSAGE:(.+)";
+ QTest::newRow("no leading spaces: match")
+ << noLeadingSpacesMessage
+ << QString()
+ << OutputParserTester::STDOUT
+ << CustomParserExpression::ParseBothChannels
+ << CustomParserExpression::ParseBothChannels
+ << noLeadingSpacesPattern << 2 << 3 << 1
+ << QString() << 1 << 2 << 3
+ << QString() << QString()
+ << Tasks({CompileTask(Task::Error, "Error", {}, -1)})
+ << QString();
+ QTest::newRow("no leading spaces: no match")
+ << leadingSpacesMessage
+ << QString()
+ << OutputParserTester::STDOUT
+ << CustomParserExpression::ParseBothChannels
+ << CustomParserExpression::ParseBothChannels
+ << noLeadingSpacesPattern << 3 << 2 << 1
+ << QString() << 1 << 2 << 3
+ << (leadingSpacesMessage + '\n') << QString()
+ << Tasks()
+ << QString();
}
void ProjectExplorerPlugin::testCustomOutputParsers()
diff --git a/src/plugins/projectexplorer/kitmanager.cpp b/src/plugins/projectexplorer/kitmanager.cpp
index 52cf506a885..e67bbe71d25 100644
--- a/src/plugins/projectexplorer/kitmanager.cpp
+++ b/src/plugins/projectexplorer/kitmanager.cpp
@@ -813,7 +813,10 @@ QSet KitFeatureProvider::availablePlatforms() const
QString KitFeatureProvider::displayNameForPlatform(Id id) const
{
if (IDeviceFactory *f = IDeviceFactory::find(id)) {
- const QString dn = f->displayName();
+ QString dn = f->displayName();
+ const QString deviceStr = QStringLiteral("device");
+ if (dn.endsWith(deviceStr, Qt::CaseInsensitive))
+ dn = dn.remove(deviceStr, Qt::CaseInsensitive).trimmed();
QTC_CHECK(!dn.isEmpty());
return dn;
}
diff --git a/src/plugins/projectexplorer/projectwelcomepage.cpp b/src/plugins/projectexplorer/projectwelcomepage.cpp
index 6607454443c..e1adf657ad2 100644
--- a/src/plugins/projectexplorer/projectwelcomepage.cpp
+++ b/src/plugins/projectexplorer/projectwelcomepage.cpp
@@ -124,28 +124,6 @@ void ProjectModel::resetProjects()
ProjectWelcomePage::ProjectWelcomePage()
{
- const int actionsCount = 9;
- Context welcomeContext(Core::Constants::C_WELCOME_MODE);
-
- const Id projectBase = PROJECT_BASE_ID;
- const Id sessionBase = SESSION_BASE_ID;
- for (int i = 1; i <= actionsCount; ++i) {
- auto act = new QAction(tr("Open Session #%1").arg(i), this);
- Command *cmd = ActionManager::registerAction(act, sessionBase.withSuffix(i), welcomeContext);
- cmd->setDefaultKeySequence(QKeySequence((useMacShortcuts ? tr("Ctrl+Meta+%1") : tr("Ctrl+Alt+%1")).arg(i)));
- connect(act, &QAction::triggered, this, [this, i] {
- if (i <= m_sessionModel->rowCount())
- openSessionAt(i - 1);
- });
-
- act = new QAction(tr("Open Recent Project #%1").arg(i), this);
- cmd = ActionManager::registerAction(act, projectBase.withSuffix(i), welcomeContext);
- cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+%1").arg(i)));
- connect(act, &QAction::triggered, this, [this, i] {
- if (i <= m_projectModel->rowCount(QModelIndex()))
- openProjectAt(i - 1);
- });
- }
}
Utils::Id ProjectWelcomePage::id() const
@@ -185,6 +163,40 @@ void ProjectWelcomePage::openProjectAt(int index)
ProjectExplorerPlugin::openProjectWelcomePage(projectFile);
}
+void ProjectWelcomePage::createActions()
+{
+ static bool actionsRegistered = false;
+
+ if (actionsRegistered)
+ return;
+
+ actionsRegistered = true;
+
+ const int actionsCount = 9;
+ Context welcomeContext(Core::Constants::C_WELCOME_MODE);
+
+ const Id projectBase = PROJECT_BASE_ID;
+ const Id sessionBase = SESSION_BASE_ID;
+
+ for (int i = 1; i <= actionsCount; ++i) {
+ auto act = new QAction(tr("Open Session #%1").arg(i), this);
+ Command *cmd = ActionManager::registerAction(act, sessionBase.withSuffix(i), welcomeContext);
+ cmd->setDefaultKeySequence(QKeySequence((useMacShortcuts ? tr("Ctrl+Meta+%1") : tr("Ctrl+Alt+%1")).arg(i)));
+ connect(act, &QAction::triggered, this, [this, i] {
+ if (i <= m_sessionModel->rowCount())
+ openSessionAt(i - 1);
+ });
+
+ act = new QAction(tr("Open Recent Project #%1").arg(i), this);
+ cmd = ActionManager::registerAction(act, projectBase.withSuffix(i), welcomeContext);
+ cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+%1").arg(i)));
+ connect(act, &QAction::triggered, this, [this, i] {
+ if (i <= m_projectModel->rowCount(QModelIndex()))
+ openProjectAt(i - 1);
+ });
+ }
+}
+
///////////////////
static QColor themeColor(Theme::Color role)
@@ -636,7 +648,11 @@ public:
QWidget *ProjectWelcomePage::createWidget() const
{
- return new SessionsPage(const_cast(this));
+ auto that = const_cast(this);
+ QWidget *widget = new SessionsPage(that);
+ that->createActions();
+
+ return widget;
}
} // namespace Internal
diff --git a/src/plugins/projectexplorer/projectwelcomepage.h b/src/plugins/projectexplorer/projectwelcomepage.h
index a24d8333285..f56b220529d 100644
--- a/src/plugins/projectexplorer/projectwelcomepage.h
+++ b/src/plugins/projectexplorer/projectwelcomepage.h
@@ -75,6 +75,7 @@ signals:
private:
void openSessionAt(int index);
void openProjectAt(int index);
+ void createActions();
friend class SessionsPage;
SessionModel *m_sessionModel = nullptr;
diff --git a/src/plugins/qmakeprojectmanager/librarydetailscontroller.cpp b/src/plugins/qmakeprojectmanager/librarydetailscontroller.cpp
index 6a1e405b369..d8deb72b77e 100644
--- a/src/plugins/qmakeprojectmanager/librarydetailscontroller.cpp
+++ b/src/plugins/qmakeprojectmanager/librarydetailscontroller.cpp
@@ -1134,13 +1134,14 @@ QString InternalLibraryDetailsController::snippet() const
const QString proRelavitePath = rootDir.relativeFilePath(proFile().toString());
// project for which we insert the snippet
- const Project *project = SessionManager::projectForFile(proFile());
// the build directory of the active build configuration
QDir rootBuildDir = rootDir; // If the project is unconfigured use the project dir
- if (ProjectExplorer::Target *t = project->activeTarget())
- if (ProjectExplorer::BuildConfiguration *bc = t->activeBuildConfiguration())
- rootBuildDir.setPath(bc->buildDirectory().toString());
+ if (const Project *project = SessionManager::projectForFile(proFile())) {
+ if (ProjectExplorer::Target *t = project->activeTarget())
+ if (ProjectExplorer::BuildConfiguration *bc = t->activeBuildConfiguration())
+ rootBuildDir.setPath(bc->buildDirectory().toString());
+ }
// the project for which we insert the snippet inside build tree
QFileInfo pfi(rootBuildDir.filePath(proRelavitePath));
diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp
index dc9af2da6c4..9fe3524537d 100644
--- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp
+++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp
@@ -27,10 +27,12 @@
#include "assetslibrarydirsmodel.h"
#include "assetslibraryfilesmodel.h"
+#include
+#include
+#include
+#include
#include
#include
-#include
-#include
#include
@@ -100,7 +102,7 @@ void AssetsLibraryModel::toggleExpandAll(bool expand)
endResetModel();
}
-void AssetsLibraryModel::deleteFile(const QString &filePath)
+void AssetsLibraryModel::deleteFiles(const QStringList &filePaths)
{
bool askBeforeDelete = DesignerSettings::getValue(
DesignerSettingsKey::ASK_BEFORE_DELETING_ASSET).toBool();
@@ -108,7 +110,10 @@ void AssetsLibraryModel::deleteFile(const QString &filePath)
if (askBeforeDelete) {
QMessageBox msg(QMessageBox::Question, tr("Confirm Delete File"),
- tr("\"%1\" might be in use. Delete anyway?").arg(filePath),
+ tr("File%1 might be in use. Delete anyway?\n\n%2")
+ .arg(filePaths.size() > 1 ? QChar('s') : QChar())
+ .arg(filePaths.join('\n').remove(DocumentManager::currentProjectDirPath()
+ .toString().append('/'))),
QMessageBox::No | QMessageBox::Yes);
QCheckBox cb;
cb.setText(tr("Do not ask this again"));
@@ -123,14 +128,16 @@ void AssetsLibraryModel::deleteFile(const QString &filePath)
}
if (assetDelete) {
- if (!QFile::exists(filePath)) {
- QMessageBox::warning(Core::ICore::dialogParent(),
- tr("Failed to Locate File"),
- tr("Could not find \"%1\".").arg(filePath));
- } else if (!QFile::remove(filePath)) {
- QMessageBox::warning(Core::ICore::dialogParent(),
- tr("Failed to Delete File"),
- tr("Could not delete \"%1\".").arg(filePath));
+ for (const QString &filePath : filePaths) {
+ if (!QFile::exists(filePath)) {
+ QMessageBox::warning(Core::ICore::dialogParent(),
+ tr("Failed to Locate File"),
+ tr("Could not find \"%1\".").arg(filePath));
+ } else if (!QFile::remove(filePath)) {
+ QMessageBox::warning(Core::ICore::dialogParent(),
+ tr("Failed to Delete File"),
+ tr("Could not delete \"%1\".").arg(filePath));
+ }
}
}
}
@@ -298,8 +305,8 @@ void AssetsLibraryModel::setRootPath(const QString &path)
m_fileSystemWatcher->clear();
- std::function parseDirRecursive;
- parseDirRecursive = [this, &parseDirRecursive](AssetsLibraryDir *currAssetsDir, int currDepth) {
+ std::function parseDir;
+ parseDir = [this, &parseDir](AssetsLibraryDir *currAssetsDir, int currDepth, bool recursive) {
m_fileSystemWatcher->addDirectory(currAssetsDir->dirPath(), Utils::FileSystemWatcher::WatchAllChanges);
QDir dir(currAssetsDir->dirPath());
@@ -317,20 +324,22 @@ void AssetsLibraryModel::setRootPath(const QString &path)
}
}
- dir.setNameFilters({});
- dir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
- QDirIterator itDirs(dir);
+ if (recursive) {
+ dir.setNameFilters({});
+ dir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
+ QDirIterator itDirs(dir);
- while (itDirs.hasNext()) {
- QDir subDir = itDirs.next();
- if (currDepth == 1 && ignoredTopLevelDirs.contains(subDir.dirName()))
- continue;
+ while (itDirs.hasNext()) {
+ QDir subDir = itDirs.next();
+ if (currDepth == 1 && ignoredTopLevelDirs.contains(subDir.dirName()))
+ continue;
- auto assetsDir = new AssetsLibraryDir(subDir.path(), currDepth,
- loadExpandedState(subDir.path()), currAssetsDir);
- currAssetsDir->addDir(assetsDir);
- saveExpandedState(loadExpandedState(assetsDir->dirPath()), assetsDir->dirPath());
- isEmpty &= parseDirRecursive(assetsDir, currDepth + 1);
+ auto assetsDir = new AssetsLibraryDir(subDir.path(), currDepth,
+ loadExpandedState(subDir.path()), currAssetsDir);
+ currAssetsDir->addDir(assetsDir);
+ saveExpandedState(loadExpandedState(assetsDir->dirPath()), assetsDir->dirPath());
+ isEmpty &= parseDir(assetsDir, currDepth + 1, true);
+ }
}
if (!m_searchText.isEmpty() && isEmpty)
@@ -344,7 +353,8 @@ void AssetsLibraryModel::setRootPath(const QString &path)
beginResetModel();
m_assetsDir = new AssetsLibraryDir(path, 0, true, this);
- bool isEmpty = parseDirRecursive(m_assetsDir, 1);
+ bool hasProject = !QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath().isEmpty();
+ bool isEmpty = parseDir(m_assetsDir, 1, hasProject);
setIsEmpty(isEmpty);
bool noAssets = m_searchText.isEmpty() && isEmpty;
@@ -363,7 +373,7 @@ void AssetsLibraryModel::setSearchText(const QString &searchText)
}
}
-const QSet &AssetsLibraryModel::supportedSuffixes() const
+const QSet &AssetsLibraryModel::supportedSuffixes()
{
static QSet allSuffixes;
if (allSuffixes.isEmpty()) {
diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h
index b27d1267866..2b87359e07d 100644
--- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h
+++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h
@@ -68,6 +68,7 @@ public:
static const QStringList &supportedAudioSuffixes();
static const QStringList &supportedVideoSuffixes();
static const QStringList &supportedTexture3DSuffixes();
+ static const QSet &supportedSuffixes();
const QSet &previewableSuffixes() const;
@@ -83,7 +84,7 @@ public:
Q_INVOKABLE void toggleExpandAll(bool expand);
Q_INVOKABLE DirExpandState getAllExpandedState() const;
- Q_INVOKABLE void deleteFile(const QString &filePath);
+ Q_INVOKABLE void deleteFiles(const QStringList &filePaths);
Q_INVOKABLE bool renameFolder(const QString &folderPath, const QString &newName);
Q_INVOKABLE void addNewFolder(const QString &folderPath);
Q_INVOKABLE void deleteFolder(const QString &folderPath);
@@ -93,7 +94,6 @@ signals:
void isEmptyChanged();
private:
- const QSet &supportedSuffixes() const;
void setIsEmpty(bool empty);
diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp
index 21478fd39d3..b68128099b5 100644
--- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp
+++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp
@@ -213,9 +213,24 @@ void AssetsLibraryWidget::handleAddAsset()
addResources({});
}
-void AssetsLibraryWidget::handleFilesDrop(const QStringList &filesPaths)
+void AssetsLibraryWidget::handleExtFilesDrop(const QStringList &filesPaths, const QString &targetDirPath)
{
- addResources(filesPaths);
+ QStringList assetPaths;
+ QStringList otherPaths; // as of now 3D models, and 3D Studio presentations
+ std::tie(assetPaths, otherPaths) = Utils::partition(filesPaths, [](const QString &path) {
+ QString suffix = "*." + path.split('.').last().toLower();
+ return AssetsLibraryModel::supportedSuffixes().contains(suffix);
+ });
+
+ AddFilesResult result = ModelNodeOperations::addFilesToProject(assetPaths, targetDirPath);
+ if (result == AddFilesResult::Failed) {
+ Core::AsynchronousMessageBox::warning(tr("Failed to Add Files"),
+ tr("Could not add %1 to project.")
+ .arg(filesPaths.join(' ')));
+ }
+
+ if (!otherPaths.empty())
+ addResources(otherPaths);
}
QSet AssetsLibraryWidget::supportedDropSuffixes()
diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h
index 6c27be54a61..7d3f48ef4dc 100644
--- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h
+++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h
@@ -80,7 +80,7 @@ public:
Q_INVOKABLE void startDragAsset(const QStringList &assetPaths, const QPointF &mousePos);
Q_INVOKABLE void handleAddAsset();
Q_INVOKABLE void handleSearchfilterChanged(const QString &filterText);
- Q_INVOKABLE void handleFilesDrop(const QStringList &filesPaths);
+ Q_INVOKABLE void handleExtFilesDrop(const QStringList &filesPaths, const QString &targetDirPath);
Q_INVOKABLE QSet supportedDropSuffixes();
signals:
diff --git a/src/plugins/qmldesigner/components/componentcore/crumblebar.cpp b/src/plugins/qmldesigner/components/componentcore/crumblebar.cpp
index c729fd07512..82032b3fc2e 100644
--- a/src/plugins/qmldesigner/components/componentcore/crumblebar.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/crumblebar.cpp
@@ -83,20 +83,29 @@ void CrumbleBar::pushFile(const Utils::FilePath &fileName)
if (!m_isInternalCalled) {
crumblePath()->clear();
} else {
- CrumbleBarInfo lastElementCrumbleBarInfo = crumblePath()->dataForLastIndex().value();
+ // If the path already exists in crumblePath, pop up to first instance of that to avoid
+ // cyclical crumblePath
+ int match = -1;
+ for (int i = crumblePath()->length() - 1; i >= 0; --i) {
+ CrumbleBarInfo info = crumblePath()->dataForIndex(i).value();
+ if (info.fileName == fileName)
+ match = i;
+ }
- if (!lastElementCrumbleBarInfo.displayName.isEmpty()
- && lastElementCrumbleBarInfo.fileName == fileName)
- crumblePath()->popElement();
+ if (match != -1) {
+ for (int i = crumblePath()->length() - 1 - match; i > 0; --i)
+ crumblePath()->popElement();
+ }
}
- CrumbleBarInfo crumbleBarInfo;
- crumbleBarInfo.fileName = fileName;
-
- crumblePath()->pushElement(fileName.fileName(), QVariant::fromValue(crumbleBarInfo));
+ CrumbleBarInfo info = crumblePath()->dataForLastIndex().value();
+ if (info.fileName != fileName) {
+ CrumbleBarInfo crumbleBarInfo;
+ crumbleBarInfo.fileName = fileName;
+ crumblePath()->pushElement(fileName.fileName(), QVariant::fromValue(crumbleBarInfo));
+ }
m_isInternalCalled = false;
-
updateVisibility();
}
diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp
index 79242905905..f3a93650dfa 100644
--- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp
@@ -989,7 +989,7 @@ void addTabBarToStackedContainer(const SelectionContext &selectionContext)
}
-static AddFilesResult addFilesToProject(const QStringList &fileNames, const QString &defaultDirectory)
+AddFilesResult addFilesToProject(const QStringList &fileNames, const QString &defaultDirectory)
{
QString directory = AddImagesDialog::getDirectory(fileNames, defaultDirectory);
if (directory.isEmpty())
diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h
index f9e39c06f48..317d2d15e7e 100644
--- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h
+++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h
@@ -76,6 +76,7 @@ void addItemToStackedContainer(const SelectionContext &selectionContext);
void increaseIndexOfStackedContainer(const SelectionContext &selectionContext);
void decreaseIndexOfStackedContainer(const SelectionContext &selectionContext);
void addTabBarToStackedContainer(const SelectionContext &selectionContext);
+AddFilesResult addFilesToProject(const QStringList &fileNames, const QString &defaultDirectory);
AddFilesResult addImageToProject(const QStringList &fileNames, const QString &directory);
AddFilesResult addFontToProject(const QStringList &fileNames, const QString &directory);
AddFilesResult addSoundToProject(const QStringList &fileNames, const QString &directory);
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp
index bb795eb40fc..f664d793d80 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp
@@ -118,17 +118,18 @@ void Edit3DView::renderImage3DChanged(const QImage &img)
void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState)
{
- const QString sceneKey = QStringLiteral("sceneInstanceId");
- const QString selectKey = QStringLiteral("selectionMode");
- const QString transformKey = QStringLiteral("transformMode");
- const QString perspectiveKey = QStringLiteral("usePerspective");
- const QString orientationKey = QStringLiteral("globalOrientation");
- const QString editLightKey = QStringLiteral("showEditLight");
- const QString gridKey = QStringLiteral("showGrid");
- const QString selectionBoxKey = QStringLiteral("showSelectionBox");
- const QString iconGizmoKey = QStringLiteral("showIconGizmo");
- const QString cameraFrustumKey = QStringLiteral("showCameraFrustum");
- const QString particlesPlayKey = QStringLiteral("particlePlay");
+ const QString sceneKey = QStringLiteral("sceneInstanceId");
+ const QString selectKey = QStringLiteral("selectionMode");
+ const QString transformKey = QStringLiteral("transformMode");
+ const QString perspectiveKey = QStringLiteral("usePerspective");
+ const QString orientationKey = QStringLiteral("globalOrientation");
+ const QString editLightKey = QStringLiteral("showEditLight");
+ const QString gridKey = QStringLiteral("showGrid");
+ const QString selectionBoxKey = QStringLiteral("showSelectionBox");
+ const QString iconGizmoKey = QStringLiteral("showIconGizmo");
+ const QString cameraFrustumKey = QStringLiteral("showCameraFrustum");
+ const QString particleEmitterKey = QStringLiteral("showParticleEmitter");
+ const QString particlesPlayKey = QStringLiteral("particlePlay");
if (sceneState.contains(sceneKey)) {
qint32 newActiveScene = sceneState[sceneKey].value();
@@ -188,6 +189,11 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState)
else
m_showCameraFrustumAction->action()->setChecked(false);
+ if (sceneState.contains(particleEmitterKey))
+ m_showParticleEmitterAction->action()->setChecked(sceneState[particleEmitterKey].toBool());
+ else
+ m_showParticleEmitterAction->action()->setChecked(false);
+
if (sceneState.contains(particlesPlayKey))
m_particlesPlayAction->action()->setChecked(sceneState[particlesPlayKey].toBool());
else
@@ -352,6 +358,12 @@ void Edit3DView::createEdit3DActions()
QKeySequence(Qt::Key_C), true, false, {}, {}, nullptr,
QCoreApplication::translate("ShowCameraFrustumAction", "Toggle between always showing the camera frustum visualization and only showing it when the camera is selected."));
+ m_showParticleEmitterAction = new Edit3DAction(
+ QmlDesigner::Constants::EDIT3D_EDIT_SHOW_PARTICLE_EMITTER, View3DActionCommand::ShowParticleEmitter,
+ QCoreApplication::translate("ShowParticleEmitterAction", "Always Show Particle Emitters"),
+ QKeySequence(Qt::Key_M), true, false, {}, {}, nullptr,
+ QCoreApplication::translate("ShowParticleEmitterAction", "Toggle between always showing the particle emitter visualization and only showing it when the emitter is selected."));
+
SelectionContextOperation resetTrigger = [this](const SelectionContext &) {
m_particlesPlayAction->action()->setEnabled(particlemode);
m_particlesRestartAction->action()->setEnabled(particlemode);
@@ -390,13 +402,13 @@ void Edit3DView::createEdit3DActions()
= new Edit3DAction(
QmlDesigner::Constants::EDIT3D_PARTICLES_PLAY, View3DActionCommand::ParticlesPlay,
QCoreApplication::translate("ParticlesPlayAction", "Play Particles"),
- QKeySequence(Qt::Key_W), true, true, Icons::EDIT3D_PARTICLE_PLAY.icon(),
+ QKeySequence(Qt::Key_Comma), true, true, Icons::EDIT3D_PARTICLE_PLAY.icon(),
Icons::EDIT3D_PARTICLE_PAUSE.icon(), particlesPlayTrigger);
m_particlesRestartAction
= new Edit3DAction(
QmlDesigner::Constants::EDIT3D_PARTICLES_RESTART, View3DActionCommand::ParticlesRestart,
QCoreApplication::translate("ParticlesRestartAction", "Restart Particles"),
- QKeySequence(Qt::Key_E), false, false, Icons::EDIT3D_PARTICLE_RESTART.icon(),
+ QKeySequence(Qt::Key_Slash), false, false, Icons::EDIT3D_PARTICLE_RESTART.icon(),
Icons::EDIT3D_PARTICLE_RESTART.icon());
m_particlesPlayAction->action()->setEnabled(particlemode);
m_particlesRestartAction->action()->setEnabled(particlemode);
@@ -458,6 +470,7 @@ void Edit3DView::createEdit3DActions()
m_visibilityToggleActions << m_showSelectionBoxAction;
m_visibilityToggleActions << m_showIconGizmoAction;
m_visibilityToggleActions << m_showCameraFrustumAction;
+ m_visibilityToggleActions << m_showParticleEmitterAction;
}
QVector Edit3DView::leftActions() const
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h
index daddb84d6c8..40e21708734 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h
@@ -102,6 +102,7 @@ private:
Edit3DAction *m_showSelectionBoxAction = nullptr;
Edit3DAction *m_showIconGizmoAction = nullptr;
Edit3DAction *m_showCameraFrustumAction = nullptr;
+ Edit3DAction *m_showParticleEmitterAction = nullptr;
Edit3DAction *m_resetAction = nullptr;
Edit3DAction *m_particleViewModeAction = nullptr;
Edit3DAction *m_particlesPlayAction = nullptr;
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp
index 945820d9134..72cb0ec21c8 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp
@@ -79,6 +79,7 @@ Edit3DWidget::Edit3DWidget(Edit3DView *view) :
auto handleActions = [this, &context](const QVector &actions, QMenu *menu, bool left) {
bool previousWasSeparator = true;
QActionGroup *group = nullptr;
+ QActionGroup *proxyGroup = nullptr;
for (auto action : actions) {
if (action) {
QAction *a = action->action();
@@ -99,6 +100,8 @@ Edit3DWidget::Edit3DWidget(Edit3DView *view) :
Core::Command *command = Core::ActionManager::registerAction(
a, action->menuId().constData(), context);
command->setDefaultKeySequence(a->shortcut());
+ if (proxyGroup)
+ proxyGroup->addAction(command->action());
// Menu actions will have custom tooltips
if (menu)
a->setToolTip(command->stringWithAppendedShortcut(a->toolTip()));
@@ -110,9 +113,11 @@ Edit3DWidget::Edit3DWidget(Edit3DView *view) :
} else {
if (previousWasSeparator) {
group = new QActionGroup(this);
+ proxyGroup = new QActionGroup(this);
previousWasSeparator = false;
} else {
group = nullptr;
+ proxyGroup = nullptr;
auto separator = new QAction(this);
separator->setSeparator(true);
if (menu) {
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecacheconnectionmanager.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecacheconnectionmanager.cpp
index a33da7df5d8..d1cd65b4c46 100644
--- a/src/plugins/qmldesigner/designercore/imagecache/imagecacheconnectionmanager.cpp
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecacheconnectionmanager.cpp
@@ -41,6 +41,8 @@ bool ImageCacheConnectionManager::waitForCapturedData()
disconnect(connections().front().socket.get(), &QIODevice::readyRead, nullptr, nullptr);
while (!m_capturedDataArrived) {
+ if (!(connections().front().socket))
+ return false;
bool dataArrived = connections().front().socket->waitForReadyRead(10000);
if (!dataArrived)
diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp
index 48391fe4b9c..547d7ad8a60 100644
--- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp
+++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp
@@ -555,6 +555,13 @@ void NodeInstanceView::nodeReparented(const ModelNode &node, const NodeAbstractP
updateChildren(newPropertyParent);
m_nodeInstanceServer->reparentInstances(
createReparentInstancesCommand(node, newPropertyParent, oldPropertyParent));
+
+ // Reset puppet when particle emitter is reparented to work around issue in
+ // autodetecting the particle system it belongs to. QTBUG-101157
+ if (node.isSubclassOf("QtQuick.Particles3D.ParticleEmitter3D")
+ && node.property("system").toBindingProperty().expression().isEmpty()) {
+ resetPuppet();
+ }
}
}
diff --git a/src/plugins/qmldesigner/designercore/model/modelnode.cpp b/src/plugins/qmldesigner/designercore/model/modelnode.cpp
index 98f4f310851..288d9dadc4f 100644
--- a/src/plugins/qmldesigner/designercore/model/modelnode.cpp
+++ b/src/plugins/qmldesigner/designercore/model/modelnode.cpp
@@ -195,7 +195,9 @@ static bool isIdToAvoid(const QString& id)
"enabled",
"anchors",
"texture",
- "shaderInfo"
+ "shaderInfo",
+ "sprite",
+ "spriteSequence"
};
return ids.contains(id);
diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h
index 03c2f42cca3..fc394ae089c 100644
--- a/src/plugins/qmldesigner/qmldesignerconstants.h
+++ b/src/plugins/qmldesigner/qmldesignerconstants.h
@@ -68,6 +68,7 @@ const char EDIT3D_EDIT_SHOW_GRID[] = "QmlDesigner.Editor3D.ToggleGrid";
const char EDIT3D_EDIT_SHOW_SELECTION_BOX[] = "QmlDesigner.Editor3D.ToggleSelectionBox";
const char EDIT3D_EDIT_SHOW_ICON_GIZMO[] = "QmlDesigner.Editor3D.ToggleIconGizmo";
const char EDIT3D_EDIT_SHOW_CAMERA_FRUSTUM[] = "QmlDesigner.Editor3D.ToggleCameraFrustum";
+const char EDIT3D_EDIT_SHOW_PARTICLE_EMITTER[] = "QmlDesigner.Editor3D.ToggleParticleEmitter";
const char EDIT3D_RESET_VIEW[] = "QmlDesigner.Editor3D.ResetView";
const char EDIT3D_PARTICLE_MODE[] = "QmlDesigner.Editor3D.ParticleViewModeToggle";
const char EDIT3D_PARTICLES_PLAY[] = "QmlDesigner.Editor3D.ParticlesPlay";
diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp
index 2a25819d6e2..b9ea5c5107b 100644
--- a/src/plugins/qmldesigner/qmldesignerplugin.cpp
+++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp
@@ -291,16 +291,15 @@ bool QmlDesignerPlugin::delayedInitialize()
d->settings.fromSettings(Core::ICore::settings());
d->viewManager.registerView(std::make_unique());
- if (DesignerSettings::getValue(DesignerSettingsKey::ENABLE_TIMELINEVIEW).toBool()) {
- auto timelineView = d->viewManager.registerView(std::make_unique());
- timelineView->registerActions();
- d->viewManager.registerView(std::make_unique());
+ auto timelineView = d->viewManager.registerView(std::make_unique());
+ timelineView->registerActions();
- auto eventlistView = d->viewManager.registerView(
- std::make_unique());
- eventlistView->registerActions();
- }
+ d->viewManager.registerView(std::make_unique());
+
+ auto eventlistView = d->viewManager.registerView(
+ std::make_unique());
+ eventlistView->registerActions();
auto transitionEditorView = d->viewManager.registerView(
std::make_unique());
@@ -316,8 +315,12 @@ bool QmlDesignerPlugin::delayedInitialize()
emitUsageStatistics("StandaloneMode");
if (QmlProjectManager::QmlProject::isQtDesignStudioStartedFromQtC())
emitUsageStatistics("QDSlaunchedFromQtC");
+ emitUsageStatistics("QDSstartupCount");
}
+ if (QmlProjectManager::QmlProject::isQtDesignStudio())
+ d->mainWidget.initialize();
+
return true;
}
@@ -329,9 +332,6 @@ void QmlDesignerPlugin::extensionsInitialized()
integrateIntoQtCreator(&d->mainWidget);
});
- if (QmlProjectManager::QmlProject::isQtDesignStudio())
- d->mainWidget.initialize();
-
auto &actionManager = d->viewManager.designerActionManager();
actionManager.createDefaultDesignerActions();
actionManager.createDefaultAddResourceHandler();
@@ -339,6 +339,14 @@ void QmlDesignerPlugin::extensionsInitialized()
actionManager.polishActions();
}
+ExtensionSystem::IPlugin::ShutdownFlag QmlDesignerPlugin::aboutToShutdown()
+{
+ if (QmlProjectManager::QmlProject::isQtDesignStudio())
+ emitUsageStatistics("QDSstartupCount");
+
+ return SynchronousShutdown;
+}
+
static QStringList allUiQmlFilesforCurrentProject(const Utils::FilePath &fileName)
{
QStringList list;
diff --git a/src/plugins/qmldesigner/qmldesignerplugin.h b/src/plugins/qmldesigner/qmldesignerplugin.h
index 22800aa0454..0cd21bd2940 100644
--- a/src/plugins/qmldesigner/qmldesignerplugin.h
+++ b/src/plugins/qmldesigner/qmldesignerplugin.h
@@ -60,6 +60,7 @@ public:
bool initialize(const QStringList &arguments, QString *errorMessage) final;
bool delayedInitialize() final;
void extensionsInitialized() final;
+ ShutdownFlag aboutToShutdown() final;
static QmlDesignerPlugin *instance();
diff --git a/src/plugins/qtsupport/gettingstartedwelcomepage.cpp b/src/plugins/qtsupport/gettingstartedwelcomepage.cpp
index 93b630c3eb2..e163165f962 100644
--- a/src/plugins/qtsupport/gettingstartedwelcomepage.cpp
+++ b/src/plugins/qtsupport/gettingstartedwelcomepage.cpp
@@ -326,8 +326,9 @@ public:
void onTagClicked(const QString &tag)
{
- QString text = m_searcher->text();
- m_searcher->setText(text + QString("tag:\"%1\" ").arg(tag));
+ const QString text = m_searcher->text();
+ m_searcher->setText((text.startsWith("tag:\"") ? text.trimmed() + " " : QString())
+ + QString("tag:\"%1\" ").arg(tag));
}
const bool m_isExamples;
diff --git a/src/plugins/studiowelcome/CMakeLists.txt b/src/plugins/studiowelcome/CMakeLists.txt
index a8b92ab1f8b..209bb80b01c 100644
--- a/src/plugins/studiowelcome/CMakeLists.txt
+++ b/src/plugins/studiowelcome/CMakeLists.txt
@@ -14,6 +14,7 @@ add_qtc_plugin(StudioWelcome
createproject.cpp createproject.h
wizardhandler.cpp wizardhandler.h
recentpresets.cpp recentpresets.h
+ userpresets.cpp userpresets.h
screensizemodel.h
algorithm.h
stylemodel.h stylemodel.cpp
diff --git a/src/plugins/studiowelcome/presetmodel.cpp b/src/plugins/studiowelcome/presetmodel.cpp
index b8db0503f24..cc06bf59f54 100644
--- a/src/plugins/studiowelcome/presetmodel.cpp
+++ b/src/plugins/studiowelcome/presetmodel.cpp
@@ -25,52 +25,132 @@
#include "presetmodel.h"
#include
-#include
#include "algorithm.h"
using namespace StudioWelcome;
+constexpr int NameRole = Qt::UserRole;
+constexpr int ScreenSizeRole = Qt::UserRole + 1;
+constexpr int IsUserPresetRole = Qt::UserRole + 2;
+
+static const QString RecentsTabName = QObject::tr("Recents");
+static const QString CustomTabName = QObject::tr("Custom");
+
/****************** PresetData ******************/
-void PresetData::setData(const PresetsByCategory &presetsByCategory,
- const std::vector &loadedRecents)
-{
- QTC_ASSERT(!presetsByCategory.empty(), return);
- m_recents = loadedRecents;
- if (!m_recents.empty()) {
- m_categories.push_back("Recents");
- m_presets.push_back({});
- }
+QString PresetData::recentsTabName()
+{
+ return RecentsTabName;
+}
+
+void PresetData::setData(const PresetsByCategory &presetsByCategory,
+ const std::vector &userPresetsData,
+ const std::vector &loadedRecentsData)
+{
+ QTC_ASSERT(!presetsByCategory.empty(), return );
+ m_recents = loadedRecentsData;
+ m_userPresets = userPresetsData;
for (auto &[id, category] : presetsByCategory) {
m_categories.push_back(category.name);
m_presets.push_back(category.items);
}
- PresetItems presets = Utils::flatten(m_presets);
+ PresetItems wizardPresets = Utils::flatten(m_presets);
- std::vector recentPresets = makeRecentPresets(presets);
+ PresetItems userPresetItems = makeUserPresets(wizardPresets);
+ if (!userPresetItems.empty()) {
+ m_categories.push_back(CustomTabName);
+ m_presets.push_back(userPresetItems);
+ }
- if (!m_recents.empty())
- m_presets[0] = recentPresets;
+ PresetItems allWizardPresets = std::move(wizardPresets);
+ Utils::concat(allWizardPresets, userPresetItems);
+
+ PresetItems recentPresets = makeRecentPresets(allWizardPresets);
+ if (!recentPresets.empty()) {
+ Utils::prepend(m_categories, RecentsTabName);
+ Utils::prepend(m_presets, recentPresets);
+ }
+
+ m_presetsByCategory = presetsByCategory;
}
-std::vector PresetData::makeRecentPresets(const PresetItems &wizardPresets)
+void PresetData::reload(const std::vector &userPresetsData,
+ const std::vector &loadedRecentsData)
{
- static const PresetItem empty;
+ m_categories.clear();
+ m_presets.clear();
+ m_recents.clear();
+ m_userPresets.clear();
+ setData(m_presetsByCategory, userPresetsData, loadedRecentsData);
+}
+std::shared_ptr PresetData::findPresetItemForUserPreset(const UserPresetData &preset,
+ const PresetItems &wizardPresets)
+{
+ return Utils::findOrDefault(wizardPresets, [&preset](const std::shared_ptr &item) {
+ return item->wizardName == preset.wizardName && item->categoryId == preset.categoryId;
+ });
+}
+
+PresetItems PresetData::makeUserPresets(const PresetItems &wizardPresets)
+{
PresetItems result;
- for (const RecentPreset &recent : m_recents) {
- auto item = Utils::findOptional(wizardPresets, [&recent](const PresetItem &item) {
- return item.categoryId == std::get<0>(recent) && item.name == std::get<1>(recent);
- });
+ for (const UserPresetData &userPresetData : m_userPresets) {
+ std::shared_ptr foundPreset = findPresetItemForUserPreset(userPresetData,
+ wizardPresets);
+ if (!foundPreset)
+ continue;
- if (item) {
- item->screenSizeName = std::get<2>(recent);
- result.push_back(item.value());
+ auto presetItem = std::make_shared();
+
+ presetItem->categoryId = userPresetData.categoryId;
+ presetItem->wizardName = userPresetData.wizardName;
+ presetItem->screenSizeName = userPresetData.screenSize;
+
+ presetItem->userName = userPresetData.name;
+ presetItem->qtVersion = userPresetData.qtVersion;
+ presetItem->styleName = userPresetData.styleName;
+ presetItem->useQtVirtualKeyboard = userPresetData.useQtVirtualKeyboard;
+
+ presetItem->create = foundPreset->create;
+ presetItem->description = foundPreset->description;
+ presetItem->fontIconCode = foundPreset->fontIconCode;
+ presetItem->qmlPath = foundPreset->qmlPath;
+
+ result.push_back(presetItem);
+ }
+
+ return result;
+}
+
+std::shared_ptr PresetData::findPresetItemForRecent(const RecentPresetData &recent, const PresetItems &wizardPresets)
+{
+ return Utils::findOrDefault(wizardPresets, [&recent](const std::shared_ptr &item) {
+ bool sameName = item->categoryId == recent.category
+ && item->displayName() == recent.presetName;
+
+ bool sameType = (recent.isUserPreset ? item->isUserPreset() : !item->isUserPreset());
+
+ return sameName && sameType;
+ });
+}
+
+PresetItems PresetData::makeRecentPresets(const PresetItems &wizardPresets)
+{
+ PresetItems result;
+
+ for (const RecentPresetData &recent : m_recents) {
+ std::shared_ptr preset = findPresetItemForRecent(recent, wizardPresets);
+
+ if (preset) {
+ auto clone = std::shared_ptr{preset->clone()};
+ clone->screenSizeName = recent.sizeName;
+ result.push_back(clone);
}
}
@@ -86,8 +166,8 @@ BasePresetModel::BasePresetModel(const PresetData *data, QObject *parent)
QHash BasePresetModel::roleNames() const
{
- QHash roleNames;
- roleNames[Qt::UserRole] = "name";
+ static QHash roleNames{{NameRole, "name"},
+ {ScreenSizeRole, "resolution"}};
return roleNames;
}
@@ -97,8 +177,9 @@ PresetCategoryModel::PresetCategoryModel(const PresetData *data, QObject *parent
: BasePresetModel(data, parent)
{}
-int PresetCategoryModel::rowCount(const QModelIndex &) const
+int PresetCategoryModel::rowCount(const QModelIndex &parent) const
{
+ Q_UNUSED(parent)
return static_cast(m_data->categories().size());
}
@@ -116,9 +197,9 @@ PresetModel::PresetModel(const PresetData *data, QObject *parent)
QHash PresetModel::roleNames() const
{
- QHash roleNames;
- roleNames[Qt::UserRole] = "name";
- roleNames[Qt::UserRole + 1] = "size";
+ static QHash roleNames{{NameRole, "name"},
+ {ScreenSizeRole, "resolution"},
+ {IsUserPresetRole, "isUserPreset"}};
return roleNames;
}
@@ -132,9 +213,16 @@ int PresetModel::rowCount(const QModelIndex &) const
QVariant PresetModel::data(const QModelIndex &index, int role) const
{
- Q_UNUSED(role)
- PresetItem preset = presetsOfCurrentCategory().at(index.row());
- return QVariant::fromValue(preset.name + "\n" + preset.screenSizeName);
+ std::shared_ptr preset = presetsOfCurrentCategory().at(index.row());
+
+ if (role == NameRole)
+ return preset->displayName();
+ else if (role == ScreenSizeRole)
+ return preset->screenSize();
+ else if (role == IsUserPresetRole)
+ return preset->isUserPreset();
+ else
+ return {};
}
void PresetModel::setPage(int index)
@@ -148,7 +236,7 @@ void PresetModel::setPage(int index)
QString PresetModel::fontIconCode(int index) const
{
- Utils::optional presetItem = preset(index);
+ std::shared_ptr presetItem = preset(index);
if (!presetItem)
return {};
diff --git a/src/plugins/studiowelcome/presetmodel.h b/src/plugins/studiowelcome/presetmodel.h
index a1c9b0e7d27..e4c6712b81b 100644
--- a/src/plugins/studiowelcome/presetmodel.h
+++ b/src/plugins/studiowelcome/presetmodel.h
@@ -31,8 +31,10 @@
#include
#include
+#include
#include "recentpresets.h"
+#include "userpresets.h"
namespace Utils {
class Wizard;
@@ -40,38 +42,115 @@ class Wizard;
namespace StudioWelcome {
+struct UserPresetItem;
+
struct PresetItem
{
- QString name;
+ PresetItem() = default;
+ PresetItem(const QString &wizardName, const QString &category, const QString &sizeName = "")
+ : wizardName{wizardName}
+ , categoryId{category}
+ , screenSizeName{sizeName}
+ {}
+
+ virtual ~PresetItem() {}
+ virtual QString displayName() const { return wizardName; }
+ virtual QString screenSize() const { return screenSizeName; }
+ virtual std::unique_ptr clone() const
+ {
+ return std::unique_ptr{new PresetItem{*this}};
+ }
+
+ virtual bool isUserPreset() const { return false; }
+ virtual UserPresetItem *asUserPreset() { return nullptr; }
+ std::function create;
+
+public:
+ QString wizardName;
QString categoryId;
QString screenSizeName;
QString description;
QUrl qmlPath;
QString fontIconCode;
- std::function create;
};
+struct UserPresetItem : public PresetItem
+{
+ UserPresetItem() = default;
+ UserPresetItem(const QString &userName,
+ const QString &wizardName,
+ const QString &category,
+ const QString &sizeName = "")
+ : PresetItem{wizardName, category, sizeName}
+ , userName{userName}
+ {}
+
+ QString displayName() const override { return userName; }
+ std::unique_ptr clone() const override
+ {
+ return std::unique_ptr{new UserPresetItem{*this}};
+ }
+
+ bool isUserPreset() const override { return true; }
+ UserPresetItem *asUserPreset() override { return this; }
+
+ bool isValid() const
+ {
+ return !categoryId.isEmpty() && !wizardName.isEmpty() && !userName.isEmpty();
+ }
+
+public:
+ QString userName;
+ bool useQtVirtualKeyboard;
+ QString qtVersion;
+ QString styleName;
+};
+
+inline QDebug &operator<<(QDebug &d, const UserPresetItem &item);
+
inline QDebug &operator<<(QDebug &d, const PresetItem &item)
{
- d << "name=" << item.name;
+ d << "wizardName=" << item.wizardName;
d << "; category = " << item.categoryId;
d << "; size = " << item.screenSizeName;
+ if (item.isUserPreset())
+ d << "; " << (UserPresetItem &) item;
+
+ return d;
+}
+
+inline QDebug &operator<<(QDebug &d, const UserPresetItem &item)
+{
+ d << "userName=" << item.userName;
+
return d;
}
inline bool operator==(const PresetItem &lhs, const PresetItem &rhs)
{
- return lhs.categoryId == rhs.categoryId && lhs.name == rhs.name;
+ return lhs.categoryId == rhs.categoryId && lhs.wizardName == rhs.wizardName;
}
+using PresetItems = std::vector>;
+
struct WizardCategory
{
QString id;
QString name;
- std::vector items;
+ PresetItems items;
};
+inline QDebug &operator<<(QDebug &d, const std::shared_ptr &preset)
+{
+ if (preset)
+ d << *preset;
+ else
+ d << "(null)";
+
+ return d;
+}
+
inline QDebug &operator<<(QDebug &d, const WizardCategory &cat)
{
d << "id=" << cat.id;
@@ -82,7 +161,6 @@ inline QDebug &operator<<(QDebug &d, const WizardCategory &cat)
}
using PresetsByCategory = std::map;
-using PresetItems = std::vector;
using Categories = std::vector;
/****************** PresetData ******************/
@@ -90,18 +168,30 @@ using Categories = std::vector;
class PresetData
{
public:
- void setData(const PresetsByCategory &presets, const std::vector &recents);
+ void reload(const std::vector &userPresets,
+ const std::vector &loadedRecents);
+ void setData(const PresetsByCategory &presets,
+ const std::vector &userPresets,
+ const std::vector &recents);
const std::vector &presets() const { return m_presets; }
const Categories &categories() const { return m_categories; }
+ static QString recentsTabName();
+
private:
- std::vector makeRecentPresets(const PresetItems &wizardPresets);
+ PresetItems makeRecentPresets(const PresetItems &wizardPresets);
+ PresetItems makeUserPresets(const PresetItems &wizardPresets);
+
+ std::shared_ptr findPresetItemForUserPreset(const UserPresetData &preset, const PresetItems &wizardPresets);
+ std::shared_ptr findPresetItemForRecent(const RecentPresetData &recent, const PresetItems &wizardPresets);
private:
std::vector m_presets;
Categories m_categories;
- std::vector m_recents;
+ std::vector m_recents;
+ std::vector m_userPresets;
+ PresetsByCategory m_presetsByCategory;
};
/****************** PresetCategoryModel ******************/
@@ -149,14 +239,14 @@ public:
int page() const { return static_cast(m_page); }
- Utils::optional preset(size_t selection) const
+ const std::shared_ptr preset(size_t selection) const
{
auto presets = m_data->presets();
if (presets.empty())
return {};
if (m_page < presets.size()) {
- const std::vector presetsOfCategory = presets.at(m_page);
+ const PresetItems presetsOfCategory = presets.at(m_page);
if (selection < presetsOfCategory.size())
return presets.at(m_page).at(selection);
}
@@ -166,8 +256,10 @@ public:
bool empty() const { return m_data->presets().empty(); }
private:
- const std::vector presetsOfCurrentCategory() const
+ const PresetItems presetsOfCurrentCategory() const
{
+ QTC_ASSERT(m_page < m_data->presets().size(), return {});
+
return m_data->presets().at(m_page);
}
diff --git a/src/plugins/studiowelcome/qdsnewdialog.cpp b/src/plugins/studiowelcome/qdsnewdialog.cpp
index 0324a7de93c..527204c80e8 100644
--- a/src/plugins/studiowelcome/qdsnewdialog.cpp
+++ b/src/plugins/studiowelcome/qdsnewdialog.cpp
@@ -76,22 +76,14 @@ QdsNewDialog::QdsNewDialog(QWidget *parent)
{
setParent(m_dialog);
- m_dialog->rootContext()->setContextProperties(QVector{
- {{"categoryModel"}, QVariant::fromValue(m_categoryModel.data())},
- {{"presetModel"}, QVariant::fromValue(m_presetModel.data())},
- {{"screenSizeModel"}, QVariant::fromValue(m_screenSizeModel.data())},
- {{"styleModel"}, QVariant::fromValue(m_styleModel.data())},
- {{"dialogBox"}, QVariant::fromValue(this)},
- });
-
m_dialog->setResizeMode(QQuickWidget::SizeRootObjectToView); // SizeViewToRootObject
m_dialog->engine()->addImageProvider(QStringLiteral("newprojectdialog_library"),
new Internal::NewProjectDialogImageProvider());
QmlDesigner::Theme::setupTheme(m_dialog->engine());
+ qmlRegisterSingletonInstance("BackendApi", 1, 0, "BackendApi", this);
m_dialog->engine()->addImportPath(Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources/imports").toString());
m_dialog->engine()->addImportPath(Core::ICore::resourcePath("qmldesigner/newprojectdialog/imports").toString());
- QString sourcesPath = qmlPath();
- m_dialog->setSource(QUrl::fromLocalFile(sourcesPath));
+ m_dialog->setSource(QUrl::fromLocalFile(qmlPath()));
m_dialog->setWindowModality(Qt::ApplicationModal);
m_dialog->setWindowFlags(Qt::Dialog);
@@ -114,7 +106,7 @@ QdsNewDialog::QdsNewDialog(QWidget *parent)
});
QObject::connect(m_styleModel.data(), &StyleModel::modelAboutToBeReset, this, [this]() {
- this->m_qmlStyleIndex = -1;
+ m_qmlStyleIndex = -1;
});
}
@@ -188,18 +180,47 @@ void QdsNewDialog::onWizardCreated(QStandardItemModel *screenSizeModel, QStandar
m_screenSizeModel->setBackendModel(screenSizeModel);
m_styleModel->setBackendModel(styleModel);
+ auto userPreset = m_currentPreset->asUserPreset();
+
if (m_qmlDetailsLoaded) {
- updateScreenSizes();
+ if (m_currentPreset->isUserPreset()) {
+ if (m_wizard.haveVirtualKeyboard())
+ setUseVirtualKeyboard(userPreset->useQtVirtualKeyboard);
+
+ if (m_wizard.haveTargetQtVersion()) {
+ int index = m_wizard.targetQtVersionIndex(userPreset->qtVersion);
+ if (index != -1)
+ setTargetQtVersionIndex(index);
+ }
+ } else {
+ if (m_wizard.haveTargetQtVersion()) {
+ int index = m_wizard.targetQtVersionIndex();
+ if (index != -1)
+ setTargetQtVersionIndex(index);
+ }
+ }
emit haveVirtualKeyboardChanged();
emit haveTargetQtVersionChanged();
+ updateScreenSizes();
+
setProjectName(m_qmlProjectName);
setProjectLocation(m_qmlProjectLocation.toString());
}
- if (m_qmlStylesLoaded)
+ if (m_qmlStylesLoaded && m_wizard.haveStyleModel()) {
+ if (m_currentPreset->isUserPreset()) {
+ int index = m_wizard.styleIndex(userPreset->styleName);
+ if (index != -1)
+ setStyleIndex(index);
+ } else {
+ /* NOTE: For a builtin preset, we don't need to set style index. That's because defaults
+ * will be loaded from the backend Wizard.
+ */
+ }
m_styleModel->reset();
+ }
}
QString QdsNewDialog::currentPresetQmlPath() const
@@ -221,10 +242,19 @@ int QdsNewDialog::screenSizeIndex() const
return m_wizard.screenSizeIndex();
}
-void QdsNewDialog::setTargetQtVersion(int index)
+void QdsNewDialog::setTargetQtVersionIndex(int index)
{
- m_wizard.setTargetQtVersionIndex(index);
- m_qmlTargetQtVersionIndex = index;
+ if (m_qmlTargetQtVersionIndex != index) {
+ m_wizard.setTargetQtVersionIndex(index);
+ m_qmlTargetQtVersionIndex = index;
+
+ emit targetQtVersionIndexChanged();
+ }
+}
+
+int QdsNewDialog::getTargetQtVersionIndex() const
+{
+ return m_qmlTargetQtVersionIndex;
}
void QdsNewDialog::setStyleIndex(int index)
@@ -267,6 +297,14 @@ int QdsNewDialog::getStyleIndex() const
return m_styleModel->actualIndex(m_qmlStyleIndex);
}
+void QdsNewDialog::setUseVirtualKeyboard(bool value)
+{
+ if (m_qmlUseVirtualKeyboard != value) {
+ m_qmlUseVirtualKeyboard = value;
+ emit useVirtualKeyboardChanged();
+ }
+}
+
void QdsNewDialog::setWizardFactories(QList factories_,
const Utils::FilePath &defaultLocation,
const QVariantMap &)
@@ -275,8 +313,9 @@ void QdsNewDialog::setWizardFactories(QList factories_,
WizardFactories factories{factories_, m_dialog, platform};
- std::vector recents = m_recentsStore.fetchAll();
- m_presetData.setData(factories.presetsGroupedByCategory(), recents);
+ std::vector recents = m_recentsStore.fetchAll();
+ std::vector userPresets = m_userPresetsStore.fetchAll();
+ m_presetData.setData(factories.presetsGroupedByCategory(), userPresets, recents);
m_categoryModel->reset();
m_presetModel->reset();
@@ -296,12 +335,50 @@ void QdsNewDialog::setWizardFactories(QList factories_,
m_qmlProjectLocation = Utils::FilePath::fromString(QDir::toNativeSeparators(projectLocation.toString()));
emit projectLocationChanged(); // So that QML knows to update the field
+ /* NOTE:
+ * Here we expect that details are loaded && that styles are loaded. We use the
+ * functionality below to update the state of the first item that is selected right when
+ * the dialog pops up. Otherwise, relying solely on onWizardCreated is not useful, since
+ * for the dialog popup, that wizard is created before details & styles are loaded.
+ *
+ * It might be a better alternative to receive notifications from QML in the cpp file, that
+ * style is loaded, and that details is loaded, and do updates from there. But, if we handle
+ * those events, they may be called before the wizard is created - so we would need to make
+ * sure that all events have occurred before we go ahead and configure the wizard.
+ */
+
+ auto userPreset = m_currentPreset->asUserPreset();
+
if (m_qmlDetailsLoaded) {
updateScreenSizes();
+
+ if (m_wizard.haveTargetQtVersion()) {
+ int index = (userPreset ? m_wizard.targetQtVersionIndex(userPreset->qtVersion)
+ : m_wizard.targetQtVersionIndex());
+ if (index != -1)
+ setTargetQtVersionIndex(index);
+ }
+
+ if (m_wizard.haveVirtualKeyboard() && userPreset)
+ setUseVirtualKeyboard(userPreset->useQtVirtualKeyboard);
+
+ emit haveVirtualKeyboardChanged();
+ emit haveTargetQtVersionChanged();
}
- if (m_qmlStylesLoaded)
+ if (m_qmlStylesLoaded && m_wizard.haveStyleModel()) {
+ if (userPreset) {
+ int index = m_wizard.styleIndex(userPreset->styleName);
+ if (index != -1)
+ setStyleIndex(index);
+ }
m_styleModel->reset();
+ }
+}
+
+QString QdsNewDialog::recentsTabName() const
+{
+ return PresetData::recentsTabName();
}
QString QdsNewDialog::qmlPath() const
@@ -338,10 +415,10 @@ void QdsNewDialog::accept()
.withTargetQtVersion(m_qmlTargetQtVersionIndex)
.execute();
- PresetItem item = m_wizard.preset();
+ std::shared_ptr item = m_wizard.preset();
QString customSizeName = m_qmlCustomWidth + " x " + m_qmlCustomHeight;
- m_recentsStore.add(item.categoryId, item.name, customSizeName);
+ m_recentsStore.add(item->categoryId, item->displayName(), customSizeName, item->isUserPreset());
m_dialog->close();
m_dialog->deleteLater();
@@ -355,6 +432,7 @@ void QdsNewDialog::reject()
m_wizard.destroyWizard();
m_dialog->close();
+ m_dialog = nullptr;
}
QString QdsNewDialog::chooseProjectLocation()
@@ -375,7 +453,76 @@ void QdsNewDialog::setSelectedPreset(int selection)
setProjectDescription(m_currentPreset->description);
m_presetPage = m_presetModel->page();
- m_wizard.reset(m_currentPreset.value(), m_qmlSelectedPreset);
+ m_wizard.reset(m_currentPreset, m_qmlSelectedPreset);
}
}
}
+
+void QdsNewDialog::savePresetDialogAccept()
+{
+ QString screenSize = m_qmlCustomWidth + " x " + m_qmlCustomHeight;
+ QString targetQtVersion = "";
+ QString styleName = "";
+ bool useVirtualKeyboard = false;
+
+ if (m_wizard.haveTargetQtVersion())
+ targetQtVersion = m_wizard.targetQtVersionName(m_qmlTargetQtVersionIndex);
+
+ if (m_wizard.haveStyleModel())
+ styleName = m_wizard.styleName(m_qmlStyleIndex);
+
+ if (m_wizard.haveVirtualKeyboard())
+ useVirtualKeyboard = m_qmlUseVirtualKeyboard;
+
+ UserPresetData preset = {m_currentPreset->categoryId,
+ m_currentPreset->wizardName,
+ m_qmlPresetName,
+ screenSize,
+ useVirtualKeyboard,
+ targetQtVersion,
+ styleName};
+
+ if (!m_userPresetsStore.save(preset)) {
+ QMessageBox::warning(m_dialog,
+ tr("Save Preset"),
+ tr("A preset with this name already exists."));
+ return;
+ }
+
+ // reload model
+ std::vector recents = m_recentsStore.fetchAll();
+ std::vector userPresets = m_userPresetsStore.fetchAll();
+ m_presetData.reload(userPresets, recents);
+
+ m_categoryModel->reset();
+
+ emit userPresetSaved();
+}
+
+void QdsNewDialog::removeCurrentPreset()
+{
+ if (!m_currentPreset->isUserPreset()) {
+ qWarning() << "Will not attempt to remove non-user preset";
+ return;
+ }
+
+ // remove preset & reload model
+ std::vector recents = m_recentsStore.remove(m_currentPreset->categoryId,
+ m_currentPreset->displayName());
+
+ auto userPreset = m_currentPreset->asUserPreset();
+ m_userPresetsStore.remove(userPreset->categoryId, userPreset->displayName());
+ std::vector userPresets = m_userPresetsStore.fetchAll();
+ m_presetData.reload(userPresets, recents);
+
+ m_qmlSelectedPreset = -1;
+ m_presetPage = -1;
+
+ if (userPresets.size() == 0) {
+ m_presetModel->setPage(0);
+ emit lastUserPresetRemoved();
+ }
+
+ m_categoryModel->reset();
+ m_presetModel->reset();
+}
diff --git a/src/plugins/studiowelcome/qdsnewdialog.h b/src/plugins/studiowelcome/qdsnewdialog.h
index a4afa69e927..670b39192d9 100644
--- a/src/plugins/studiowelcome/qdsnewdialog.h
+++ b/src/plugins/studiowelcome/qdsnewdialog.h
@@ -29,13 +29,13 @@
#include
#include
-#include
#include "wizardhandler.h"
#include "presetmodel.h"
#include "screensizemodel.h"
#include "stylemodel.h"
#include "recentpresets.h"
+#include "userpresets.h"
QT_BEGIN_NAMESPACE
class QStandardItemModel;
@@ -57,21 +57,31 @@ public:
Q_PROPERTY(bool useVirtualKeyboard MEMBER m_qmlUseVirtualKeyboard READ getUseVirtualKeyboard WRITE setUseVirtualKeyboard NOTIFY useVirtualKeyboardChanged)
Q_PROPERTY(bool haveVirtualKeyboard MEMBER m_qmlHaveVirtualKeyboard READ getHaveVirtualKeyboard NOTIFY haveVirtualKeyboardChanged)
Q_PROPERTY(bool haveTargetQtVersion MEMBER m_qmlHaveTargetQtVersion READ getHaveTargetQtVersion NOTIFY haveTargetQtVersionChanged)
+ Q_PROPERTY(int targetQtVersionIndex MEMBER m_qmlTargetQtVersionIndex READ getTargetQtVersionIndex WRITE setTargetQtVersionIndex NOTIFY targetQtVersionIndexChanged)
Q_PROPERTY(bool saveAsDefaultLocation MEMBER m_qmlSaveAsDefaultLocation WRITE setSaveAsDefaultLocation)
Q_PROPERTY(QString statusMessage MEMBER m_qmlStatusMessage READ getStatusMessage NOTIFY statusMessageChanged)
Q_PROPERTY(QString statusType MEMBER m_qmlStatusType READ getStatusType NOTIFY statusTypeChanged)
Q_PROPERTY(bool fieldsValid MEMBER m_qmlFieldsValid READ getFieldsValid NOTIFY fieldsValidChanged)
+ Q_PROPERTY(QString presetName MEMBER m_qmlPresetName)
Q_PROPERTY(bool detailsLoaded MEMBER m_qmlDetailsLoaded)
Q_PROPERTY(bool stylesLoaded MEMBER m_qmlStylesLoaded)
+ Q_INVOKABLE void removeCurrentPreset();
Q_INVOKABLE QString currentPresetQmlPath() const;
// TODO: screen size index should better be a property
Q_INVOKABLE void setScreenSizeIndex(int index); // called when ComboBox item is "activated"
Q_INVOKABLE int screenSizeIndex() const;
- Q_INVOKABLE void setTargetQtVersion(int index);
Q_INVOKABLE QString chooseProjectLocation();
+ Q_INVOKABLE QString recentsTabName() const;
+
+ Q_PROPERTY(QAbstractListModel *categoryModel MEMBER m_categoryModel CONSTANT);
+ Q_PROPERTY(QAbstractListModel *presetModel MEMBER m_presetModel CONSTANT);
+ Q_PROPERTY(QAbstractListModel *screenSizeModel MEMBER m_screenSizeModel CONSTANT);
+ Q_PROPERTY(QAbstractListModel *styleModel MEMBER m_styleModel CONSTANT);
+
+ /*********************/
explicit QdsNewDialog(QWidget *parent);
@@ -85,7 +95,11 @@ public:
void setStyleIndex(int index);
int getStyleIndex() const;
- void setUseVirtualKeyboard(bool value) { m_qmlUseVirtualKeyboard = value; }
+
+ void setTargetQtVersionIndex(int index);
+ int getTargetQtVersionIndex() const;
+
+ void setUseVirtualKeyboard(bool value);
bool getUseVirtualKeyboard() const { return m_qmlUseVirtualKeyboard; }
bool getFieldsValid() const { return m_qmlFieldsValid; }
@@ -101,6 +115,8 @@ public slots:
void accept();
void reject();
+ void savePresetDialogAccept();
+
signals:
void projectNameChanged();
void projectLocationChanged();
@@ -111,6 +127,9 @@ signals:
void statusMessageChanged();
void statusTypeChanged();
void fieldsValidChanged();
+ void targetQtVersionIndexChanged();
+ void userPresetSaved();
+ void lastUserPresetRemoved();
private slots:
void onStatusMessageChanged(Utils::InfoLabel::InfoType type, const QString &message);
@@ -160,6 +179,7 @@ private:
bool m_qmlFieldsValid = false;
QString m_qmlStatusMessage;
QString m_qmlStatusType;
+ QString m_qmlPresetName;
int m_presetPage = -1; // i.e. the page in the Presets View
@@ -169,10 +189,11 @@ private:
bool m_qmlDetailsLoaded = false;
bool m_qmlStylesLoaded = false;
- Utils::optional m_currentPreset;
+ std::shared_ptr m_currentPreset;
WizardHandler m_wizard;
RecentPresetsStore m_recentsStore;
+ UserPresetsStore m_userPresetsStore;
};
} //namespace StudioWelcome
diff --git a/src/plugins/studiowelcome/recentpresets.cpp b/src/plugins/studiowelcome/recentpresets.cpp
index f6e1335b479..cad2846d757 100644
--- a/src/plugins/studiowelcome/recentpresets.cpp
+++ b/src/plugins/studiowelcome/recentpresets.cpp
@@ -32,19 +32,27 @@
#include
#include
-using Core::ICore;
-using Utils::QtcSettings;
-
using namespace StudioWelcome;
constexpr char GROUP_NAME[] = "RecentPresets";
constexpr char WIZARDS[] = "Wizards";
-void RecentPresetsStore::add(const QString &categoryId, const QString &name, const QString &sizeName)
+void RecentPresetsStore::add(const QString &categoryId,
+ const QString &name,
+ const QString &sizeName,
+ bool isUserPreset)
{
- std::vector existing = fetchAll();
- QStringList encodedRecents = addRecentToExisting(RecentPreset{categoryId, name, sizeName},
- existing);
+ std::vector existing = fetchAll();
+
+ std::vector recents
+ = addRecentToExisting(RecentPresetData{categoryId, name, sizeName, isUserPreset}, existing);
+
+ save(recents);
+}
+
+void RecentPresetsStore::save(const std::vector &recents)
+{
+ QStringList encodedRecents = encodeRecentPresets(recents);
m_settings->beginGroup(GROUP_NAME);
m_settings->setValue(WIZARDS, encodedRecents);
@@ -52,8 +60,26 @@ void RecentPresetsStore::add(const QString &categoryId, const QString &name, con
m_settings->sync();
}
-QStringList RecentPresetsStore::addRecentToExisting(const RecentPreset &preset,
- std::vector &recents)
+std::vector RecentPresetsStore::remove(const QString &categoryId, const QString &presetName)
+{
+ std::vector recents = fetchAll();
+ size_t countBefore = recents.size();
+
+ /* NOTE: when removing one preset, it may happen that there are more than one recent for that
+ * preset. In that case, we need to remove all associated recents, for the preset.*/
+
+ Utils::erase(recents, [&](const RecentPresetData &p) {
+ return p.category == categoryId && p.presetName == presetName;
+ });
+
+ if (recents.size() < countBefore)
+ save(recents);
+
+ return recents;
+}
+
+std::vector RecentPresetsStore::addRecentToExisting(
+ const RecentPresetData &preset, std::vector &recents)
{
Utils::erase_one(recents, preset);
Utils::prepend(recents, preset);
@@ -61,48 +87,64 @@ QStringList RecentPresetsStore::addRecentToExisting(const RecentPreset &preset,
if (int(recents.size()) > m_max)
recents.pop_back();
- return encodeRecentPresets(recents);
+ return recents;
}
-std::vector RecentPresetsStore::fetchAll() const
+std::vector RecentPresetsStore::fetchAll() const
{
m_settings->beginGroup(GROUP_NAME);
QVariant value = m_settings->value(WIZARDS);
m_settings->endGroup();
- std::vector result;
+ std::vector result;
if (value.type() == QVariant::String)
result.push_back(decodeOneRecentPreset(value.toString()));
else if (value.type() == QVariant::StringList)
Utils::concat(result, decodeRecentPresets(value.toList()));
- const RecentPreset empty;
- return Utils::filtered(result, [&empty](const RecentPreset &recent) { return recent != empty; });
+ const RecentPresetData empty;
+ return Utils::filtered(result, [&empty](const RecentPresetData &recent) { return recent != empty; });
}
-QStringList RecentPresetsStore::encodeRecentPresets(const std::vector &recents)
+QStringList RecentPresetsStore::encodeRecentPresets(const std::vector &recents)
{
- return Utils::transform(recents, [](const RecentPreset &p) -> QString {
- return std::get<0>(p) + "/" + std::get<1>(p) + ":" + std::get<2>(p);
+ return Utils::transform(recents, [](const RecentPresetData &p) -> QString {
+ QString name = p.presetName;
+ if (p.isUserPreset)
+ name.prepend("[U]");
+
+ return p.category + "/" + name + ":" + p.sizeName;
});
}
-RecentPreset RecentPresetsStore::decodeOneRecentPreset(const QString &encoded)
+RecentPresetData RecentPresetsStore::decodeOneRecentPreset(const QString &encoded)
{
- QRegularExpression pattern{R"(^(\S+)/(.+):(\d+ x \d+))"};
+ QRegularExpression pattern{R"(^(\S+)/(.+):(\d+ x \d+)$)"};
auto m = pattern.match(encoded);
if (!m.hasMatch())
- return RecentPreset{};
+ return RecentPresetData{};
QString category = m.captured(1);
QString name = m.captured(2);
QString size = m.captured(3);
+ bool isUserPreset = name.startsWith("[U]");
+ if (isUserPreset)
+ name = name.split("[U]")[1];
- return std::make_tuple(category, name, size);
+ if (!QRegularExpression{R"(^\w[\w ]*$)"}.match(name).hasMatch())
+ return RecentPresetData{};
+
+ RecentPresetData result;
+ result.category = category;
+ result.presetName = name;
+ result.sizeName = size;
+ result.isUserPreset = isUserPreset;
+
+ return result;
}
-std::vector RecentPresetsStore::decodeRecentPresets(const QVariantList &values)
+std::vector RecentPresetsStore::decodeRecentPresets(const QVariantList &values)
{
return Utils::transform(values, [](const QVariant &value) {
return decodeOneRecentPreset(value.toString());
diff --git a/src/plugins/studiowelcome/recentpresets.h b/src/plugins/studiowelcome/recentpresets.h
index 3c223d8df55..83ab5fb8cc5 100644
--- a/src/plugins/studiowelcome/recentpresets.h
+++ b/src/plugins/studiowelcome/recentpresets.h
@@ -31,8 +31,43 @@
namespace StudioWelcome {
-// preset category, preset name, size name
-using RecentPreset = std::tuple;
+struct RecentPresetData
+{
+ RecentPresetData() = default;
+ RecentPresetData(const QString &category,
+ const QString &name,
+ const QString &size,
+ bool isUserPreset = false)
+ : category{category}
+ , presetName{name}
+ , sizeName{size}
+ , isUserPreset{isUserPreset}
+ {}
+
+ QString category;
+ QString presetName;
+ QString sizeName;
+ bool isUserPreset = false;
+};
+
+inline bool operator==(const RecentPresetData &lhs, const RecentPresetData &rhs)
+{
+ return lhs.category == rhs.category && lhs.presetName == rhs.presetName
+ && lhs.sizeName == rhs.sizeName && lhs.isUserPreset == rhs.isUserPreset;
+}
+
+inline bool operator!=(const RecentPresetData &lhs, const RecentPresetData &rhs)
+{
+ return !(lhs == rhs);
+}
+
+inline QDebug &operator<<(QDebug &d, const RecentPresetData &preset)
+{
+ d << "RecentPreset{category=" << preset.category << "; name=" << preset.presetName
+ << "; size=" << preset.sizeName << "; isUserPreset=" << preset.isUserPreset << "}";
+
+ return d;
+}
class RecentPresetsStore
{
@@ -42,14 +77,21 @@ public:
{}
void setMaximum(int n) { m_max = n; }
- void add(const QString &categoryId, const QString &name, const QString &sizeName);
- std::vector fetchAll() const;
+ void add(const QString &categoryId,
+ const QString &name,
+ const QString &sizeName,
+ bool isUserPreset = false);
+
+ std::vector remove(const QString &categoryId, const QString &presetName);
+ std::vector fetchAll() const;
private:
- QStringList addRecentToExisting(const RecentPreset &preset, std::vector &recents);
- static QStringList encodeRecentPresets(const std::vector &recents);
- static std::vector decodeRecentPresets(const QVariantList &values);
- static RecentPreset decodeOneRecentPreset(const QString &encoded);
+ std::vector addRecentToExisting(const RecentPresetData &preset,
+ std::vector &recents);
+ static QStringList encodeRecentPresets(const std::vector &recents);
+ static std::vector decodeRecentPresets(const QVariantList &values);
+ static RecentPresetData decodeOneRecentPreset(const QString &encoded);
+ void save(const std::vector &recents);
private:
QSettings *m_settings = nullptr;
diff --git a/src/plugins/studiowelcome/studiowelcome.qbs b/src/plugins/studiowelcome/studiowelcome.qbs
index cc655ea5a23..9648eda14d3 100644
--- a/src/plugins/studiowelcome/studiowelcome.qbs
+++ b/src/plugins/studiowelcome/studiowelcome.qbs
@@ -38,7 +38,9 @@ QtcPlugin {
"wizardhandler.cpp",
"wizardhandler.h",
"recentpresets.cpp",
- "recentpresets.h"
+ "recentpresets.h",
+ "userpresets.cpp",
+ "userpresets.h"
]
Group {
diff --git a/src/plugins/studiowelcome/studiowelcomeplugin.cpp b/src/plugins/studiowelcome/studiowelcomeplugin.cpp
index f292d32604c..d1436cc0430 100644
--- a/src/plugins/studiowelcome/studiowelcomeplugin.cpp
+++ b/src/plugins/studiowelcome/studiowelcomeplugin.cpp
@@ -793,7 +793,7 @@ StudioSettingsPage::StudioSettingsPage()
{
const QString toolTip = tr(
"Hide top-level menus with advanced functionality to simplify the UI. Build is "
- "generally not required in the context of Qt Design Studio.Debug and Analyze"
+ "generally not required in the context of Qt Design Studio. Debug and Analyze "
"are only required for debugging and profiling.");
QVBoxLayout *boxLayout = new QVBoxLayout();
diff --git a/src/plugins/studiowelcome/userpresets.cpp b/src/plugins/studiowelcome/userpresets.cpp
new file mode 100644
index 00000000000..d468c3522eb
--- /dev/null
+++ b/src/plugins/studiowelcome/userpresets.cpp
@@ -0,0 +1,125 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "userpresets.h"
+
+#include
+#include
+#include
+
+using namespace StudioWelcome;
+
+constexpr char PREFIX[] = "UserPresets";
+
+UserPresetsStore::UserPresetsStore()
+{
+ m_settings = std::make_unique(fullFilePath(), QSettings::IniFormat);
+}
+
+UserPresetsStore::UserPresetsStore(std::unique_ptr &&settings)
+ : m_settings{std::move(settings)}
+{}
+
+void UserPresetsStore::savePresets(const std::vector &presets)
+{
+ m_settings->beginWriteArray(PREFIX, static_cast(presets.size()));
+
+ for (size_t i = 0; i < presets.size(); ++i) {
+ m_settings->setArrayIndex(static_cast(i));
+ const auto &preset = presets[i];
+
+ m_settings->setValue("categoryId", preset.categoryId);
+ m_settings->setValue("wizardName", preset.wizardName);
+ m_settings->setValue("name", preset.name);
+ m_settings->setValue("screenSize", preset.screenSize);
+ m_settings->setValue("useQtVirtualKeyboard", preset.useQtVirtualKeyboard);
+ m_settings->setValue("qtVersion", preset.qtVersion);
+ m_settings->setValue("styleName", preset.styleName);
+ }
+ m_settings->endArray();
+ m_settings->sync();
+
+}
+
+bool UserPresetsStore::save(const UserPresetData &newPreset)
+{
+ QTC_ASSERT(newPreset.isValid(), return false);
+
+ std::vector presetItems = fetchAll();
+ if (Utils::anyOf(presetItems,
+ [&newPreset](const UserPresetData &p) { return p.name == newPreset.name; })) {
+ return false;
+ }
+
+ presetItems.push_back(newPreset);
+ savePresets(presetItems);
+
+ return true;
+}
+
+void UserPresetsStore::remove(const QString &category, const QString &name)
+{
+ std::vector presetItems = fetchAll();
+ auto item = Utils::take(presetItems, [&](const UserPresetData &p) {
+ return p.categoryId == category && p.name == name;
+ });
+
+ if (!item)
+ return;
+
+ savePresets(presetItems);
+}
+
+std::vector UserPresetsStore::fetchAll() const
+{
+ std::vector result;
+ int size = m_settings->beginReadArray(PREFIX);
+ if (size >= 0)
+ result.reserve(static_cast(size) + 1);
+
+ for (int i = 0; i < size; ++i) {
+ m_settings->setArrayIndex(i);
+
+ UserPresetData preset;
+ preset.categoryId = m_settings->value("categoryId").toString();
+ preset.wizardName = m_settings->value("wizardName").toString();
+ preset.name = m_settings->value("name").toString();
+ preset.screenSize = m_settings->value("screenSize").toString();
+ preset.useQtVirtualKeyboard = m_settings->value("useQtVirtualKeyboard").toBool();
+ preset.qtVersion = m_settings->value("qtVersion").toString();
+ preset.styleName = m_settings->value("styleName").toString();
+
+ if (preset.isValid())
+ result.push_back(std::move(preset));
+ }
+ m_settings->endArray();
+
+ return result;
+}
+
+QString UserPresetsStore::fullFilePath() const
+{
+ return Core::ICore::userResourcePath("UserPresets.ini").toString();
+}
diff --git a/src/plugins/studiowelcome/userpresets.h b/src/plugins/studiowelcome/userpresets.h
new file mode 100644
index 00000000000..4f6053ef260
--- /dev/null
+++ b/src/plugins/studiowelcome/userpresets.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include
+#include
+
+namespace StudioWelcome {
+
+struct UserPresetData
+{
+ QString categoryId;
+ QString wizardName;
+ QString name;
+ QString screenSize;
+
+ bool useQtVirtualKeyboard;
+ QString qtVersion;
+ QString styleName;
+
+ bool isValid() const
+ {
+ return !categoryId.isEmpty()
+ && !wizardName.isEmpty()
+ && !name.isEmpty();
+ }
+};
+
+inline QDebug &operator<<(QDebug &d, const UserPresetData &preset)
+{
+ d << "UserPreset{category = " << preset.categoryId;
+ d << "; wizardName = " << preset.wizardName;
+ d << "; name = " << preset.name;
+ d << "; screenSize = " << preset.screenSize;
+ d << "; keyboard = " << preset.useQtVirtualKeyboard;
+ d << "; qt = " << preset.qtVersion;
+ d << "; style = " << preset.styleName;
+ d << "}";
+
+ return d;
+}
+
+inline bool operator==(const UserPresetData &lhs, const UserPresetData &rhs)
+{
+ return lhs.categoryId == rhs.categoryId && lhs.wizardName == rhs.wizardName
+ && lhs.name == rhs.name && lhs.screenSize == rhs.screenSize
+ && lhs.useQtVirtualKeyboard == rhs.useQtVirtualKeyboard && lhs.qtVersion == rhs.qtVersion
+ && lhs.styleName == rhs.styleName;
+}
+
+class UserPresetsStore
+{
+public:
+ UserPresetsStore();
+ UserPresetsStore(std::unique_ptr &&settings);
+
+ bool save(const UserPresetData &preset);
+ void remove(const QString &category, const QString &name);
+ std::vector fetchAll() const;
+
+private:
+ QString fullFilePath() const;
+ void savePresets(const std::vector &presets);
+
+ std::unique_ptr m_settings;
+};
+
+} // namespace StudioWelcome
diff --git a/src/plugins/studiowelcome/wizardfactories.cpp b/src/plugins/studiowelcome/wizardfactories.cpp
index d374a5258c8..e271d5572e4 100644
--- a/src/plugins/studiowelcome/wizardfactories.cpp
+++ b/src/plugins/studiowelcome/wizardfactories.cpp
@@ -76,7 +76,7 @@ void WizardFactories::filter()
m_factories = acceptedFactories;
}
-PresetItem WizardFactories::makePresetItem(JsonWizardFactory *f, QWidget *parent,
+std::shared_ptr WizardFactories::makePresetItem(JsonWizardFactory *f, QWidget *parent,
const Utils::Id &platform)
{
using namespace std::placeholders;
@@ -89,16 +89,17 @@ PresetItem WizardFactories::makePresetItem(JsonWizardFactory *f, QWidget *parent
else
sizeName = screenSizes[index];
- return {
- /*.name =*/f->displayName(),
- /*.categoryId =*/f->category(),
- /*.screenSizeName=*/sizeName,
- /*.description =*/f->description(),
- /*.qmlPath =*/f->detailsPageQmlPath(),
- /*.fontIconCode =*/m_getIconUnicode(f->fontIconName()),
- /*.create =*/ std::bind(&JsonWizardFactory::runWizard, f, _1, parent, platform,
- QVariantMap(), false),
- };
+ auto result = std::make_shared();
+ result->wizardName = f->displayName();
+ result->categoryId = f->category();
+ result->screenSizeName=sizeName;
+ result->description = f->description();
+ result->qmlPath = f->detailsPageQmlPath();
+ result->fontIconCode = m_getIconUnicode(f->fontIconName());
+ result->create
+ = std::bind(&JsonWizardFactory::runWizard, f, _1, parent, platform, QVariantMap(), false);
+
+ return result;
}
std::map WizardFactories::makePresetItemsGroupedByCategory()
diff --git a/src/plugins/studiowelcome/wizardfactories.h b/src/plugins/studiowelcome/wizardfactories.h
index aa209362de6..c98fd5e1027 100644
--- a/src/plugins/studiowelcome/wizardfactories.h
+++ b/src/plugins/studiowelcome/wizardfactories.h
@@ -64,7 +64,7 @@ private:
void sortByCategoryAndId();
void filter();
- PresetItem makePresetItem(JsonWizardFactory *f, QWidget *parent, const Utils::Id &platform);
+ std::shared_ptr makePresetItem(JsonWizardFactory *f, QWidget *parent, const Utils::Id &platform);
std::map makePresetItemsGroupedByCategory();
private:
diff --git a/src/plugins/studiowelcome/wizardhandler.cpp b/src/plugins/studiowelcome/wizardhandler.cpp
index cd176707ac9..56c53fcad92 100644
--- a/src/plugins/studiowelcome/wizardhandler.cpp
+++ b/src/plugins/studiowelcome/wizardhandler.cpp
@@ -38,7 +38,7 @@
using namespace StudioWelcome;
-void WizardHandler::reset(const PresetItem &presetInfo, int presetSelection)
+void WizardHandler::reset(const std::shared_ptr &presetInfo, int presetSelection)
{
m_preset = presetInfo;
m_selectedPreset = presetSelection;
@@ -67,7 +67,7 @@ void WizardHandler::destroyWizard()
void WizardHandler::setupWizard()
{
- m_wizard = m_preset.create(m_projectLocation);
+ m_wizard = m_preset->create(m_projectLocation);
if (!m_wizard) {
emit wizardCreationFailed();
return;
@@ -142,6 +142,11 @@ QStandardItemModel *WizardHandler::getScreenFactorModel(ProjectExplorer::JsonFie
return cbfield->model();
}
+bool WizardHandler::haveStyleModel() const
+{
+ return m_wizard->hasField("ControlsStyle");
+}
+
QStandardItemModel *WizardHandler::getStyleModel(ProjectExplorer::JsonFieldPage *page)
{
auto *field = page->jsonField("ControlsStyle");
@@ -229,6 +234,47 @@ bool WizardHandler::haveTargetQtVersion() const
return m_wizard->hasField("TargetQtVersion");
}
+QString WizardHandler::targetQtVersionName(int index) const
+{
+ auto *field = m_detailsPage->jsonField("TargetQtVersion");
+ auto *cbfield = dynamic_cast