Merge remote-tracking branch 'origin/7.0'

Conflicts:
	src/plugins/webassembly/webassemblyrunconfiguration.cpp
	src/tools/processlauncher/launchersockethandler.cpp

Change-Id: Iab052af98013aa59282c16f22ae6e9ecb32f50c4
This commit is contained in:
Eike Ziller
2022-04-20 16:12:41 +02:00
99 changed files with 1785 additions and 875 deletions

View File

@@ -23,7 +23,7 @@ instructions:
maxTimeBetweenOutput: 360 maxTimeBetweenOutput: 360
userMessageOnFailure: "Failed to extract LLVM package, check logs." userMessageOnFailure: "Failed to extract LLVM package, check logs."
- type: ExecuteCommand - type: ExecuteCommand
command: "python -u {{.AgentWorkingDir}}/qt-creator/qt-creator/scripts/build.py --build-type {{.Env.QTC_BUILD_TYPE}} --src {{.AgentWorkingDir}}/qt-creator/qt-creator --build {{.AgentWorkingDir}}/qt-creator/qt-creator_build --qt-path {{.AgentWorkingDir}}/build/qt_install_dir --elfutils-path {{.AgentWorkingDir}}/build/qt_temp/elfutils --llvm-path {{.AgentWorkingDir}}/build/qt_temp/libclang --with-tests --no-zip --add-config=-DCMAKE_C_COMPILER_LAUNCHER=sccache --add-config=-DCMAKE_CXX_COMPILER_LAUNCHER=sccache" command: "python3 -u {{.AgentWorkingDir}}/qt-creator/qt-creator/scripts/build.py --build-type {{.Env.QTC_BUILD_TYPE}} --src {{.AgentWorkingDir}}/qt-creator/qt-creator --build {{.AgentWorkingDir}}/qt-creator/qt-creator_build --qt-path {{.AgentWorkingDir}}/build/qt_install_dir --elfutils-path {{.AgentWorkingDir}}/build/qt_temp/elfutils --llvm-path {{.AgentWorkingDir}}/build/qt_temp/libclang --with-tests --no-zip --add-config=-DCMAKE_C_COMPILER_LAUNCHER=sccache --add-config=-DCMAKE_CXX_COMPILER_LAUNCHER=sccache"
maxTimeInSeconds: 36000 maxTimeInSeconds: 36000
maxTimeBetweenOutput: 3600 maxTimeBetweenOutput: 3600
userMessageOnFailure: "Failed to run build.py, check logs." userMessageOnFailure: "Failed to run build.py, check logs."
@@ -50,7 +50,7 @@ instructions:
maxTimeBetweenOutput: 360 maxTimeBetweenOutput: 360
userMessageOnFailure: "Failed to extract LLVM package, check logs." userMessageOnFailure: "Failed to extract LLVM package, check logs."
- type: ExecuteCommand - type: ExecuteCommand
command: "python -u {{.AgentWorkingDir}}/qt-creator/qt-creator/scripts/build.py --build-type {{.Env.QTC_BUILD_TYPE}} --src {{.AgentWorkingDir}}/qt-creator/qt-creator --build {{.AgentWorkingDir}}/qt-creator/qt-creator_build --qt-path {{.AgentWorkingDir}}/build/qt_install_dir --llvm-path {{.AgentWorkingDir}}/build/qt_temp/libclang --keychain-unlock-script /Users/qt/unlock-keychain.sh --with-tests --no-zip --add-config=-DCMAKE_C_COMPILER_LAUNCHER=sccache --add-config=-DCMAKE_CXX_COMPILER_LAUNCHER=sccache" command: "python3 -u {{.AgentWorkingDir}}/qt-creator/qt-creator/scripts/build.py --build-type {{.Env.QTC_BUILD_TYPE}} --src {{.AgentWorkingDir}}/qt-creator/qt-creator --build {{.AgentWorkingDir}}/qt-creator/qt-creator_build --qt-path {{.AgentWorkingDir}}/build/qt_install_dir --llvm-path {{.AgentWorkingDir}}/build/qt_temp/libclang --keychain-unlock-script /Users/qt/unlock-keychain.sh --with-tests --no-zip --add-config=-DCMAKE_C_COMPILER_LAUNCHER=sccache --add-config=-DCMAKE_CXX_COMPILER_LAUNCHER=sccache"
maxTimeInSeconds: 36000 maxTimeInSeconds: 36000
maxTimeBetweenOutput: 3600 maxTimeBetweenOutput: 3600
userMessageOnFailure: "Failed to run build.py, check logs." userMessageOnFailure: "Failed to run build.py, check logs."

View File

@@ -96,7 +96,7 @@ instructions:
- type: PrependToEnvironmentVariable - type: PrependToEnvironmentVariable
variableName: PATH variableName: PATH
variableValue: "{{.InstallDir}}\\bin;" variableValue: "{{.Env.PYTHON3_PATH}};{{.Env.PIP3_PATH}};C:\\Utils\\gnuwin21\\bin;{{.InstallDir}}\\bin;"
enable_if: enable_if:
condition: property condition: property
property: target.os property: target.os

View File

@@ -32,7 +32,7 @@ instructions:
property: host.os property: host.os
in_values: [MacOS, Linux, Windows] in_values: [MacOS, Linux, Windows]
- type: ExecuteCommand - type: ExecuteCommand
command: "python -u {{.AgentWorkingDir}}/build/qtsdk/packaging-tools/install_qt.py --qt-path {{.AgentWorkingDir}}/build/qt_install_dir --temp-path {{.AgentWorkingDir}}/build/qt_temp --base-url {{.Env.QTC_QT_BASE_URL}} --base-url-postfix={{.Env.QTC_QT_POSTFIX}} --icu7z http://master.qt.io/development_releases/prebuilt/icu/prebuilt/56.1/icu-linux-g++-Rhel7.2-x64.7z {{.Env.QTC_QT_MODULES}}" command: "python3 -u {{.AgentWorkingDir}}/build/qtsdk/packaging-tools/install_qt.py --qt-path {{.AgentWorkingDir}}/build/qt_install_dir --temp-path {{.AgentWorkingDir}}/build/qt_temp --base-url {{.Env.QTC_QT_BASE_URL}} --base-url-postfix={{.Env.QTC_QT_POSTFIX}} --icu7z http://master.qt.io/development_releases/prebuilt/icu/prebuilt/56.1/icu-linux-g++-Rhel7.2-x64.7z {{.Env.QTC_QT_MODULES}}"
executeCommandArgumentSplitingBehavior: SplitAfterVariableSubstitution executeCommandArgumentSplitingBehavior: SplitAfterVariableSubstitution
maxTimeInSeconds: 3600 maxTimeInSeconds: 3600
maxTimeBetweenOutput: 360 maxTimeBetweenOutput: 360
@@ -42,7 +42,7 @@ instructions:
property: host.os property: host.os
equals_value: Linux equals_value: Linux
- type: ExecuteCommand - type: ExecuteCommand
command: "python -u {{.AgentWorkingDir}}/build/qtsdk/packaging-tools/install_qt.py --qt-path {{.AgentWorkingDir}}/build/qt_install_dir --temp-path {{.AgentWorkingDir}}/build/qt_temp --base-url {{.Env.QTC_QT_BASE_URL}} --base-url-postfix={{.Env.QTC_QT_POSTFIX}} {{.Env.QTC_QT_MODULES}}" command: "python3 -u {{.AgentWorkingDir}}/build/qtsdk/packaging-tools/install_qt.py --qt-path {{.AgentWorkingDir}}/build/qt_install_dir --temp-path {{.AgentWorkingDir}}/build/qt_temp --base-url {{.Env.QTC_QT_BASE_URL}} --base-url-postfix={{.Env.QTC_QT_POSTFIX}} {{.Env.QTC_QT_MODULES}}"
executeCommandArgumentSplitingBehavior: SplitAfterVariableSubstitution executeCommandArgumentSplitingBehavior: SplitAfterVariableSubstitution
maxTimeInSeconds: 3600 maxTimeInSeconds: 3600
maxTimeBetweenOutput: 360 maxTimeBetweenOutput: 360
@@ -52,7 +52,7 @@ instructions:
property: host.os property: host.os
equals_value: MacOS equals_value: MacOS
- type: ExecuteCommand - type: ExecuteCommand
command: "C:\\Python27\\Scripts\\pip.exe install pywin32" command: "pip.exe install pywin32"
maxTimeInSeconds: 1200 maxTimeInSeconds: 1200
maxTimeBetweenOutput: 120 maxTimeBetweenOutput: 120
userMessageOnFailure: "Failed to install win32api, check logs." userMessageOnFailure: "Failed to install win32api, check logs."

View File

@@ -452,11 +452,10 @@
The Performance Analyzer can read Perf data files generated in either frame The Performance Analyzer can read Perf data files generated in either frame
pointer or dwarf mode. However, to generate the files correctly, numerous pointer or dwarf mode. However, to generate the files correctly, numerous
preconditions have to be met. All system images for the preconditions have to be met. All system images for the
\l{http://doc.qt.io/QtForDeviceCreation/qtee-supported-platforms.html} \l{https://doc.qt.io/Boot2Qt/qtdc-supported-platforms.html}
{Qt for Device Creation reference devices}, except for Freescale iMX53 Quick {Boot2Qt:Supported Target Devices and Development Hosts} are correctly set
Start Board and SILICA Architect Tibidabo, are correctly set up for up for profiling in the dwarf mode. For other devices, check whether Perf
profiling in the dwarf mode. For other devices, check whether Perf can read can read back its own data in a sensible way by checking the output of
back its own data in a sensible way by checking the output of
\c {perf report} or \c {perf script} for the recorded Perf data files. \c {perf report} or \c {perf script} for the recorded Perf data files.
\section1 Loading and Saving Trace Files \section1 Loading and Saving Trace Files

View File

@@ -157,3 +157,15 @@
\externalpage https://microsoft.github.io/language-server-protocol/ \externalpage https://microsoft.github.io/language-server-protocol/
\title Language Server Protocol \title Language Server Protocol
*/ */
/*!
\externalpage https://docs.microsoft.com/en-us/java/openjdk/download
\title Download OpenJDK
*/
/*!
\externalpage https://developer.android.com/studio
\title Download Android Studio
*/
/*!
\externalpage https://developer.android.com/studio/install
\title Android Studio Installation Guide
*/

View File

@@ -63,8 +63,8 @@
\note On Ubuntu Linux, the development user account must have access to \note On Ubuntu Linux, the development user account must have access to
plugged in devices. To allow the development user access to the device plugged in devices. To allow the development user access to the device
via USB, create a new \c udev rule, as described in via USB, create a new \c udev rule, as described in
\l{https://doc.qt.io/QtForDeviceCreation/b2qt-requirements-x11.html#setting-up-usb-access-to-embedded-devices} \l{https://doc.qt.io/Boot2Qt/b2qt-requirements-x11.html#setting-up-usb-access-to-embedded-devices}
{Setting Up USB Access to Embedded Devices}. {Boot2Qt: Setting Up USB Access to Embedded Devices}.
You can edit the settings later in \uicontrol Tools > \uicontrol Options > You can edit the settings later in \uicontrol Tools > \uicontrol Options >
\uicontrol Devices > \uicontrol Devices. \uicontrol Devices > \uicontrol Devices.

View File

@@ -65,24 +65,22 @@
license holders, tooling is provided to customize the contents of the stack license holders, tooling is provided to customize the contents of the stack
as well as to take it into desired production hardware. as well as to take it into desired production hardware.
Either Windows 7 or later or Ubuntu Linux 64-bit 12.04 LTS Either Windows 10 64-bit or later or Ubuntu Linux 64-bit 20.04 LTS
or later is required to install and use Boot2Qt. or later is required to install and use Boot2Qt.
The following topics contain more information about developing applications The following topics contain more information about developing applications
for Boot2Qt devices: for Boot2Qt devices:
\list \list
\li \l{https://doc.qt.io/QtForDeviceCreation/qtee-supported-platforms.html} \li \l{https://doc.qt.io/Boot2Qt/qtdc-supported-platforms.html}
{Reference Target Devices and Development Hosts} {Boot2Qt: Supported Target Devices and Development Hosts}
\li \l{https://doc.qt.io/QtForDeviceCreation/b2qt-installation-guides.html} \li \l{https://doc.qt.io/Boot2Qt/b2qt-installation-guides.html}
{Installation Guides} {Boot2Qt: Installation Guides}
\li \l{Connecting Boot2Qt Devices} \li \l{Connecting Boot2Qt Devices}
\li \l{Specifying Run Settings for Boot2Qt Devices} \li \l{Specifying Run Settings for Boot2Qt Devices}
\li \l{Deploying Applications to Boot2Qt Devices} \li \l{Deploying Applications to Boot2Qt Devices}
\li \l{https://doc.qt.io/qtcreator/creator-overview-qtasam.html} \li \l{https://doc.qt.io/qtcreator/creator-overview-qtasam.html}
{Qt Creator Plugin for Qt Application Manager} {Qt Creator Plugin for Qt Application Manager}
\li \l{https://doc.qt.io/QtForDeviceCreation/index.html}
{Qt for Device Creation}
\endlist \endlist
\section1 Generic Remote Linux \section1 Generic Remote Linux

View File

@@ -32,10 +32,8 @@
must create connections from the development host to the device and add the must create connections from the development host to the device and add the
device configurations to \l{glossary-buildandrun-kit}{kits}. Select device configurations to \l{glossary-buildandrun-kit}{kits}. Select
\uicontrol {Manage Kits} to add devices to kits. For more information, see \uicontrol {Manage Kits} to add devices to kits. For more information, see
the \l{http://doc.qt.io/QtForDeviceCreation/qtee-installation-guide.html} \l{http://doc.qt.io/Boot2Qt/b2qt-installation-guides.html}
{Installation Guide} in the {Boot2Qt: Installation Guide}.
\l{http://doc.qt.io/QtForDeviceCreation/index.html}{Qt for Device Creation}
documentation.
The run settings display the path to the executable file on the development The run settings display the path to the executable file on the development
host and on the device. host and on the device.

View File

@@ -40,9 +40,8 @@
\l{http://qt.io/licensing/}{Qt license}: \l{http://qt.io/licensing/}{Qt license}:
\list \list
\li \l{http://doc.qt.io/QtForDeviceCreation/index.html}{Developing for \li \l{https://doc.qt.io/Boot2Qt/index.html}{Boot2Qt}
embedded devices} \li \l{https://doc.qt.io/qtcreator/creator-overview-qtasam.html}
\li \l{http://doc.qt.io/qtcreator/creator-overview-qtasam.html}
{Qt Application Manager} integration {Qt Application Manager} integration
\endlist \endlist
*/ */

View File

@@ -44,12 +44,10 @@
\l{Connecting Android Devices} and \l{Connecting iOS Devices}. \l{Connecting Android Devices} and \l{Connecting iOS Devices}.
To run an example application on a Boot2Qt device, you must set up To run an example application on a Boot2Qt device, you must set up
Qt for Device Creation on the development host and create connections Boot2Qt on the development host and create connections
between the host and devices. For more information, see between the host and devices. For more information, see
\l{https://doc.qt.io/QtForDeviceCreation/b2qt-installation-guides.html} \l{https://doc.qt.io/Boot2Qt/b2qt-installation-guides.html}
{Installation Guides} in the {Boot2Qt: Installation Guides}
\l{http://doc.qt.io/QtForDeviceCreation/index.html}{Qt for Device Creation}
documentation.
If you have \l{Qt Design Studio Manual}{\QDS} installed, you can open If you have \l{Qt Design Studio Manual}{\QDS} installed, you can open
\QDS examples from \QC in \QDS. \QDS examples from \QC in \QDS.

View File

@@ -49,7 +49,7 @@
\list \list
\li \l{Connecting Android Devices}{Android Device} \li \l{Connecting Android Devices}{Android Device}
\li \l{Connecting Bare Metal Devices}{Bare Metal Device} \li \l{Connecting Bare Metal Devices}{Bare Metal Device}
\li \l{https://doc.qt.io/QtForDeviceCreation/b2qt-installation-guides.html} \li \l{https://doc.qt.io/Boot2Qt/b2qt-installation-guides.html}
{Boot2Qt Device} (commercial only) {Boot2Qt Device} (commercial only)
\li \l{Emulator}{Boot2Qt Emulator Device} (commercial only) \li \l{Emulator}{Boot2Qt Emulator Device} (commercial only)
\li \l{Connecting Generic Remote Linux Devices}{Generic Remote Linux Device} \li \l{Connecting Generic Remote Linux Devices}{Generic Remote Linux Device}

View File

@@ -24,11 +24,12 @@
****************************************************************************/ ****************************************************************************/
/*! /*!
\previouspage creator-live-preview-devices.html
\page qt-design-viewer.html \page qt-design-viewer.html
\if defined(qtdesignstudio) \if defined(qtdesignstudio)
\previouspage creator-live-preview-android.html
\nextpage studio-exporting-and-importing.html \nextpage studio-exporting-and-importing.html
\else \else
\previouspage creator-live-preview-devices.html
\nextpage creator-building-targets.html \nextpage creator-building-targets.html
\endif \endif

View File

@@ -26,8 +26,11 @@
/*! /*!
\previouspage creator-live-preview-desktop.html \previouspage creator-live-preview-desktop.html
\page creator-live-preview-devices.html \page creator-live-preview-devices.html
\if defined(qtdesignstudio)
\nextpage creator-live-preview-android.html
\else
\nextpage qt-design-viewer.html \nextpage qt-design-viewer.html
\endif
\title Previewing on Devices \title Previewing on Devices
To preview UIs on Android devices, you need to enable USB debugging on them To preview UIs on Android devices, you need to enable USB debugging on them
@@ -78,19 +81,17 @@
\section2 Previewing on Boot2Qt Devices \section2 Previewing on Boot2Qt Devices
You can preview UIs on Boot2Qt devices that are supported by You can preview UIs on Boot2Qt devices. For a list of supported devices, see
\l{Qt for Device Creation}. For a list of supported devices, see \l{https://doc.qt.io/Boot2Qt/qtdc-supported-platforms.html}
\l{https://doc.qt.io/QtForDeviceCreation/qtdc-supported-platforms.html} {Boot2Qt: Supported Target Devices and Development Hosts}.
{Reference Target Devices and Development Hosts}.
You must configure the device as instructed in the You must configure the device as instructed in the
\l{https://doc.qt.io/QtForDeviceCreation/b2qt-installation-guides.html} \l{https://doc.qt.io/Boot2Qt/b2qt-installation-guides.html}
{Installation Guides}. {Boot2Qt: Installation Guides}.
\note At the time of this writing, \macos is not supported as a development \note At the time of this writing, \macos is not supported as a development
host for Qt for Device Creation. This means that you cannot preview UIs on host for Boot2Qt. This means that you cannot preview UIs on
devices if you are using \QC on \macos. For more information about devices if you are using \QC on \macos. For more information, see
supported development hosts, see \l {https://doc.qt.io/Boot2Qt/qtdc-supported-platforms.html#supported-development-hosts}
\l {https://doc.qt.io/QtForDeviceCreation/qtdc-supported-platforms.html#supported-development-hosts} {Boot2Qt: Supported Development Hosts}.
{Supported Development Hosts}.
*/ */

View File

@@ -72,6 +72,14 @@
devices is set up automatically. You only need to connect your devices is set up automatically. You only need to connect your
devices to your system. devices to your system.
\endif \endif
\if defined(qtdesignstudio)
\li \l{Previewing Android applications}
You can preview Android applications live using an Android
emulator.
\endif
\li \l{Previewing in Browsers} \li \l{Previewing in Browsers}
You can open \l{https://qt-webassembly.io/designviewer/}{\QDV} You can open \l{https://qt-webassembly.io/designviewer/}{\QDV}

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

@@ -0,0 +1,200 @@
/****************************************************************************
**
** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Design Studio documentation.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Free Documentation License Usage
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of
** this file. Please review the following information to ensure
** the GNU Free Documentation License version 1.3 requirements
** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
**
****************************************************************************/
/*!
\previouspage creator-live-preview-devices.html
\page creator-live-preview-android.html
\nextpage qt-design-viewer.html
\title Previewing Android Applications
In \QDS, you can preview Android applications live using an Android emulator.
\section1 Prerequisites
\section2 Install OpenJDK 11
You need to install OpenJDK 11 as described in \l{Getting Started with Qt for Android},
to do this:
\list
\li On Linux:
\list 1
\li In the command line, run:
\code
sudo apt-get install openjdk-11-jdk
\endcode
\endlist
\li On macOS:
\list 1
\li Download OpenJDK 11 from \l{Download OpenJDK}.
\li In the command line, run:
\code
cd ~/Downloads
tar xf microsoft-jdk-11.0.13.8.1-macos-x64.tar.gz
\endcode
\li Copy the unzipped folder to a location where macOS searches for Java by default:
\code
sudo cp -Rv jdk-11.0.13+8 /Library/Java/JavaVirtualMachines/
\endcode
\li Check if Java was correctly installed:
\code
java -version
\endcode
The Java installation is correct if the command returns something like:
\code
openjdk version "11.0.13" 2021-10-19 LTS
OpenJDK Runtime Environment Microsoft-27990 (build 11.0.13+8-LTS)
OpenJDK 64-Bit Server VM Microsoft-27990 (build 11.0.13+8-LTS, mixed mode)
\endcode
\endlist
\li On Windows:
\list
\li OpenJDK 11 is automatically installed with Android Studio.
\endlist
\endlist
\section2 Install Android Studio and SDK Tools
You need to install Android Studio:
\list 1
\li Download Android Studio from \l{Download Android Studio}.
\li Install Android Studio according to the
\l{Android Studio Installation Guide}.
\endlist
Next, you need to install Android SDK command-line tools:
\list 1
\li Run Android Studio and on the welcome page, select \uicontrol{More Actions} >
\uicontrol{SDK Manager}.
\image android-studio-sdk-manager.png
\li Select \uicontrol{Android SDK Build-Tools 32-rc1}, \uicontrol{NDK (Side by side)}, and
\uicontrol{Android SDK Command-line Tools (latest)}.
\image android-studio-sdk-tools.png
\li Select \uicontrol{Apply} and follow the instructions in the wizard to finalize the
installation.
\endlist
\section2 Install Android SDK Packages in \QDS
You need to install Android SDK packages in \QDS:
\list 1
\li Run \QDS.
\li Go to \uicontrol Tools > \uicontrol Options > \uicontrol{Devices}.
\li Select \uicontrol Yes on the \uicontrol{Missing Android SDK Packages} dialog.
\image qtds-options-dialog-missing-packages.png
\li Select \uicontrol OK on the \uicontrol{Android SDK Changes} dialog.
\image qtds-android-sdk-changes-dialog.png
\li Select \uicontrol Yes on the \uicontrol{Android SDK Licenses} dialog.
\image qtds-android-sdk-licenses-dialog.png
\endlist
\note The installation can take a while. If the installation process seems to
have stopped working, try to restart \QDS and run the installation again.
After completing these steps, you should no longer have any errors on the
\uicontrol Tools > \uicontrol Options > \uicontrol Devices page.
\image qtds-options-accept-licenses.png
\section2 Create Android Virtual Devices
Next, you need to create an Android Virtual Device (AVD):
\note You might need to download a system image depending on your setup.
\list 1
\li Run Android Studio and on the welcome page, select \uicontrol{More Actions} >
\uicontrol{AVD Manager}.
\image android-studio-avd-manager.png
\li Select \uicontrol{Create Virtual Device} and follow the instructions in the wizard to
finalize the creation.
\endlist
\QDS has a AVD manager where you can create AVDs as well but it is recommended to use Android
Studio because then you can directly install the needed system package for the selected device
configuration.
To create an AVD in \QDS:
\list 1
\li Go to \uicontrol Tools > \uicontrol Options.
\li On the \uicontrol Devices tab, select \uicontrol Add and follow the wizard to finalize
the creation. If there is no entry for \e{Android Device} in the
\uicontrol{Available device types} list, try restarting \QDS.
\endlist
\note Many device images require Intel HAXM to work on Windows 10 and later, you can
download and install the drivers from
\l{https://github.com/intel/haxm/wiki/Installation-Instructions-on-Windows}{here}.
\image qtds-options-devices.png
\section2 Set the AVD as the Device in the Android Kit
Next, you need to set the AVD as the Android device kit. You do this under the the
\uicontrol Kits tab. If the \uicontrol Kits list is empty, restart \QDS.
\image qtds-options-kits.png
\section1 Create a Project and Run the Emulator
Now, you are set up and can create a project in \QDS. In the project, configure it to run on the
Android device:
\list 1
\li Select the \uicontrol Projects mode tab.
\li Under \uicontrol{Build & Run}, select the Android device.
\endlist
\image qtds-run-settings.png
Next, to run the emulator, do one of the following:
\list
\li Select \uicontrol{Show Live Preview} in the \uicontrol{Form Editor} toolbar.
\image toolbar-show-live-preview.png
\li Select \uicontrol Build > \uicontrol{QML Preview}.
\note The \uicontrol Build menu option is not visible by default. To show
it, go to \uicontrol Tools > \uicontrol Options > \uicontrol Environment
> \uicontrol {Qt Design Studio Configuration}.
\image menu-build-qml-preview.png
\endlist
Now the emulator runs, the qtdesignviewer APK delivered with the \QDS installation
is uploaded, and the project is uploaded and shown in the emulator.
\image qtds-running-emulator.png
Note the following:
\list
\li The qtdesignviewer for Android currently has no live preview. You have to restart
the preview to see updates.
\li Android typically has very high DPI and it is good to familiarize yourself with how
\l{https://doc-snapshots.qt.io/qt6-dev/highdpi.html}{high DPI works in Qt 6}. You can,
for example, use QT_SCALE_FACTOR or QT_USE_PHYSICAL_DPI. You can define those in the
\e .qmlproject file.
\li The qtdesignviewer for Android is currently built with Qt 6.2 and comes with all
QML modules shipped with \QDS 2.3.
\endlist
*/

View File

@@ -97,6 +97,25 @@
handle, such as gradient fill colors or a mixed radius, the frames are handle, such as gradient fill colors or a mixed radius, the frames are
exported as images. exported as images.
\section2 Using Variants
Figma variants are exported as a component with states. All variants
inside a \e component-set are merged together and the differences across
the variants are translated into states.
For an optimal output, follow these guidelines:
\list
\li \QBF panel is disabled for variants. Before adding
a variant to a component, the \QBF settings for the component
should be complete.
\li Do not change the layer names across the variants. The \l ID of
a layer is derived from the layer name which in turn is used
to identify the property differences for the state generation, so
it is essential to keep the layer names same across variants.
\li Adding and removing layers across the variants is fine and
encouraged to create the variant differences.
\endlist
\section1 Exporting Designs \section1 Exporting Designs
\image qt-figma-bridge.png "Qt Bridge for Figma" \image qt-figma-bridge.png "Qt Bridge for Figma"

View File

@@ -67,7 +67,7 @@
\section1 Embedding Resources into Applications \section1 Embedding Resources into Applications
Alternatively, you can embedd the resources into your application by Alternatively, you can embedd the resources into your application by
selecting \uicontrol Build > \uicontrol {Generate RCC Resource File}. selecting \uicontrol Build > \uicontrol {Generate Deployable Package}.
Select the location for the .qmlrc file, and then select the files to Select the location for the .qmlrc file, and then select the files to
embedd in the \uicontrol {Add Resources} dialog. embedd in the \uicontrol {Add Resources} dialog.

View File

@@ -151,6 +151,7 @@
\list \list
\li \l{Previewing on Desktop} \li \l{Previewing on Desktop}
\li \l{Previewing on Devices} \li \l{Previewing on Devices}
\li \l{Previewing Android Applications}
\li \l{Previewing in Browsers} \li \l{Previewing in Browsers}
\endlist \endlist
\li \l {Asset Creation with Other Tools} \li \l {Asset Creation with Other Tools}

View File

@@ -31,7 +31,7 @@
\nextpage studio-3d-particle-system.html \nextpage studio-3d-particle-system.html
\title Particles \title Particles
A \e {particle system} enables you to use sprites, 3D models, or images to With a \e {particle system} you can use sprites, 3D models, or images to
create effects that are hard to reproduce with conventional rendering create effects that are hard to reproduce with conventional rendering
techniques. This includes chaotic systems, natural phenomena, or processes techniques. This includes chaotic systems, natural phenomena, or processes
caused by chemical reactions. For example, you can simulate fire, smoke, caused by chemical reactions. For example, you can simulate fire, smoke,
@@ -47,15 +47,16 @@
\li \l {Particle Directions} \li \l {Particle Directions}
\endlist \endlist
\section1 Adding a Particle System Preset particle \l{Particle Components}{components},
\l{Particle Templates}{templates}, and \l{Particle Effects}{effects} are
Preset particle components are available in available in \uicontrol Components > \uicontrol {QtQuick3D Particles3D}
\uicontrol Components > \uicontrol {Qt Quick 3D Particles 3D}
after you add the \uicontrol {QtQuick3D.Particles3D} module to after you add the \uicontrol {QtQuick3D.Particles3D} module to
your project, as instructed in \l{Adding and Removing Modules}. your project, as instructed in \l{Adding and Removing Modules}.
\image studio-3d-particles.png "3D Particles" \image studio-3d-particles.png "3D Particles"
\section1 Particle Components
When you add an instance of the \uicontrol {Particle System} component to a When you add an instance of the \uicontrol {Particle System} component to a
scene, \QDS automatically adds instances of the \uicontrol {Sprite Particle}, scene, \QDS automatically adds instances of the \uicontrol {Sprite Particle},
\uicontrol Emitter, and \uicontrol {Vector Direction} components for you. \uicontrol Emitter, and \uicontrol {Vector Direction} components for you.
@@ -82,53 +83,6 @@
to simulate flying objects that follow wavy curves, or an instance of the to simulate flying objects that follow wavy curves, or an instance of the
\uicontrol {Point Rotator} to simulate windy weather. \uicontrol {Point Rotator} to simulate windy weather.
To add a particle system that emits sprite particles:
\list 1
\li Select \uicontrol Assets > \inlineimage icons/plus.png
to add your sprites, 3D models, textures, and other graphical
\l{Assets}{assets} to the project.
\li Drag-and-drop an instance of the \uicontrol {Particle System}
component from \uicontrol Components to a scene component instance
in \l Navigator.
\li Drag-and-drop the sprite image from \uicontrol Assets to the sprite
particle instance in \uicontrol Navigator.
\endlist
Add instances of other components according to your use case.
\section1 Performance Considerations
The particles are designed to be usable on a variety of hardware on
desktops, as well as mobile and embedded devices. However, in addition
to rendering the maximum amount of particle elements on the screen,
extensibility to different use-cases, rendering quality, integration
with the other UI elements, are also important.
Currently, the rendering runs on GPU, while the particle system logic
runs on CPU. However, the \e {stateless particle system} enables you
to move the system logic onto GPU if that seems beneficial. The initial
measurements indicate that the system is quite well balanced between
CPU and GPU. The stateless system also enables animating particles by
using a \l{Timeline}{timeline}. The model particles use instanced rendering
to boost the performance. Therefore, OpenGL ES 2.0 isn't sufficient to make
rendering performant, and at least OpenGL ES 3.0, Vulkan, or some other
modern backend is required.
To get a more concrete view on the actual performance, the video below shows
a particles Testbed application running on four different Android devices.
These devices and their chipsets and GPUs could be considered to be
lower-end to mid-range, confirming that the particles can perform well also
on affordable hardware.
\youtube 9MqUCP6JLCQ
\section1 Summary of 3D Particles
\note The \uicontrol {Particles 3D} components are released as a tech
preview feature in \QDS 2.2, and their functionality will be improved
in future releases.
The following table lists preset particle components. The following table lists preset particle components.
\table \table
@@ -207,6 +161,190 @@
\li Applies random wave curves to particles. \li Applies random wave curves to particles.
\endtable \endtable
\section1 Particle Templates
A particle template is a preset of particle components that you can use to
create specific particle effects in a convenient way.
The following table lists particle templates and their components.
\table
\header
\li Template
\li Components
\row
\li Animated Sprite
\li
Particle System
\list
\li Particle Emitter
\list
\li Sprite Particle
\li Sprite Sequence
\li Texture
\endlist
\li Vector Direction
\endlist
\row
\li Attractor
\li
Particle System
\list
\li Particle Emitter
\list
\li Sprite Particle
\li Vector Direction
\endlist
\li Particle Attractor
\endlist
\row
\li Burst
\li
Particle System
\list
\li Particle Emitter
\list
\li Sprite Particle
\li Vector Direction
\li Emit Burst
\endlist
\endlist
\row
\li Model Blend
\li
Particle System
\list
\li Particle Emitter
\list
\li Node
\li Model Blend Particle
\li Particle Emitter
\list
\li Vector Direction
\endlist
\endlist
\endlist
\row
\li Model Shape
\li
Particle System
\list
\li Particle Emitter
\list
\li Sprite Particle
\list
\li Vector Direction
\endlist
\endlist
\li Particle Model Shape
\endlist
\row
\li Particle Trail
\li
Particle System
\list
\li Trail Emitter
\list
\li Vector Direction
\li Sprite Particle
\endlist
\li Particle Emitter
\list
\li Vector Direction
\li Sprite Particle
\endlist
\endlist
\row
\li Sprite
\li
Particle System
\list
\li Sprite Emitter
\list
\li Sprite Particle
\li Vector Direction
\endlist
\endlist
\row
\li Wander
\li
Particle System
\list
\li Sprite Emitter
\list
\li Sprite Particle
\list
\li Texture
\endlist
\li Wander
\li Node
\endlist
\endlist
\endtable
\section1 Particle Effects
A particle effect is a ready-made effect that you can use to create, for
example, fire, rain, or mist in a convenient way.
The following particle effects are available:
\list
\li Clouds
\li Dust
\li Exhaust
\li Fire
\li Heavy Rain
\li Heavy Tire Spray
\li Light Rain
\li Light Tire Spray
\li Rain Mist
\li Snow
\li Steam
\endlist
\section1 Adding a Particle System
The recommended way to add a particle system is to use one of the
\l{Particle Templates}{particle templates} and then add or remove
particle components according to your use case.
For example, to add a particle system that emits sprite particles:
\list
\li From \uicontrol {Qt Quick 3D Particles System Templates} in
\uicontrol {Components}, drag \uicontrol Sprite to a scene component in
\uicontrol Navigator. You can also drag it to \uicontrol {3D Editor}.
\endlist
\image studio-3d-particles-sprite-template.png
\section1 Performance Considerations
The particles are designed to be usable on a variety of hardware on
desktops, as well as mobile and embedded devices. However, in addition
to rendering the maximum amount of particle elements on the screen,
extensibility to different use-cases, rendering quality, integration
with the other UI elements, are also important.
Currently, the rendering runs on GPU, while the particle system logic
runs on CPU. However, the \e {stateless particle system} enables you
to move the system logic onto GPU if that seems beneficial. The initial
measurements indicate that the system is quite well balanced between
CPU and GPU. The stateless system also enables animating particles by
using a \l{Timeline}{timeline}. The model particles use instanced rendering
to boost the performance. Therefore, OpenGL ES 2.0 isn't sufficient to make
rendering performant, and at least OpenGL ES 3.0, Vulkan, or some other
modern backend is required.
To get a more concrete view on the actual performance, the video below shows
a particles Testbed application running on four different Android devices.
These devices and their chipsets and GPUs could be considered to be
lower-end to mid-range, confirming that the particles can perform well also
on affordable hardware.
\youtube 9MqUCP6JLCQ
\section1 Particle System Tutorials \section1 Particle System Tutorials
\list \list

View File

@@ -67,41 +67,6 @@ if [ -d "$assetimporterSrcDir" ]; then
fi fi
fi fi
# collect tls plugins to have ssl download feature available
tlsDestDir="$app_path/Contents/PlugIns/tls"
tlssrcDir="$plugin_src/tls"
if [ -d "$tlssrcDir" ]; then
if [ ! -d "$tlsDestDir" ]; then
echo "- Copying tls plugins to have ssl download feature available"
mkdir -p "$tlsDestDir"
find "$tlssrcDir" -iname "*.dylib" -maxdepth 1 -exec cp {} "$tlsDestDir"/ \;
fi
fi
# workaround for Qt 6.2:
# - QTBUG-94796 macdeployqt does not deploy /Contents/PlugIns/sqldrivers/libqsqlite.dylib anymore
sqldriversDestDir="$app_path/Contents/PlugIns/sqldrivers"
sqldriversSrcDir="$plugin_src/sqldrivers"
if [ -d "$sqldriversSrcDir" ]; then
if [ ! -d "$sqldriversDestDir" ]; then
echo "- Copying sqlitedriver plugin"
mkdir -p "$sqldriversDestDir"
cp "$sqldriversSrcDir/libqsqlite.dylib" "$sqldriversDestDir/libqsqlite.dylib"
fi
fi
# workaround for Qt 6.2:
# - QTBUG-94796 macdeployqt does not deploy /Contents/PlugIns/imageformats/libqsvg.dylib anymore
imageformatsDestDir="$app_path/Contents/PlugIns/imageformats"
imageformatsSrcDir="$plugin_src/imageformats"
if [ -d "$imageformatsSrcDir" ]; then
if [ ! -d "$imageformatsDestDir" ]; then
echo "- Copying sqlitedriver plugin"
mkdir -p "$imageformatsDestDir"
cp "$imageformatsSrcDir/libqsvg.dylib" "$imageformatsDestDir/libqsvg.dylib"
fi
fi
# copy Qt Quick 2 imports # copy Qt Quick 2 imports
imports2Dir="$app_path/Contents/Imports/qtquick2" imports2Dir="$app_path/Contents/Imports/qtquick2"
if [ -d "$quick2_src" ]; then if [ -d "$quick2_src" ]; then
@@ -209,3 +174,19 @@ if [ ! -d "$app_path/Contents/Frameworks/QtCore.framework" ]; then
"$clangbackendArgument" || exit 1 "$clangbackendArgument" || exit 1
fi fi
# clean up after macdeployqt
# it deploys some plugins (and libs for these) that interfere with what we want
echo "Cleaning up after macdeployqt..."
toRemove=(\
"Contents/PlugIns/tls/libqopensslbackend.dylib" \
"Contents/PlugIns/sqldrivers/libqsqlpsql.dylib" \
"Contents/PlugIns/sqldrivers/libqsqlodbc.dylib" \
"Contents/Frameworks/libpq.*dylib" \
"Contents/Frameworks/libssl.*dylib" \
"Contents/Frameworks/libcrypto.*dylib" \
)
for f in "${toRemove[@]}"; do
echo "- removing \"$app_path/$f\""
rm "$app_path"/$f 2> /dev/null; done
exit 0

View File

@@ -25,9 +25,10 @@
#pragma once #pragma once
#include <qmetatype.h> #include <QSize>
#include <QUrl> #include <QUrl>
#include <QVector> #include <QVector>
#include <qmetatype.h>
#include "instancecontainer.h" #include "instancecontainer.h"
#include "reparentcontainer.h" #include "reparentcontainer.h"
@@ -55,6 +56,8 @@ public:
const QUrl &resourceUrl, const QUrl &resourceUrl,
const QHash<QString, QVariantMap> &edit3dToolStates, const QHash<QString, QVariantMap> &edit3dToolStates,
const QString &language, const QString &language,
QSize captureImageMinimumSize,
QSize captureImageMaximumSize,
qint32 stateInstanceId) qint32 stateInstanceId)
: instances(instanceContainer) : instances(instanceContainer)
, reparentInstances(reparentContainer) , reparentInstances(reparentContainer)
@@ -68,6 +71,8 @@ public:
, resourceUrl(resourceUrl) , resourceUrl(resourceUrl)
, edit3dToolStates(edit3dToolStates) , edit3dToolStates(edit3dToolStates)
, language(language) , language(language)
, captureImageMinimumSize(captureImageMinimumSize)
, captureImageMaximumSize(captureImageMaximumSize)
, stateInstanceId{stateInstanceId} , stateInstanceId{stateInstanceId}
{} {}
@@ -86,6 +91,8 @@ public:
out << command.edit3dToolStates; out << command.edit3dToolStates;
out << command.language; out << command.language;
out << command.stateInstanceId; out << command.stateInstanceId;
out << command.captureImageMinimumSize;
out << command.captureImageMaximumSize;
return out; return out;
} }
@@ -105,6 +112,8 @@ public:
in >> command.edit3dToolStates; in >> command.edit3dToolStates;
in >> command.language; in >> command.language;
in >> command.stateInstanceId; in >> command.stateInstanceId;
in >> command.captureImageMinimumSize;
in >> command.captureImageMaximumSize;
return in; return in;
} }
@@ -122,6 +131,8 @@ public:
QUrl resourceUrl; QUrl resourceUrl;
QHash<QString, QVariantMap> edit3dToolStates; QHash<QString, QVariantMap> edit3dToolStates;
QString language; QString language;
QSize captureImageMinimumSize;
QSize captureImageMaximumSize;
qint32 stateInstanceId = 0; qint32 stateInstanceId = 0;
}; };

View File

@@ -38,16 +38,22 @@ namespace QmlDesigner {
namespace { namespace {
QImage renderImage(ServerNodeInstance rootNodeInstance) QImage renderImage(ServerNodeInstance rootNodeInstance, QSize minimumSize, QSize maximumSize)
{ {
rootNodeInstance.updateDirtyNodeRecursive(); rootNodeInstance.updateDirtyNodeRecursive();
QSize previewImageSize = rootNodeInstance.boundingRect().size().toSize(); QSize previewImageSize = rootNodeInstance.boundingRect().size().toSize();
if (previewImageSize.isEmpty()) if (previewImageSize.isEmpty()) {
previewImageSize = {150, 150}; previewImageSize = minimumSize;
} else if (previewImageSize.width() < minimumSize.width()
|| previewImageSize.height() < minimumSize.height()) {
previewImageSize.scale(minimumSize, Qt::KeepAspectRatio);
}
if (previewImageSize.width() > 150 || previewImageSize.height() > 150) if (previewImageSize.width() > maximumSize.width()
previewImageSize.scale({150, 150}, Qt::KeepAspectRatio); || previewImageSize.height() > maximumSize.height()) {
previewImageSize.scale(maximumSize, Qt::KeepAspectRatio);
}
QImage previewImage = rootNodeInstance.renderPreviewImage(previewImageSize); QImage previewImage = rootNodeInstance.renderPreviewImage(previewImageSize);
@@ -73,7 +79,7 @@ void Qt5CaptureImageNodeInstanceServer::collectItemChangesAndSendChangeCommands(
DesignerSupport::polishItems(quickWindow()); DesignerSupport::polishItems(quickWindow());
QImage image = renderImage(rooNodeInstance); QImage image = renderImage(rooNodeInstance, m_minimumSize, m_maximumSize);
nodeInstanceClient()->capturedData(CapturedDataCommand{std::move(image)}); nodeInstanceClient()->capturedData(CapturedDataCommand{std::move(image)});
@@ -82,4 +88,12 @@ void Qt5CaptureImageNodeInstanceServer::collectItemChangesAndSendChangeCommands(
} }
} }
void QmlDesigner::Qt5CaptureImageNodeInstanceServer::createScene(const CreateSceneCommand &command)
{
m_minimumSize = command.captureImageMinimumSize;
m_maximumSize = command.captureImageMaximumSize;
Qt5PreviewNodeInstanceServer::createScene(command);
}
} // namespace } // namespace

View File

@@ -36,10 +36,14 @@ public:
: Qt5PreviewNodeInstanceServer(nodeInstanceClient) : Qt5PreviewNodeInstanceServer(nodeInstanceClient)
{} {}
void createScene(const CreateSceneCommand &command) override;
protected: protected:
void collectItemChangesAndSendChangeCommands() override; void collectItemChangesAndSendChangeCommands() override;
private: private:
QSize m_minimumSize;
QSize m_maximumSize;
}; };
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -285,6 +285,7 @@ Rectangle {
anchors.fill: parent anchors.fill: parent
source: delegateStateImageSource source: delegateStateImageSource
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
mipmap: true
} }
} }
} }

View File

@@ -95,6 +95,15 @@ Project {
@if %{IsQt6Project} @if %{IsQt6Project}
/* If any modules the project imports require widgets (e.g. QtCharts), widgetApp must be true */ /* If any modules the project imports require widgets (e.g. QtCharts), widgetApp must be true */
widgetApp: true widgetApp: true
/* args: Specifies command line arguments for qsb tool to generate shaders.
files: Specifies target files for qsb tool. If path is included, it must be relative to this file.
Wildcard '*' can be used in the file name part of the path.
e.g. files: [ "content/shaders/*.vert", "*.frag" ] */
ShaderTool {
args: "-s --glsl \\\"100 es,120,150\\\" --hlsl 50 --msl 12"
files: [ "content/shaders/*" ]
}
@endif @endif
multilanguageSupport: true multilanguageSupport: true

View File

@@ -84,10 +84,13 @@ void NamePrettyPrinter::visit(const TemplateNameId *name)
TemplateArgument templArg = name->templateArgumentAt(index); TemplateArgument templArg = name->templateArgumentAt(index);
QString arg; QString arg;
if (templArg.type().isValid()) if (templArg.type().isValid()) {
arg = overview()->prettyType(templArg.type()); Overview o = *_overview;
else if (const NumericLiteral *num = templArg.numericLiteral()) o.showReturnTypes = true;
arg = o.prettyType(templArg.type());
} else if (const NumericLiteral *num = templArg.numericLiteral()) {
arg = QString::fromLatin1(num->chars(), num->size()); arg = QString::fromLatin1(num->chars(), num->size());
}
if (arg.isEmpty()) if (arg.isEmpty())
_name += QString::fromLatin1("_Tp%1").arg(index + 1); _name += QString::fromLatin1("_Tp%1").arg(index + 1);

View File

@@ -290,8 +290,8 @@ AndroidRunnerWorker::AndroidRunnerWorker(RunWorker *runner, const QString &packa
for (const QString &shellCmd : commands) for (const QString &shellCmd : commands)
m_beforeStartAdbCommands.append(QString("shell %1").arg(shellCmd)); m_beforeStartAdbCommands.append(QString("shell %1").arg(shellCmd));
} }
const auto data = runner->recordedData(Constants::ANDROID_PRESTARTSHELLCMDLIST).toStringList(); const auto preStartCmdList = runner->recordedData(Constants::ANDROID_PRESTARTSHELLCMDLIST);
for (const QString &shellCmd : data) for (const QString &shellCmd : preStartCmdList.toStringList())
m_beforeStartAdbCommands.append(QString("shell %1").arg(shellCmd)); m_beforeStartAdbCommands.append(QString("shell %1").arg(shellCmd));
if (auto aspect = runControl->aspect(Constants::ANDROID_POSTFINISHSHELLCMDLIST)) { if (auto aspect = runControl->aspect(Constants::ANDROID_POSTFINISHSHELLCMDLIST)) {
@@ -300,8 +300,8 @@ AndroidRunnerWorker::AndroidRunnerWorker(RunWorker *runner, const QString &packa
for (const QString &shellCmd : commands) for (const QString &shellCmd : commands)
m_afterFinishAdbCommands.append(QString("shell %1").arg(shellCmd)); m_afterFinishAdbCommands.append(QString("shell %1").arg(shellCmd));
} }
const auto data2 = runner->recordedData(Constants::ANDROID_POSTFINISHSHELLCMDLIST).toStringList(); const auto postFinishCmdList = runner->recordedData(Constants::ANDROID_POSTFINISHSHELLCMDLIST);
for (const QString &shellCmd : data) for (const QString &shellCmd : postFinishCmdList.toStringList())
m_afterFinishAdbCommands.append(QString("shell %1").arg(shellCmd)); m_afterFinishAdbCommands.append(QString("shell %1").arg(shellCmd));
m_debugServerPath = debugServer(m_useLldb, target).toString(); m_debugServerPath = debugServer(m_useLldb, target).toString();

View File

@@ -677,6 +677,7 @@ public:
void update(); void update();
void finalize(); void finalize();
void resetData(bool resetFollowSymbolData);
private: private:
IAssistProposal *perform(const AssistInterface *) override IAssistProposal *perform(const AssistInterface *) override
@@ -689,8 +690,6 @@ private:
return createProposal(false); return createProposal(false);
} }
void resetData();
IAssistProposal *immediateProposalImpl() const; IAssistProposal *immediateProposalImpl() const;
IAssistProposal *createProposal(bool final) const; IAssistProposal *createProposal(bool final) const;
CppEditor::VirtualFunctionProposalItem *createEntry(const QString &name, CppEditor::VirtualFunctionProposalItem *createEntry(const QString &name,
@@ -726,7 +725,7 @@ public:
{ {
closeTempDocuments(); closeTempDocuments();
if (virtualFuncAssistProcessor) if (virtualFuncAssistProcessor)
virtualFuncAssistProcessor->cancel(); virtualFuncAssistProcessor->resetData(false);
for (const MessageId &id : qAsConst(pendingSymbolInfoRequests)) for (const MessageId &id : qAsConst(pendingSymbolInfoRequests))
q->cancelRequest(id); q->cancelRequest(id);
for (const MessageId &id : qAsConst(pendingGotoImplRequests)) for (const MessageId &id : qAsConst(pendingGotoImplRequests))
@@ -2715,6 +2714,7 @@ private:
const AstNode &m_ast; const AstNode &m_ast;
const QTextDocument * const m_doc; const QTextDocument * const m_doc;
const QString &m_docContent; const QString &m_docContent;
AstNode::FileStatus m_currentFileStatus = AstNode::FileStatus::Unknown;
}; };
// clangd reports also the #ifs, #elses and #endifs around the disabled code as disabled, // clangd reports also the #ifs, #elses and #endifs around the disabled code as disabled,
@@ -2839,11 +2839,16 @@ static void semanticHighlighter(QFutureInterface<HighlightingResult> &future,
return true; return true;
if (it->kind() == "Call") { if (it->kind() == "Call") {
// In class templates, member calls can result in "Call" nodes rather than // The first child is e.g. a called lambda or an object on which
// "CXXMemberCall". We try to detect this by checking for a certain kind of // the call happens, and should not be highlighted as an output argument.
// child node. // If the call is not fully resolved (as in templates), we don't
// know whether the argument is passed as const or not.
if (it->arcanaContains("dependent type"))
return false;
const QList<AstNode> children = it->children().value_or(QList<AstNode>()); const QList<AstNode> children = it->children().value_or(QList<AstNode>());
return children.isEmpty() || children.first().kind() != "CXXDependentScopeMember"; return children.isEmpty()
|| (children.first().range() != (it - 1)->range()
&& children.first().kind() != "UnresolvedLookup");
} }
// The token should get marked for e.g. lambdas, but not for assignment operators, // The token should get marked for e.g. lambdas, but not for assignment operators,
@@ -2863,7 +2868,7 @@ static void semanticHighlighter(QFutureInterface<HighlightingResult> &future,
// The callable is never displayed as an output parameter. // The callable is never displayed as an output parameter.
// TODO: A good argument can be made to display objects on which a non-const // TODO: A good argument can be made to display objects on which a non-const
// operator or function is called as output parameters. // operator or function is called as output parameters.
if (children.at(1).range() == range) if (children.at(1).range().contains(range))
return false; return false;
QList<AstNode> firstChildTree{children.first()}; QList<AstNode> firstChildTree{children.first()};
@@ -2883,6 +2888,8 @@ static void semanticHighlighter(QFutureInterface<HighlightingResult> &future,
if (it->kind() == "Lambda") if (it->kind() == "Lambda")
return false; return false;
if (it->kind() == "BinaryOperator")
return false;
if (it->hasConstType()) if (it->hasConstType())
return false; return false;
@@ -3110,7 +3117,7 @@ void ClangdClient::Private::handleSemanticTokens(TextDocument *doc,
void ClangdClient::VirtualFunctionAssistProcessor::cancel() void ClangdClient::VirtualFunctionAssistProcessor::cancel()
{ {
resetData(); resetData(true);
} }
void ClangdClient::VirtualFunctionAssistProcessor::update() void ClangdClient::VirtualFunctionAssistProcessor::update()
@@ -3132,15 +3139,16 @@ void ClangdClient::VirtualFunctionAssistProcessor::finalize()
} else { } else {
setAsyncProposalAvailable(proposal); setAsyncProposalAvailable(proposal);
} }
resetData(); resetData(true);
} }
void ClangdClient::VirtualFunctionAssistProcessor::resetData() void ClangdClient::VirtualFunctionAssistProcessor::resetData(bool resetFollowSymbolData)
{ {
if (!m_data) if (!m_data)
return; return;
m_data->followSymbolData->virtualFuncAssistProcessor = nullptr; m_data->followSymbolData->virtualFuncAssistProcessor = nullptr;
m_data->followSymbolData.reset(); if (resetFollowSymbolData)
m_data->followSymbolData.reset();
m_data = nullptr; m_data = nullptr;
} }
@@ -3503,6 +3511,8 @@ QIcon ClangdCompletionItem::icon() const
case SpecialQtType::None: case SpecialQtType::None:
break; break;
} }
if (item().kind().value_or(CompletionItemKind::Text) == CompletionItemKind::Property)
return Utils::CodeModelIcon::iconForType(Utils::CodeModelIcon::VarPublicStatic);
return LanguageClientCompletionItem::icon(); return LanguageClientCompletionItem::icon();
} }
@@ -4080,7 +4090,13 @@ void ExtraHighlightingResultsCollector::visitNode(const AstNode &node)
{ {
if (m_future.isCanceled()) if (m_future.isCanceled())
return; return;
switch (node.fileStatus(m_filePath)) { const AstNode::FileStatus prevFileStatus = m_currentFileStatus;
m_currentFileStatus = node.fileStatus(m_filePath);
if (m_currentFileStatus == AstNode::FileStatus::Unknown
&& prevFileStatus != AstNode::FileStatus::Ours) {
m_currentFileStatus = prevFileStatus;
}
switch (m_currentFileStatus) {
case AstNode::FileStatus::Ours: case AstNode::FileStatus::Ours:
case AstNode::FileStatus::Unknown: case AstNode::FileStatus::Unknown:
collectFromNode(node); collectFromNode(node);
@@ -4095,6 +4111,7 @@ void ExtraHighlightingResultsCollector::visitNode(const AstNode &node)
break; break;
} }
} }
m_currentFileStatus = prevFileStatus;
} }
bool ClangdClient::FollowSymbolData::defLinkIsAmbiguous() const bool ClangdClient::FollowSymbolData::defLinkIsAmbiguous() const

View File

@@ -27,8 +27,9 @@
#include <texteditor/codeassist/iassistproposalmodel.h> #include <texteditor/codeassist/iassistproposalmodel.h>
#include <QString> #include <QList>
#include <QSharedPointer> #include <QSharedPointer>
#include <QString>
namespace TextEditor { class BaseTextEditor; } namespace TextEditor { class BaseTextEditor; }

View File

@@ -1321,6 +1321,14 @@ void ClangdTestHighlighting::test_data()
<< QList<int>{C_FIELD} << 0; << QList<int>{C_FIELD} << 0;
QTest::newRow("member call on dependent (3)") << 999 << 9 << 999 << 12 QTest::newRow("member call on dependent (3)") << 999 << 9 << 999 << 12
<< QList<int>{C_LOCAL} << 0; << QList<int>{C_LOCAL} << 0;
QTest::newRow("member access via operator->") << 1009 << 7 << 1009 << 21
<< QList<int>{C_FIELD} << 0;
QTest::newRow("lambda call in member") << 1023 << 9 << 1023 << 15
<< QList<int>{C_LOCAL} << 0;
QTest::newRow("call on inherited member") << 1024 << 9 << 1024 << 12
<< QList<int>{C_FIELD} << 0;
QTest::newRow("pass inherited member by value") << 1038 << 21 << 1038 << 26
<< QList<int>{C_FIELD} << 0;
} }
void ClangdTestHighlighting::test() void ClangdTestHighlighting::test()
@@ -1423,12 +1431,12 @@ void ClangdTestHighlighting::test()
void ClangdTestHighlighting::testIfdefedOutBlocks() void ClangdTestHighlighting::testIfdefedOutBlocks()
{ {
QCOMPARE(m_ifdefedOutBlocks.size(), 3); QCOMPARE(m_ifdefedOutBlocks.size(), 3);
QCOMPARE(m_ifdefedOutBlocks.at(0).first(), 12033); QCOMPARE(m_ifdefedOutBlocks.at(0).first(), 12056);
QCOMPARE(m_ifdefedOutBlocks.at(0).last(), 12050); QCOMPARE(m_ifdefedOutBlocks.at(0).last(), 12073);
QCOMPARE(m_ifdefedOutBlocks.at(1).first(), 13351); QCOMPARE(m_ifdefedOutBlocks.at(1).first(), 13374);
QCOMPARE(m_ifdefedOutBlocks.at(1).last(), 13364); QCOMPARE(m_ifdefedOutBlocks.at(1).last(), 13387);
QCOMPARE(m_ifdefedOutBlocks.at(2).first(), 13390); QCOMPARE(m_ifdefedOutBlocks.at(2).first(), 13413);
QCOMPARE(m_ifdefedOutBlocks.at(2).last(), 13402); QCOMPARE(m_ifdefedOutBlocks.at(2).last(), 13425);
} }

View File

@@ -5,7 +5,7 @@ auto *rawVariable = R"(Vari
auto Character = 'c'; auto Character = 'c';
namespace std { namespace std {
template<typename T> class vector {}; template<typename T> class vector { public: void clear(); };
template<typename T, typename U> class pair {}; template<typename T, typename U> class pair {};
} }
@@ -999,3 +999,44 @@ public:
ptr->bar(); ptr->bar();
} }
}; };
namespace std { template<typename T> struct optional { T* operator->(); }; }
struct structWithData { int value; };
struct structWithOptional { std::optional<structWithData> opt_my_struct1; };
void foo(structWithOptional & s)
{
s.opt_my_struct1->value = 5;
}
class BaseWithMember
{
protected:
std::vector<unsigned char> vec;
};
template<typename T> class Derived : public BaseWithMember
{
void foo()
{
auto lambda = [&] {};
lambda();
vec.clear();
}
};
static bool testVal(int val);
class BaseWithMember2
{
protected:
int value;
};
template<typename T> class Derived2 : public BaseWithMember2
{
bool foo()
{
if (testVal(value))
return true;
return false;
}
};

View File

@@ -259,7 +259,7 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildConfiguration *bc)
m_configView->setUniformRowHeights(true); m_configView->setUniformRowHeights(true);
m_configView->setSortingEnabled(true); m_configView->setSortingEnabled(true);
m_configView->sortByColumn(0, Qt::AscendingOrder); m_configView->sortByColumn(0, Qt::AscendingOrder);
auto stretcher = new HeaderViewStretcher(m_configView->header(), 0); (void) new HeaderViewStretcher(m_configView->header(), 0);
m_configView->setSelectionMode(QAbstractItemView::ExtendedSelection); m_configView->setSelectionMode(QAbstractItemView::ExtendedSelection);
m_configView->setSelectionBehavior(QAbstractItemView::SelectItems); m_configView->setSelectionBehavior(QAbstractItemView::SelectItems);
m_configView->setAlternatingRowColors(true); m_configView->setAlternatingRowColors(true);
@@ -373,18 +373,15 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildConfiguration *bc)
m_configModel->setConfiguration(m_buildConfiguration->configurationFromCMake()); m_configModel->setConfiguration(m_buildConfiguration->configurationFromCMake());
m_configModel->setInitialParametersConfiguration( m_configModel->setInitialParametersConfiguration(
m_buildConfiguration->initialCMakeConfiguration()); m_buildConfiguration->initialCMakeConfiguration());
m_configView->expandAll();
} }
connect(bc->buildSystem(), &BuildSystem::parsingFinished, this, [this, stretcher] { connect(bc->buildSystem(), &BuildSystem::parsingFinished, this, [this] {
m_configModel->setConfiguration(m_buildConfiguration->configurationFromCMake()); m_configModel->setConfiguration(m_buildConfiguration->configurationFromCMake());
m_configModel->setInitialParametersConfiguration( m_configModel->setInitialParametersConfiguration(
m_buildConfiguration->initialCMakeConfiguration()); m_buildConfiguration->initialCMakeConfiguration());
m_buildConfiguration->filterConfigArgumentsFromAdditionalCMakeArguments(); m_buildConfiguration->filterConfigArgumentsFromAdditionalCMakeArguments();
updateFromKit(); updateFromKit();
m_configView->expandAll();
m_configView->setEnabled(true); m_configView->setEnabled(true);
stretcher->stretch();
updateButtonState(); updateButtonState();
m_showProgressTimer.stop(); m_showProgressTimer.stop();
m_progressIndicator->hide(); m_progressIndicator->hide();
@@ -402,10 +399,6 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildConfiguration *bc)
m_progressIndicator->hide(); m_progressIndicator->hide();
updateConfigurationStateSelection(); updateConfigurationStateSelection();
}); });
connect(m_configTextFilterModel, &QAbstractItemModel::modelReset, this, [this, stretcher]() {
m_configView->expandAll();
stretcher->stretch();
});
connect(m_configModel, &QAbstractItemModel::dataChanged, connect(m_configModel, &QAbstractItemModel::dataChanged,
this, &CMakeBuildSettingsWidget::updateButtonState); this, &CMakeBuildSettingsWidget::updateButtonState);

View File

@@ -216,8 +216,8 @@ void SearchResultTreeItemDelegate::drawText(QPainter *painter,
const QString textBefore = text.left(searchTermStart).replace(QLatin1Char('\t'), m_tabString); const QString textBefore = text.left(searchTermStart).replace(QLatin1Char('\t'), m_tabString);
const QString textHighlight = text.mid(searchTermStart, searchTermLength).replace(QLatin1Char('\t'), m_tabString); const QString textHighlight = text.mid(searchTermStart, searchTermLength).replace(QLatin1Char('\t'), m_tabString);
const QString textAfter = text.mid(searchTermStart + searchTermLength).replace(QLatin1Char('\t'), m_tabString); const QString textAfter = text.mid(searchTermStart + searchTermLength).replace(QLatin1Char('\t'), m_tabString);
int searchTermStartPixels = painter->fontMetrics().horizontalAdvance(textBefore); int searchTermStartPixels = option.fontMetrics.horizontalAdvance(textBefore);
int searchTermLengthPixels = painter->fontMetrics().horizontalAdvance(textHighlight); int searchTermLengthPixels = option.fontMetrics.horizontalAdvance(textHighlight);
// rects // rects
QRect beforeHighlightRect(rect); QRect beforeHighlightRect(rect);

View File

@@ -3273,6 +3273,58 @@ void QuickfixTest::testGenerateGetterSetterOnlySetter()
QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 0); QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 0);
} }
void QuickfixTest::testGenerateGetterSetterAnonymousClass()
{
QList<TestDocumentPtr> testDocuments;
QByteArray original;
QByteArray expected;
QuickFixSettings s;
s->setterInCppFileFrom = 1;
s->setterParameterNameTemplate = "value";
// Header File
original = R"(
class {
int @m_foo;
} bar;
)";
expected = R"(
class {
int m_foo;
public:
int foo() const
{
return m_foo;
}
void setFoo(int value)
{
if (m_foo == value)
return;
m_foo = value;
emit fooChanged();
}
void resetFoo()
{
setFoo({}); // TODO: Adapt to use your actual default defaultValue
}
signals:
void fooChanged();
private:
Q_PROPERTY(int foo READ foo WRITE setFoo RESET resetFoo NOTIFY fooChanged)
} bar;
)";
testDocuments << CppTestDocument::create("file.h", original, expected);
// Source File
testDocuments << CppTestDocument::create("file.cpp", {}, {});
GenerateGetterSetter factory;
QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 4);
}
void QuickfixTest::testGenerateGetterSetterInlineInHeaderFile() void QuickfixTest::testGenerateGetterSetterInlineInHeaderFile()
{ {
QList<TestDocumentPtr> testDocuments; QList<TestDocumentPtr> testDocuments;
@@ -3362,6 +3414,43 @@ void QuickfixTest::testGenerateGetterSetterOnlySetterHeaderFileWithIncludeGuard(
QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 0); QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 0);
} }
void QuickfixTest::testGenerateGetterFunctionAsTemplateArg()
{
QList<TestDocumentPtr> testDocuments;
const QByteArray original = R"(
template<typename T> class TS {};
template<typename T, typename U> class TS<T(U)> {};
class S2 {
TS<int(int)> @member;
};
)";
const QByteArray expected = R"(
template<typename T> class TS {};
template<typename T, typename U> class TS<T(U)> {};
class S2 {
TS<int(int)> member;
public:
const TS<int (int)> &getMember() const
{
return member;
}
};
)";
testDocuments << CppTestDocument::create("file.h", original, expected);
QuickFixSettings s;
s->getterOutsideClassFrom = 0;
s->getterInCppFileFrom = 0;
s->getterNameTemplate = "get<Name>";
GenerateGetterSetter factory;
QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), 1);
}
class CppCodeStyleSettingsChanger { class CppCodeStyleSettingsChanger {
public: public:
CppCodeStyleSettingsChanger(const CppCodeStyleSettings &settings); CppCodeStyleSettingsChanger(const CppCodeStyleSettings &settings);

View File

@@ -113,8 +113,10 @@ private slots:
void testGenerateGetterSetterGeneralTests(); void testGenerateGetterSetterGeneralTests();
void testGenerateGetterSetterOnlyGetter(); void testGenerateGetterSetterOnlyGetter();
void testGenerateGetterSetterOnlySetter(); void testGenerateGetterSetterOnlySetter();
void testGenerateGetterSetterAnonymousClass();
void testGenerateGetterSetterInlineInHeaderFile(); void testGenerateGetterSetterInlineInHeaderFile();
void testGenerateGetterSetterOnlySetterHeaderFileWithIncludeGuard(); void testGenerateGetterSetterOnlySetterHeaderFileWithIncludeGuard();
void testGenerateGetterFunctionAsTemplateArg();
void testGenerateGettersSetters_data(); void testGenerateGettersSetters_data();
void testGenerateGettersSetters(); void testGenerateGettersSetters();

View File

@@ -3907,7 +3907,12 @@ void GetterSetterRefactoringHelper::performGeneration(ExistingGetterSetterData d
else else
getterInClassDeclaration += QLatin1String(" const"); getterInClassDeclaration += QLatin1String(" const");
getterInClassDeclaration.prepend(m_settings->getterAttributes + QLatin1Char(' ')); getterInClassDeclaration.prepend(m_settings->getterAttributes + QLatin1Char(' '));
auto getterLocation = m_settings->determineGetterLocation(1); auto getterLocation = m_settings->determineGetterLocation(1);
// if we have an anonymous class we must add code inside the class
if (data.clazz->name()->isAnonymousNameId())
getterLocation = CppQuickFixSettings::FunctionLocation::InsideClass;
if (getterLocation == CppQuickFixSettings::FunctionLocation::InsideClass) { if (getterLocation == CppQuickFixSettings::FunctionLocation::InsideClass) {
getterInClassDeclaration += QLatin1String("\n{\nreturn ") + returnExpression getterInClassDeclaration += QLatin1String("\n{\nreturn ") + returnExpression
+ QLatin1String(";\n}\n"); + QLatin1String(";\n}\n");
@@ -4026,6 +4031,10 @@ void GetterSetterRefactoringHelper::performGeneration(ExistingGetterSetterData d
body += "}"; body += "}";
auto setterLocation = m_settings->determineSetterLocation(body.count('\n') - 2); auto setterLocation = m_settings->determineSetterLocation(body.count('\n') - 2);
// if we have an anonymous class we must add code inside the class
if (data.clazz->name()->isAnonymousNameId())
setterLocation = CppQuickFixSettings::FunctionLocation::InsideClass;
if (setterLocation == CppQuickFixSettings::FunctionLocation::CppFile && !hasSourceFile()) if (setterLocation == CppQuickFixSettings::FunctionLocation::CppFile && !hasSourceFile())
setterLocation = CppQuickFixSettings::FunctionLocation::OutsideClass; setterLocation = CppQuickFixSettings::FunctionLocation::OutsideClass;
@@ -4100,6 +4109,10 @@ void GetterSetterRefactoringHelper::performGeneration(ExistingGetterSetterData d
body.replace(QRegularExpression("\\b" + parameterName + "\\b"), "defaultValue"); body.replace(QRegularExpression("\\b" + parameterName + "\\b"), "defaultValue");
// body.count('\n') - 2 : do not count the 2 at start // body.count('\n') - 2 : do not count the 2 at start
auto resetLocation = m_settings->determineSetterLocation(body.count('\n') - 2); auto resetLocation = m_settings->determineSetterLocation(body.count('\n') - 2);
// if we have an anonymous class we must add code inside the class
if (data.clazz->name()->isAnonymousNameId())
resetLocation = CppQuickFixSettings::FunctionLocation::InsideClass;
if (resetLocation == CppQuickFixSettings::FunctionLocation::CppFile && !hasSourceFile()) if (resetLocation == CppQuickFixSettings::FunctionLocation::CppFile && !hasSourceFile())
resetLocation = CppQuickFixSettings::FunctionLocation::OutsideClass; resetLocation = CppQuickFixSettings::FunctionLocation::OutsideClass;

View File

@@ -290,6 +290,13 @@ bool DebuggerItem::addAndroidLldbPythonEnv(const Utils::FilePath &lldbCmd, Utils
if (pythonBinDir.exists()) { if (pythonBinDir.exists()) {
env.set("PYTHONHOME", pythonDir.toUserOutput()); env.set("PYTHONHOME", pythonDir.toUserOutput());
env.prependOrSetPath(pythonBinDir); env.prependOrSetPath(pythonBinDir);
if (HostOsInfo::isAnyUnixHost()) {
const FilePath pythonLibDir = pythonDir.pathAppended("lib");
if (pythonLibDir.exists())
env.prependOrSet("LD_LIBRARY_PATH", pythonLibDir.toString());
}
return true; return true;
} }
} }

View File

@@ -435,17 +435,22 @@ void GenericBuildSystem::parse(RefreshOptions options)
if (options & Configuration) { if (options & Configuration) {
m_rawProjectIncludePaths = readLines(m_includesFileName); m_rawProjectIncludePaths = readLines(m_includesFileName);
Utils::FilePaths normalPaths; QStringList normalPaths;
Utils::FilePaths frameworkPaths; QStringList frameworkPaths;
const auto baseDir = Utils::FilePath::fromString(m_includesFileName).parentDir(); const auto baseDir = Utils::FilePath::fromString(m_includesFileName).parentDir();
for (const QString &rawPath : qAsConst(m_rawProjectIncludePaths)) { for (const QString &rawPath : qAsConst(m_rawProjectIncludePaths)) {
if (rawPath.startsWith("-F")) if (rawPath.startsWith("-F"))
frameworkPaths << baseDir.resolvePath(rawPath.mid(2)); frameworkPaths << rawPath.mid(2);
else else
normalPaths << baseDir.resolvePath(rawPath); normalPaths << rawPath;
} }
m_projectIncludePaths = toUserHeaderPaths(normalPaths); const auto expandedPaths = [this](const QStringList &paths) {
m_projectIncludePaths << toFrameworkHeaderPaths(frameworkPaths); return Utils::transform(processEntries(paths), [](const auto &pair) {
return pair.first;
});
};
m_projectIncludePaths = toUserHeaderPaths(expandedPaths(normalPaths));
m_projectIncludePaths << toFrameworkHeaderPaths(expandedPaths(frameworkPaths));
m_cxxflags = readFlags(m_cxxflagsFileName); m_cxxflags = readFlags(m_cxxflagsFileName);
m_cflags = readFlags(m_cflagsFileName); m_cflags = readFlags(m_cflagsFileName);
} }

View File

@@ -50,31 +50,17 @@
namespace QmlDesigner { namespace QmlDesigner {
namespace { class AssetsLibraryView::ImageCacheData
ProjectExplorer::Target *activeTarget(ProjectExplorer::Project *project)
{
if (project)
return project->activeTarget();
return {};
}
} // namespace
class ImageCacheData
{ {
public: public:
Sqlite::Database database{Utils::PathString{ Sqlite::Database database{Utils::PathString{
Core::ICore::cacheResourcePath("imagecache-v2.db").toString()}, Core::ICore::cacheResourcePath("fontimagecache.db").toString()},
Sqlite::JournalMode::Wal, Sqlite::JournalMode::Wal,
Sqlite::LockingMode::Normal}; Sqlite::LockingMode::Normal};
ImageCacheStorage<Sqlite::Database> storage{database}; ImageCacheStorage<Sqlite::Database> storage{database};
ImageCacheConnectionManager connectionManager;
ImageCacheCollector collector{connectionManager};
ImageCacheFontCollector fontCollector; ImageCacheFontCollector fontCollector;
ImageCacheGenerator generator{collector, storage};
ImageCacheGenerator fontGenerator{fontCollector, storage}; ImageCacheGenerator fontGenerator{fontCollector, storage};
TimeStampProvider timeStampProvider; TimeStampProvider timeStampProvider;
AsynchronousImageCache cache{storage, generator, timeStampProvider};
AsynchronousImageCache asynchronousFontImageCache{storage, fontGenerator, timeStampProvider}; AsynchronousImageCache asynchronousFontImageCache{storage, fontGenerator, timeStampProvider};
SynchronousImageCache synchronousFontImageCache{storage, timeStampProvider, fontCollector}; SynchronousImageCache synchronousFontImageCache{storage, timeStampProvider, fontCollector};
}; };
@@ -94,9 +80,8 @@ bool AssetsLibraryView::hasWidget() const
WidgetInfo AssetsLibraryView::widgetInfo() WidgetInfo AssetsLibraryView::widgetInfo()
{ {
if (m_widget.isNull()) { if (m_widget.isNull()) {
m_widget = new AssetsLibraryWidget{imageCacheData()->cache, m_widget = new AssetsLibraryWidget{imageCacheData()->asynchronousFontImageCache,
imageCacheData()->asynchronousFontImageCache, imageCacheData()->synchronousFontImageCache};
imageCacheData()->synchronousFontImageCache};
} }
return createWidgetInfo(m_widget.data(), "Assets", WidgetInfo::LeftPane, 0, tr("Assets")); return createWidgetInfo(m_widget.data(), "Assets", WidgetInfo::LeftPane, 0, tr("Assets"));
@@ -128,49 +113,18 @@ void AssetsLibraryView::setResourcePath(const QString &resourcePath)
m_lastResourcePath = resourcePath; m_lastResourcePath = resourcePath;
if (m_widget.isNull()) { if (m_widget.isNull()) {
m_widget = new AssetsLibraryWidget{m_imageCacheData->cache, m_widget = new AssetsLibraryWidget{imageCacheData()->asynchronousFontImageCache,
m_imageCacheData->asynchronousFontImageCache, imageCacheData()->synchronousFontImageCache};
m_imageCacheData->synchronousFontImageCache};
} }
m_widget->setResourcePath(resourcePath); m_widget->setResourcePath(resourcePath);
} }
ImageCacheData *AssetsLibraryView::imageCacheData() AssetsLibraryView::ImageCacheData *AssetsLibraryView::imageCacheData()
{ {
std::call_once(imageCacheFlag, [this]() { std::call_once(imageCacheFlag,
m_imageCacheData = std::make_unique<ImageCacheData>(); [this]() { m_imageCacheData = std::make_unique<ImageCacheData>(); });
auto setTargetInImageCache =
[imageCacheData = m_imageCacheData.get()](ProjectExplorer::Target *target) {
if (target == imageCacheData->collector.target())
return;
if (target)
imageCacheData->cache.clean();
imageCacheData->collector.setTarget(target);
};
if (auto project = ProjectExplorer::SessionManager::startupProject(); project) {
m_imageCacheData->collector.setTarget(project->activeTarget());
connect(project,
&ProjectExplorer::Project::activeTargetChanged,
this,
setTargetInImageCache);
}
connect(ProjectExplorer::SessionManager::instance(),
&ProjectExplorer::SessionManager::startupProjectChanged,
this,
[=](ProjectExplorer::Project *project) {
setTargetInImageCache(activeTarget(project));
});
});
return m_imageCacheData.get(); return m_imageCacheData.get();
} }
AsynchronousImageCache &AssetsLibraryView::imageCache()
{
return imageCacheData()->cache;
}
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -34,7 +34,6 @@
namespace QmlDesigner { namespace QmlDesigner {
class AssetsLibraryWidget; class AssetsLibraryWidget;
class ImageCacheData;
class AsynchronousImageCache; class AsynchronousImageCache;
class AssetsLibraryView : public AbstractView class AssetsLibraryView : public AbstractView
@@ -54,9 +53,8 @@ public:
void setResourcePath(const QString &resourcePath); void setResourcePath(const QString &resourcePath);
AsynchronousImageCache &imageCache();
private: private:
class ImageCacheData;
ImageCacheData *imageCacheData(); ImageCacheData *imageCacheData();
std::once_flag imageCacheFlag; std::once_flag imageCacheFlag;

View File

@@ -110,16 +110,14 @@ bool AssetsLibraryWidget::eventFilter(QObject *obj, QEvent *event)
return QObject::eventFilter(obj, event); return QObject::eventFilter(obj, event);
} }
AssetsLibraryWidget::AssetsLibraryWidget(AsynchronousImageCache &imageCache, AssetsLibraryWidget::AssetsLibraryWidget(AsynchronousImageCache &asynchronousFontImageCache,
AsynchronousImageCache &asynchronousFontImageCache, SynchronousImageCache &synchronousFontImageCache)
SynchronousImageCache &synchronousFontImageCache)
: m_itemIconSize(24, 24) : m_itemIconSize(24, 24)
, m_fontImageCache(synchronousFontImageCache) , m_fontImageCache(synchronousFontImageCache)
, m_assetsIconProvider(new AssetsLibraryIconProvider(synchronousFontImageCache)) , m_assetsIconProvider(new AssetsLibraryIconProvider(synchronousFontImageCache))
, m_fileSystemWatcher(new Utils::FileSystemWatcher(this)) , m_fileSystemWatcher(new Utils::FileSystemWatcher(this))
, m_assetsModel(new AssetsLibraryModel(m_fileSystemWatcher, this)) , m_assetsModel(new AssetsLibraryModel(m_fileSystemWatcher, this))
, m_assetsWidget(new QQuickWidget(this)) , m_assetsWidget(new QQuickWidget(this))
, m_imageCache{imageCache}
{ {
m_assetCompressionTimer.setInterval(200); m_assetCompressionTimer.setInterval(200);
m_assetCompressionTimer.setSingleShot(true); m_assetCompressionTimer.setSingleShot(true);

View File

@@ -60,8 +60,7 @@ class AssetsLibraryWidget : public QFrame
Q_OBJECT Q_OBJECT
public: public:
AssetsLibraryWidget(AsynchronousImageCache &imageCache, AssetsLibraryWidget(AsynchronousImageCache &asynchronousFontImageCache,
AsynchronousImageCache &asynchronousFontImageCache,
SynchronousImageCache &synchronousFontImageCache); SynchronousImageCache &synchronousFontImageCache);
~AssetsLibraryWidget(); ~AssetsLibraryWidget();
@@ -110,7 +109,6 @@ private:
std::unique_ptr<PreviewTooltipBackend> m_fontPreviewTooltipBackend; std::unique_ptr<PreviewTooltipBackend> m_fontPreviewTooltipBackend;
QShortcut *m_qmlSourceUpdateShortcut = nullptr; QShortcut *m_qmlSourceUpdateShortcut = nullptr;
AsynchronousImageCache &m_imageCache;
QPointer<Model> m_model; QPointer<Model> m_model;
QStringList m_assetsToDrag; QStringList m_assetsToDrag;
bool m_updateRetry = false; bool m_updateRetry = false;

View File

@@ -41,29 +41,6 @@ typedef unsigned char RGBE[4];
#define B 2 #define B 2
#define E 3 #define E 3
struct M8E8
{
quint8 m;
quint8 e;
M8E8() : m(0), e(0){
}
M8E8(const float val) {
float l2 = 1.f + std::floor(log2f(val));
float mm = val / powf(2.f, l2);
m = quint8(mm * 255.f);
e = quint8(l2 + 128);
}
M8E8(const float val, quint8 exp) {
if (val <= 0) {
m = e = 0;
return;
}
float mm = val / powf(2.f, exp - 128);
m = quint8(mm * 255.f);
e = exp;
}
};
QByteArray fileToByteArray(QString const &filename) QByteArray fileToByteArray(QString const &filename)
{ {
QFile file(filename); QFile file(filename);
@@ -147,17 +124,18 @@ void decodeScanlineToImageData(RGBE *scanline, int width, void *outBuf, quint32
rgbaF32[R] = convertComponent(scanline[i][E], scanline[i][R]); rgbaF32[R] = convertComponent(scanline[i][E], scanline[i][R]);
rgbaF32[G] = convertComponent(scanline[i][E], scanline[i][G]); rgbaF32[G] = convertComponent(scanline[i][E], scanline[i][G]);
rgbaF32[B] = convertComponent(scanline[i][E], scanline[i][B]); rgbaF32[B] = convertComponent(scanline[i][E], scanline[i][B]);
rgbaF32[3] = 1.0f; rgbaF32[E] = 1.0f;
float max = qMax(rgbaF32[R], qMax(rgbaF32[G], rgbaF32[B]));
M8E8 ex(max);
M8E8 a(rgbaF32[R], ex.e);
M8E8 b(rgbaF32[G], ex.e);
M8E8 c(rgbaF32[B], ex.e);
quint8 *dst = target + i * 4; quint8 *dst = target + i * 4;
dst[0] = c.m;
dst[1] = b.m; auto getColor = [](float src) -> quint8 {
dst[2] = a.m; const float srcColor = (src > 1.0f) ? 1.0f : src;
return quint8(srcColor * 255.0f);
};
dst[0] = getColor(rgbaF32[B]);
dst[1] = getColor(rgbaF32[G]);
dst[2] = getColor(rgbaF32[R]);
dst[3] = 255; dst[3] = 255;
} }
} }

View File

@@ -33,6 +33,8 @@
#include <qmldesignerplugin.h> #include <qmldesignerplugin.h>
#include <qmldesignerconstants.h> #include <qmldesignerconstants.h>
#include <QFile>
namespace QmlDesigner { namespace QmlDesigner {
static inline bool itemsHaveSameParent(const QList<ModelNode> &siblingList) static inline bool itemsHaveSameParent(const QList<ModelNode> &siblingList)
@@ -94,10 +96,24 @@ bool selectionHasSameParent(const SelectionContext &selectionState)
return !selectionState.selectedModelNodes().isEmpty() && itemsHaveSameParent(selectionState.selectedModelNodes()); return !selectionState.selectedModelNodes().isEmpty() && itemsHaveSameParent(selectionState.selectedModelNodes());
} }
bool fileComponentExists(const ModelNode &modelNode)
{
if (!modelNode.metaInfo().isFileComponent())
return true;
const QString fileName = modelNode.metaInfo().componentFileName();
if (fileName.contains("qml/QtQuick"))
return false;
return QFile::exists(fileName);
}
bool selectionIsComponent(const SelectionContext &selectionState) bool selectionIsComponent(const SelectionContext &selectionState)
{ {
return selectionState.currentSingleSelectedNode().isValid() return selectionState.currentSingleSelectedNode().isValid()
&& selectionState.currentSingleSelectedNode().isComponent(); && selectionState.currentSingleSelectedNode().isComponent()
&& fileComponentExists(selectionState.currentSingleSelectedNode());
} }
bool selectionIsImported3DAsset(const SelectionContext &selectionState) bool selectionIsImported3DAsset(const SelectionContext &selectionState)

View File

@@ -55,6 +55,7 @@
#include <QScrollArea> #include <QScrollArea>
#include <QMessageBox> #include <QMessageBox>
#include <QFileDialog> #include <QFileDialog>
#include <QVBoxLayout>
namespace QmlDesigner { namespace QmlDesigner {
@@ -73,7 +74,11 @@ static void addFormattedMessage(Utils::OutputFormatter *formatter, const QString
formatter->plainTextEdit()->verticalScrollBar()->maximum()); formatter->plainTextEdit()->verticalScrollBar()->maximum());
} }
static const int rowHeight = 26; static const int rowHeight = 28;
static const int checkBoxColWidth = 18;
static const int labelMinWidth = 130;
static const int controlMinWidth = 65;
static const int columnSpacing = 16;
} }
@@ -120,6 +125,12 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog(
ui->buttonBox->button(QDialogButtonBox::Close)->setDefault(true); ui->buttonBox->button(QDialogButtonBox::Close)->setDefault(true);
ui->advancedSettingsButton->setStyleSheet(
"QPushButton#advancedSettingsButton {background-color: transparent}");
ui->advancedSettingsButton->setStyleSheet(
QString("QPushButton { border: none; color :%1 }").arg(
Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_HighlightColor).name()));
QStringList importPaths; QStringList importPaths;
auto doc = QmlDesignerPlugin::instance()->currentDesignDocument(); auto doc = QmlDesignerPlugin::instance()->currentDesignDocument();
if (doc) { if (doc) {
@@ -196,6 +207,13 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog(
++optIndex; ++optIndex;
} }
// Resize lists in loop for Qt5 compatibility
for (int i = 0; i < optIndex; ++i) {
m_simpleData.contentWidgets.append({});
m_advancedData.contentWidgets.append({});
m_labelToControlWidgetMaps.append(QHash<QString, QWidget *>());
}
// Create tab for each supported extension group that also has files included in the import // Create tab for each supported extension group that also has files included in the import
QMap<QString, int> tabMap; // QMap used for alphabetical order QMap<QString, int> tabMap; // QMap used for alphabetical order
for (const auto &file : qAsConst(m_quick3DFiles)) { for (const auto &file : qAsConst(m_quick3DFiles)) {
@@ -214,22 +232,21 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog(
auto tabIt = tabMap.constBegin(); auto tabIt = tabMap.constBegin();
while (tabIt != tabMap.constEnd()) { while (tabIt != tabMap.constEnd()) {
createTab(tabIt.key(), tabIt.value(), groups[tabIt.value()]); createTab(tabIt.key(), tabIt.value(), groups[tabIt.value()]);
++tabIt;
}
// Pad all tabs to same height auto padGrid = [](QWidget *widget, int optionRows) {
for (int i = 0; i < ui->tabWidget->count(); ++i) { auto grid = qobject_cast<QGridLayout *>(widget->layout());
auto optionsArea = qobject_cast<QScrollArea *>(ui->tabWidget->widget(i));
if (optionsArea && optionsArea->widget()) {
auto grid = qobject_cast<QGridLayout *>(optionsArea->widget()->layout());
if (grid) { if (grid) {
int rows = grid->rowCount(); int rows = grid->rowCount();
for (int j = rows; j < m_optionsRows; ++j) { for (int i = rows; i <optionRows; ++i) {
grid->addWidget(new QWidget(optionsArea->widget()), j, 0); grid->addWidget(new QWidget(widget), i, 0);
grid->setRowMinimumHeight(j, rowHeight); grid->setRowMinimumHeight(i, rowHeight);
} }
} }
} };
padGrid(m_simpleData.contentWidgets[tabIt.value()], m_simpleData.optionsRows);
padGrid(m_advancedData.contentWidgets[tabIt.value()], m_advancedData.optionsRows);
++tabIt;
} }
ui->tabWidget->setCurrentIndex(0); ui->tabWidget->setCurrentIndex(0);
@@ -257,8 +274,10 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog(
for (const auto &file : qAsConst(m_quick3DFiles)) for (const auto &file : qAsConst(m_quick3DFiles))
addInfo(file); addInfo(file);
QTimer::singleShot(0, [this]() { connect(ui->advancedSettingsButton, &QPushButton::clicked,
ui->tabWidget->setMaximumHeight(m_optionsHeight + ui->tabWidget->tabBar()->height() + 10); this, &ItemLibraryAssetImportDialog::toggleAdvanced);
QTimer::singleShot(0, this, [this]() {
updateUi(); updateUi();
}); });
} }
@@ -390,28 +409,45 @@ void ItemLibraryAssetImportDialog::updateImport(const ModelNode &updateNode,
void ItemLibraryAssetImportDialog::createTab(const QString &tabLabel, int optionsIndex, void ItemLibraryAssetImportDialog::createTab(const QString &tabLabel, int optionsIndex,
const QJsonObject &groups) const QJsonObject &groups)
{ {
const int checkBoxColWidth = 18;
const int labelMinWidth = 130;
const int controlMinWidth = 65;
const int columnSpacing = 16;
int rowIndex[2] = {0, 0};
QJsonObject &options = m_importOptions[optionsIndex];
// First index has ungrouped widgets, rest are groups
// First item in each real group is group label
QVector<QVector<QPair<QWidget *, QWidget *>>> widgets;
QHash<QString, int> groupIndexMap;
QHash<QString, QPair<QWidget *, QWidget *>> optionToWidgetsMap;
QHash<QString, QJsonArray> conditionMap;
QHash<QWidget *, QWidget *> conditionalWidgetMap;
QHash<QString, QString> optionToGroupMap;
auto optionsArea = new QScrollArea(ui->tabWidget); auto optionsArea = new QScrollArea(ui->tabWidget);
optionsArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); optionsArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
auto optionsAreaContents = new QWidget(optionsArea); auto optionsAreaContents = new QWidget(optionsArea);
m_simpleData.contentWidgets[optionsIndex] = new QWidget(optionsAreaContents);
m_advancedData.contentWidgets[optionsIndex] = new QWidget(optionsAreaContents);
auto layout = new QGridLayout(optionsAreaContents); // Advanced widgets need to be set up first, as simple widgets will connect to those
QGridLayout *advancedLayout = createOptionsGrid(m_advancedData.contentWidgets[optionsIndex], true,
optionsIndex, groups);
QGridLayout *simpleLayout = createOptionsGrid(m_simpleData.contentWidgets[optionsIndex], false,
optionsIndex, groups);
m_advancedData.contentWidgets[optionsIndex]->setLayout(advancedLayout);
m_simpleData.contentWidgets[optionsIndex]->setLayout(simpleLayout);
m_advancedData.contentWidgets[optionsIndex]->setVisible(false);
auto layout = new QVBoxLayout(optionsAreaContents);
layout->addWidget(m_simpleData.contentWidgets[optionsIndex]);
layout->addWidget(m_advancedData.contentWidgets[optionsIndex]);
optionsAreaContents->setContentsMargins(0, 0, 0, 0);
optionsAreaContents->setLayout(layout);
optionsAreaContents->setMinimumWidth(
(checkBoxColWidth + labelMinWidth + controlMinWidth) * 2 + columnSpacing);
optionsAreaContents->setObjectName("optionsAreaContents"); // For stylesheet
optionsArea->setWidget(optionsAreaContents);
optionsArea->setStyleSheet("QScrollArea {background-color: transparent}");
optionsAreaContents->setStyleSheet(
"QWidget#optionsAreaContents {background-color: transparent}");
ui->tabWidget->addTab(optionsArea, tr("%1 options").arg(tabLabel));
}
QGridLayout *ItemLibraryAssetImportDialog::createOptionsGrid(
QWidget *contentWidget, bool advanced, int optionsIndex, const QJsonObject &groups)
{
auto layout = new QGridLayout();
layout->setColumnMinimumWidth(0, checkBoxColWidth); layout->setColumnMinimumWidth(0, checkBoxColWidth);
layout->setColumnMinimumWidth(1, labelMinWidth); layout->setColumnMinimumWidth(1, labelMinWidth);
layout->setColumnMinimumWidth(2, controlMinWidth); layout->setColumnMinimumWidth(2, controlMinWidth);
@@ -427,14 +463,29 @@ void ItemLibraryAssetImportDialog::createTab(const QString &tabLabel, int option
layout->setColumnStretch(5, 4); layout->setColumnStretch(5, 4);
layout->setColumnStretch(6, 2); layout->setColumnStretch(6, 2);
// First index has ungrouped widgets, rest are groups
// First item in each real group is group label
QVector<QVector<QPair<QWidget *, QWidget *>>> widgets;
QHash<QString, int> groupIndexMap;
QHash<QString, QPair<QWidget *, QWidget *>> optionToWidgetsMap;
QHash<QString, QJsonArray> conditionMap;
QHash<QWidget *, QWidget *> conditionalWidgetMap;
QHash<QString, QString> optionToGroupMap;
int rowIndex[2] = {0, 0};
widgets.append(QVector<QPair<QWidget *, QWidget *>>()); widgets.append(QVector<QPair<QWidget *, QWidget *>>());
for (const auto &group : groups) { const QStringList &groupIds = groups.keys();
for (const QString &groupId : groupIds) {
if (!advanced && !isSimpleGroup(groupId))
continue;
const auto &group = groups.value(groupId);
const QString name = group.toObject().value("name").toString(); const QString name = group.toObject().value("name").toString();
const QJsonArray items = group.toObject().value("items").toArray(); const QJsonArray items = group.toObject().value("items").toArray();
for (const auto &item : items) for (const auto &item : items)
optionToGroupMap.insert(item.toString(), name); optionToGroupMap.insert(item.toString(), name);
auto groupLabel = new QLabel(name, optionsAreaContents); auto groupLabel = new QLabel(name, contentWidget);
QFont labelFont = groupLabel->font(); QFont labelFont = groupLabel->font();
labelFont.setBold(true); labelFont.setBold(true);
groupLabel->setFont(labelFont); groupLabel->setFont(labelFont);
@@ -442,8 +493,11 @@ void ItemLibraryAssetImportDialog::createTab(const QString &tabLabel, int option
groupIndexMap.insert(name, widgets.size() - 1); groupIndexMap.insert(name, widgets.size() - 1);
} }
QJsonObject &options = m_importOptions[optionsIndex];
const auto optKeys = options.keys(); const auto optKeys = options.keys();
for (const auto &optKey : optKeys) { for (const auto &optKey : optKeys) {
if (!advanced && !isSimpleOption(optKey))
continue;
QJsonObject optObj = options.value(optKey).toObject(); QJsonObject optObj = options.value(optKey).toObject();
const QString optName = optObj.value("name").toString(); const QString optName = optObj.value("name").toString();
const QString optDesc = optObj.value("description").toString(); const QString optDesc = optObj.value("description").toString();
@@ -452,24 +506,41 @@ void ItemLibraryAssetImportDialog::createTab(const QString &tabLabel, int option
QJsonValue optValue = optObj.value("value"); QJsonValue optValue = optObj.value("value");
QJsonArray conditions = optObj.value("conditions").toArray(); QJsonArray conditions = optObj.value("conditions").toArray();
auto *optLabel = new QLabel(optionsAreaContents); auto *optLabel = new QLabel(contentWidget);
optLabel->setText(optName); optLabel->setText(optName);
optLabel->setToolTip(optDesc); optLabel->setToolTip(optDesc);
QWidget *optControl = nullptr; QWidget *optControl = nullptr;
if (optType == "Boolean") { if (optType == "Boolean") {
auto *optCheck = new QCheckBox(optionsAreaContents); auto *optCheck = new QCheckBox(contentWidget);
optCheck->setChecked(optValue.toBool()); optCheck->setChecked(optValue.toBool());
optControl = optCheck; optControl = optCheck;
QObject::connect(optCheck, &QCheckBox::toggled, if (advanced) {
[this, optCheck, optKey, optionsIndex]() { QObject::connect(optCheck, &QCheckBox::toggled, this,
QJsonObject optObj = m_importOptions[optionsIndex].value(optKey).toObject(); [this, optCheck, optKey, optionsIndex]() {
QJsonValue value(optCheck->isChecked()); QJsonObject optObj = m_importOptions[optionsIndex].value(optKey).toObject();
optObj.insert("value", value); QJsonValue value(optCheck->isChecked());
m_importOptions[optionsIndex].insert(optKey, optObj); optObj.insert("value", value);
}); m_importOptions[optionsIndex].insert(optKey, optObj);
});
} else {
// Simple options also exist in advanced, so don't connect simple controls directly
// to import options. Connect them instead to corresponding advanced controls.
auto *advCheck = qobject_cast<QCheckBox *>(
m_labelToControlWidgetMaps[optionsIndex].value(optKey));
if (advCheck) {
QObject::connect(optCheck, &QCheckBox::toggled, this, [optCheck, advCheck]() {
if (advCheck->isChecked() != optCheck->isChecked())
advCheck->setChecked(optCheck->isChecked());
});
QObject::connect(advCheck, &QCheckBox::toggled, this, [optCheck, advCheck]() {
if (advCheck->isChecked() != optCheck->isChecked())
optCheck->setChecked(advCheck->isChecked());
});
}
}
} else if (optType == "Real") { } else if (optType == "Real") {
auto *optSpin = new QDoubleSpinBox(optionsAreaContents); auto *optSpin = new QDoubleSpinBox(contentWidget);
double min = -999999999.; double min = -999999999.;
double max = 999999999.; double max = 999999999.;
double step = 1.; double step = 1.;
@@ -493,13 +564,31 @@ void ItemLibraryAssetImportDialog::createTab(const QString &tabLabel, int option
optSpin->setSingleStep(step); optSpin->setSingleStep(step);
optSpin->setMinimumWidth(controlMinWidth); optSpin->setMinimumWidth(controlMinWidth);
optControl = optSpin; optControl = optSpin;
QObject::connect(optSpin, QOverload<double>::of(&QDoubleSpinBox::valueChanged), if (advanced) {
[this, optSpin, optKey, optionsIndex]() { QObject::connect(optSpin, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this,
QJsonObject optObj = m_importOptions[optionsIndex].value(optKey).toObject(); [this, optSpin, optKey, optionsIndex]() {
QJsonValue value(optSpin->value()); QJsonObject optObj = m_importOptions[optionsIndex].value(optKey).toObject();
optObj.insert("value", value); QJsonValue value(optSpin->value());
m_importOptions[optionsIndex].insert(optKey, optObj); optObj.insert("value", value);
}); m_importOptions[optionsIndex].insert(optKey, optObj);
});
} else {
auto *advSpin = qobject_cast<QDoubleSpinBox *>(
m_labelToControlWidgetMaps[optionsIndex].value(optKey));
if (advSpin) {
// Connect corresponding advanced control
QObject::connect(optSpin, QOverload<double>::of(&QDoubleSpinBox::valueChanged),
this, [optSpin, advSpin]() {
if (advSpin->value() != optSpin->value())
advSpin->setValue(optSpin->value());
});
QObject::connect(advSpin, QOverload<double>::of(&QDoubleSpinBox::valueChanged),
this, [optSpin, advSpin]() {
if (advSpin->value() != optSpin->value())
optSpin->setValue(advSpin->value());
});
}
}
} else { } else {
qWarning() << __FUNCTION__ << "Unsupported option type:" << optType; qWarning() << __FUNCTION__ << "Unsupported option type:" << optType;
continue; continue;
@@ -515,6 +604,8 @@ void ItemLibraryAssetImportDialog::createTab(const QString &tabLabel, int option
else else
widgets[0].append({optLabel, optControl}); widgets[0].append({optLabel, optControl});
optionToWidgetsMap.insert(optKey, {optLabel, optControl}); optionToWidgetsMap.insert(optKey, {optLabel, optControl});
if (advanced)
m_labelToControlWidgetMaps[optionsIndex].insert(optKey, optControl);
} }
// Handle conditions // Handle conditions
@@ -562,7 +653,7 @@ void ItemLibraryAssetImportDialog::createTab(const QString &tabLabel, int option
else else
conditionalWidgetMap.insert(optCb, conControl); conditionalWidgetMap.insert(optCb, conControl);
QObject::connect( QObject::connect(
optCb, &QCheckBox::toggled, optCb, &QCheckBox::toggled, optCb,
[optCb, conLabel, conControl, mode, enableConditionally]() { [optCb, conLabel, conControl, mode, enableConditionally]() {
enableConditionally(optCb, conLabel, conControl, mode); enableConditionally(optCb, conLabel, conControl, mode);
}); });
@@ -586,7 +677,7 @@ void ItemLibraryAssetImportDialog::createTab(const QString &tabLabel, int option
enableConditionally(optSpin, conLabel, conControl, mode); enableConditionally(optSpin, conLabel, conControl, mode);
QObject::connect( QObject::connect(
optSpin, QOverload<double>::of(&QDoubleSpinBox::valueChanged), optSpin, QOverload<double>::of(&QDoubleSpinBox::valueChanged),
[optSpin, conLabel, conControl, mode, enableConditionally]() { optSpin, [optSpin, conLabel, conControl, mode, enableConditionally]() {
enableConditionally(optSpin, conLabel, conControl, mode); enableConditionally(optSpin, conLabel, conControl, mode);
}); });
} }
@@ -646,8 +737,13 @@ void ItemLibraryAssetImportDialog::createTab(const QString &tabLabel, int option
}; };
if (widgets.size() == 1 && widgets[0].isEmpty()) { if (widgets.size() == 1 && widgets[0].isEmpty()) {
layout->addWidget(new QLabel(tr("No options available for this type."), if (advanced) {
optionsAreaContents), 0, 0, 2, 7, Qt::AlignCenter); layout->addWidget(new QLabel(tr("No options available for this type."),
contentWidget), 0, 0, 2, 7, Qt::AlignCenter);
} else {
layout->addWidget(new QLabel(tr("No simple options available for this type."),
contentWidget), 0, 0, 2, 7, Qt::AlignCenter);
}
incrementColIndex(0); incrementColIndex(0);
incrementColIndex(0); incrementColIndex(0);
} }
@@ -663,7 +759,7 @@ void ItemLibraryAssetImportDialog::createTab(const QString &tabLabel, int option
for (int j = 1; j < groupWidgets.size(); ++j) for (int j = 1; j < groupWidgets.size(); ++j)
insertOptionToLayout(col, groupWidgets[j]); insertOptionToLayout(col, groupWidgets[j]);
// Add a separator line after each group // Add a separator line after each group
auto *separator = new QFrame(optionsAreaContents); auto *separator = new QFrame(contentWidget);
separator->setMaximumHeight(1); separator->setMaximumHeight(1);
separator->setFrameShape(QFrame::HLine); separator->setFrameShape(QFrame::HLine);
separator->setFrameShadow(QFrame::Sunken); separator->setFrameShadow(QFrame::Sunken);
@@ -681,38 +777,56 @@ void ItemLibraryAssetImportDialog::createTab(const QString &tabLabel, int option
} }
int optionRows = qMax(rowIndex[0], rowIndex[1]); int optionRows = qMax(rowIndex[0], rowIndex[1]);
m_optionsRows = qMax(m_optionsRows, optionRows); int &globalOptionRows = advanced ? m_advancedData.optionsRows : m_simpleData.optionsRows;
m_optionsHeight = qMax(rowHeight * optionRows + 16, m_optionsHeight); int &globalOptionsHeight = advanced ? m_advancedData.optionsHeight : m_simpleData.optionsHeight;
layout->setContentsMargins(8, 8, 8, 8); globalOptionRows = qMax(globalOptionRows, optionRows);
optionsAreaContents->setContentsMargins(0, 0, 0, 0); globalOptionsHeight = qMax(rowHeight * optionRows + 20, globalOptionsHeight);
optionsAreaContents->setLayout(layout); layout->setContentsMargins(8, 8, 8, 0);
optionsAreaContents->setMinimumWidth(
(checkBoxColWidth + labelMinWidth + controlMinWidth) * 2 + columnSpacing);
optionsAreaContents->setObjectName("optionsAreaContents"); // For stylesheet
optionsArea->setWidget(optionsAreaContents); return layout;
optionsArea->setStyleSheet("QScrollArea {background-color: transparent}");
optionsAreaContents->setStyleSheet(
"QWidget#optionsAreaContents {background-color: transparent}");
ui->tabWidget->addTab(optionsArea, tr("%1 options").arg(tabLabel));
} }
void ItemLibraryAssetImportDialog::updateUi() void ItemLibraryAssetImportDialog::updateUi()
{ {
auto optionsArea = qobject_cast<QScrollArea *>(ui->tabWidget->currentWidget()); auto optionsArea = qobject_cast<QScrollArea *>(ui->tabWidget->currentWidget());
if (optionsArea) { if (optionsArea) {
int optionsHeight = m_advancedMode ? m_advancedData.optionsHeight
: m_simpleData.optionsHeight;
ui->tabWidget->setMaximumHeight(optionsHeight + ui->tabWidget->tabBar()->height() + 10);
auto optionsAreaContents = optionsArea->widget(); auto optionsAreaContents = optionsArea->widget();
int scrollBarWidth = optionsArea->verticalScrollBar()->isVisible() int scrollBarWidth = optionsArea->verticalScrollBar()->isVisible()
? optionsArea->verticalScrollBar()->width() : 0; ? optionsArea->verticalScrollBar()->width() : 0;
optionsAreaContents->resize(optionsArea->contentsRect().width()
- scrollBarWidth - 8, m_optionsHeight); optionsAreaContents->resize(optionsArea->contentsRect().width() - scrollBarWidth - 8,
optionsHeight);
resize(width(), m_dialogHeight);
} }
} }
bool ItemLibraryAssetImportDialog::isSimpleGroup(const QString &id)
{
static QStringList simpleGroups {
"globalScale"
};
return simpleGroups.contains(id);
}
bool ItemLibraryAssetImportDialog::isSimpleOption(const QString &id)
{
static QStringList simpleOptions {
"globalScale",
"globalScaleValue"
};
return simpleOptions.contains(id);
}
void ItemLibraryAssetImportDialog::resizeEvent(QResizeEvent *event) void ItemLibraryAssetImportDialog::resizeEvent(QResizeEvent *event)
{ {
Q_UNUSED(event) m_dialogHeight = event->size().height();
updateUi(); updateUi();
} }
@@ -801,4 +915,27 @@ void ItemLibraryAssetImportDialog::onClose()
} }
} }
void ItemLibraryAssetImportDialog::toggleAdvanced()
{
m_advancedMode = !m_advancedMode;
for (const auto &widget : qAsConst(m_simpleData.contentWidgets)) {
if (widget)
widget->setVisible(!m_advancedMode);
}
for (const auto &widget : qAsConst(m_advancedData.contentWidgets)) {
if (widget)
widget->setVisible(m_advancedMode);
}
if (m_advancedMode)
ui->advancedSettingsButton->setText(tr("Hide Advanced Options"));
else
ui->advancedSettingsButton->setText(tr("Show All Options"));
int diff = qMin(300, m_advancedData.optionsHeight - m_simpleData.optionsHeight);
m_dialogHeight = qMax(350, m_dialogHeight + (m_advancedMode ? diff : -diff));
updateUi();
}
} }

View File

@@ -31,6 +31,10 @@
#include <QJsonObject> #include <QJsonObject>
#include <QSet> #include <QSet>
QT_BEGIN_NAMESPACE
class QGridLayout;
QT_END_NAMESPACE
namespace Utils { namespace Utils {
class OutputFormatter; class OutputFormatter;
} }
@@ -76,21 +80,37 @@ private:
void onImportNearlyFinished(); void onImportNearlyFinished();
void onImportFinished(); void onImportFinished();
void onClose(); void onClose();
void toggleAdvanced();
void createTab(const QString &tabLabel, int optionsIndex, const QJsonObject &groups); void createTab(const QString &tabLabel, int optionsIndex, const QJsonObject &groups);
QGridLayout *createOptionsGrid(QWidget *contentWidget, bool advanced, int optionsIndex,
const QJsonObject &groups);
void updateUi(); void updateUi();
bool isSimpleGroup(const QString &id);
bool isSimpleOption(const QString &id);
Ui::ItemLibraryAssetImportDialog *ui = nullptr; Ui::ItemLibraryAssetImportDialog *ui = nullptr;
Utils::OutputFormatter *m_outputFormatter = nullptr; Utils::OutputFormatter *m_outputFormatter = nullptr;
struct OptionsData
{
int optionsRows = 0;
int optionsHeight = 0;
QList<QWidget *> contentWidgets; // Tab content widgets
};
QStringList m_quick3DFiles; QStringList m_quick3DFiles;
QString m_quick3DImportPath; QString m_quick3DImportPath;
ItemLibraryAssetImporter m_importer; ItemLibraryAssetImporter m_importer;
QVector<QJsonObject> m_importOptions; QVector<QJsonObject> m_importOptions;
QHash<QString, int> m_extToImportOptionsMap; QHash<QString, int> m_extToImportOptionsMap;
int m_optionsHeight = 0;
int m_optionsRows = 0;
QSet<QString> m_preselectedFilesForOverwrite; QSet<QString> m_preselectedFilesForOverwrite;
bool m_closeOnFinish = true; bool m_closeOnFinish = true;
QList<QHash<QString, QWidget *>> m_labelToControlWidgetMaps;
OptionsData m_simpleData;
OptionsData m_advancedData;
bool m_advancedMode = false;
int m_dialogHeight = 350;
}; };
} }

View File

@@ -6,68 +6,118 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>631</width> <width>630</width>
<height>750</height> <height>350</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Asset Import</string> <string>Asset Import</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QFormLayout" name="formLayout">
<item> <item row="0" column="0" colspan="2">
<widget class="QTabWidget" name="tabWidget"> <layout class="QVBoxLayout" name="verticalLayout">
<property name="sizePolicy"> <item>
<sizepolicy hsizetype="Expanding" vsizetype="Expanding"> <widget class="QTabWidget" name="tabWidget">
<horstretch>0</horstretch> <property name="sizePolicy">
<verstretch>2</verstretch> <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
</sizepolicy> <horstretch>0</horstretch>
</property> <verstretch>2</verstretch>
<property name="currentIndex"> </sizepolicy>
<number>0</number> </property>
</property> <property name="currentIndex">
<property name="tabBarAutoHide"> <number>0</number>
<bool>false</bool> </property>
</property> <property name="tabBarAutoHide">
<widget class="QWidget" name="modelOptionsTab"> <bool>false</bool>
<attribute name="title"> </property>
<string>Import Options</string> <widget class="QWidget" name="modelOptionsTab">
</attribute> <attribute name="title">
</widget> <string>Import Options</string>
</widget> </attribute>
</item> </widget>
<item> </widget>
<widget class="QPlainTextEdit" name="plainTextEdit"> </item>
<property name="sizePolicy"> <item>
<sizepolicy hsizetype="Expanding" vsizetype="Expanding"> <layout class="QHBoxLayout" name="horizontalLayout">
<horstretch>0</horstretch> <item>
<verstretch>1</verstretch> <spacer name="horizontalSpacer">
</sizepolicy> <property name="orientation">
</property> <enum>Qt::Horizontal</enum>
<property name="readOnly"> </property>
<bool>true</bool> <property name="sizeHint" stdset="0">
</property> <size>
</widget> <width>80</width>
</item> <height>20</height>
<item> </size>
<widget class="QLabel" name="progressLabel"> </property>
<property name="text"> </spacer>
<string/> </item>
</property> <item>
</widget> <widget class="QPushButton" name="advancedSettingsButton">
</item> <property name="text">
<item> <string>Show All Settings</string>
<widget class="QProgressBar" name="progressBar"> </property>
<property name="value"> </widget>
<number>0</number> </item>
</property> <item>
</widget> <spacer name="horizontalSpacer_2">
</item> <property name="orientation">
<item> <enum>Qt::Horizontal</enum>
<widget class="QDialogButtonBox" name="buttonBox"> </property>
<property name="standardButtons"> <property name="sizeType">
<set>QDialogButtonBox::Close|QDialogButtonBox::Ok</set> <enum>QSizePolicy::Fixed</enum>
</property> </property>
</widget> <property name="sizeHint" stdset="0">
<size>
<width>8</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QPlainTextEdit" name="plainTextEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="progressLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Close|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item> </item>
</layout> </layout>
</widget> </widget>

View File

@@ -62,7 +62,7 @@ ProjectExplorer::Target *activeTarget(ProjectExplorer::Project *project)
} }
} // namespace } // namespace
class ImageCacheData class ItemLibraryView::ImageCacheData
{ {
public: public:
Sqlite::Database database{Utils::PathString{ Sqlite::Database database{Utils::PathString{
@@ -71,7 +71,7 @@ public:
Sqlite::LockingMode::Normal}; Sqlite::LockingMode::Normal};
ImageCacheStorage<Sqlite::Database> storage{database}; ImageCacheStorage<Sqlite::Database> storage{database};
ImageCacheConnectionManager connectionManager; ImageCacheConnectionManager connectionManager;
ImageCacheCollector collector{connectionManager}; ImageCacheCollector collector{connectionManager, QSize{300, 300}, QSize{600, 600}};
ImageCacheFontCollector fontCollector; ImageCacheFontCollector fontCollector;
ImageCacheGenerator generator{collector, storage}; ImageCacheGenerator generator{collector, storage};
ImageCacheGenerator fontGenerator{fontCollector, storage}; ImageCacheGenerator fontGenerator{fontCollector, storage};
@@ -177,7 +177,7 @@ void ItemLibraryView::usedImportsChanged(const QList<Import> &usedImports)
m_widget->updateUsedImports(usedImports); m_widget->updateUsedImports(usedImports);
} }
ImageCacheData *ItemLibraryView::imageCacheData() ItemLibraryView::ImageCacheData *ItemLibraryView::imageCacheData()
{ {
std::call_once(imageCacheFlag, [this]() { std::call_once(imageCacheFlag, [this]() {
m_imageCacheData = std::make_unique<ImageCacheData>(); m_imageCacheData = std::make_unique<ImageCacheData>();

View File

@@ -34,7 +34,6 @@
namespace QmlDesigner { namespace QmlDesigner {
class ItemLibraryWidget; class ItemLibraryWidget;
class ImageCacheData;
class AsynchronousImageCache; class AsynchronousImageCache;
class ItemLibraryView : public AbstractView class ItemLibraryView : public AbstractView
@@ -65,6 +64,7 @@ protected:
void updateImports(); void updateImports();
private: private:
class ImageCacheData;
ImageCacheData *imageCacheData(); ImageCacheData *imageCacheData();
std::once_flag imageCacheFlag; std::once_flag imageCacheFlag;

View File

@@ -273,8 +273,10 @@ void ItemLibraryWidget::handleAddImport(int index)
Import import = m_addModuleModel->getImportAt(index); Import import = m_addModuleModel->getImportAt(index);
if (import.isLibraryImport() && (import.url().startsWith("QtQuick") if (import.isLibraryImport() && (import.url().startsWith("QtQuick")
|| import.url().startsWith("SimulinkConnector"))) { || import.url().startsWith("SimulinkConnector"))) {
QString importStr = import.toImportString();
importStr.replace(' ', '-');
QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_IMPORT_ADDED QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_IMPORT_ADDED
+ import.toImportString()); + importStr);
} }
QList<Import> imports; QList<Import> imports;

View File

@@ -28,30 +28,87 @@
#include <utils/stylehelper.h> #include <utils/stylehelper.h>
#include <theme.h> #include <theme.h>
#include <QAction>
#include <QBoxLayout> #include <QBoxLayout>
#include <QLabel> #include <QLabel>
#include <QPushButton> #include <QStyle>
#include <QToolButton>
namespace QmlDesigner { namespace QmlDesigner {
LineEdit::LineEdit(QWidget *parent)
: QLineEdit(parent)
{
clearButton = new QToolButton(this);
const QString fontName = "qtds_propertyIconFont.ttf";
const int searchIconSize = 16;
const int clearIconSize = 12;
const QColor iconColor(QmlDesigner::Theme::getColor(QmlDesigner::Theme::DSiconColor));
const QIcon searchIcon
= Utils::StyleHelper::getIconFromIconFont(fontName,
QmlDesigner::Theme::getIconUnicode(
QmlDesigner::Theme::Icon::search),
searchIconSize,
searchIconSize,
iconColor);
const QIcon clearIcon
= Utils::StyleHelper::getIconFromIconFont(fontName,
QmlDesigner::Theme::getIconUnicode(
QmlDesigner::Theme::Icon::closeCross),
clearIconSize,
clearIconSize,
iconColor);
addAction(searchIcon, QLineEdit::LeadingPosition);
clearButton->setIcon(clearIcon);
clearButton->setIconSize(QSize(clearIconSize, clearIconSize));
clearButton->setCursor(Qt::ArrowCursor);
clearButton->hide();
clearButton->setStyleSheet(Theme::replaceCssColors(
QString("QToolButton { border: none; padding: 0px; }"
"QToolButton:hover { background: creatorTheme.DScontrolBackgroundHover; }")));
setClearButtonEnabled(false);
connect(clearButton, &QToolButton::clicked, this, &QLineEdit::clear);
connect(this, &QLineEdit::textChanged, this, &LineEdit::updateClearButton);
int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
setStyleSheet(QString("QLineEdit { padding-right: %1px; } ")
.arg(clearButton->sizeHint().width() + frameWidth + 8));
setFixedHeight(29);
}
void LineEdit::resizeEvent(QResizeEvent *)
{
QSize hint = clearButton->sizeHint();
int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
clearButton->move(rect().right() - frameWidth - hint.width() - 3,
(rect().bottom() + 1 - hint.height()) / 2);
}
void LineEdit::updateClearButton(const QString& text)
{
clearButton->setVisible(!text.isEmpty());
}
NavigatorSearchWidget::NavigatorSearchWidget(QWidget *parent) NavigatorSearchWidget::NavigatorSearchWidget(QWidget *parent)
: QWidget(parent) : QWidget(parent)
{ {
auto layout = new QBoxLayout(QBoxLayout::LeftToRight); auto layout = new QBoxLayout(QBoxLayout::LeftToRight);
layout->setSpacing(0);
layout->setContentsMargins(5, 5, 5, 3);
setLayout(layout); setLayout(layout);
const QString fontName = "qtds_propertyIconFont.ttf"; m_textField = new LineEdit;
const int iconSize = 15; m_textField->setPlaceholderText(tr("Search"));
const QColor iconColor(QmlDesigner::Theme::getColor(QmlDesigner::Theme::IconsBaseColor));
const QIcon searchIcon = Utils::StyleHelper::getIconFromIconFont(
fontName, QmlDesigner::Theme::getIconUnicode(QmlDesigner::Theme::Icon::search),
iconSize, iconSize, iconColor);
m_textField = new QLineEdit;
m_textField->setPlaceholderText(tr("Filter"));
m_textField->setFrame(false); m_textField->setFrame(false);
m_textField->setClearButtonEnabled(true);
m_textField->addAction(searchIcon, QLineEdit::LeadingPosition);
connect(m_textField, &QLineEdit::textChanged, this, &NavigatorSearchWidget::textChanged); connect(m_textField, &QLineEdit::textChanged, this, &NavigatorSearchWidget::textChanged);

View File

@@ -27,8 +27,27 @@
#include <QLineEdit> #include <QLineEdit>
class QToolButton;
namespace QmlDesigner { namespace QmlDesigner {
class LineEdit : public QLineEdit
{
Q_OBJECT
public:
LineEdit(QWidget *parent = nullptr);
protected:
void resizeEvent(QResizeEvent *);
private slots:
void updateClearButton(const QString &text);
private:
QToolButton *clearButton;
};
class NavigatorSearchWidget : public QWidget class NavigatorSearchWidget : public QWidget
{ {
Q_OBJECT Q_OBJECT
@@ -42,8 +61,7 @@ signals:
void textChanged(const QString &text); void textChanged(const QString &text);
private: private:
LineEdit *m_textField;
QLineEdit *m_textField;
}; };
} //QmlDesigner } //QmlDesigner

View File

@@ -69,6 +69,10 @@ NavigatorWidget::NavigatorWidget(NavigatorView *view)
toolBar->setParent(this); toolBar->setParent(this);
layout->addWidget(toolBar); layout->addWidget(toolBar);
m_searchWidget = new NavigatorSearchWidget();
connect(m_searchWidget, &NavigatorSearchWidget::textChanged, this, &NavigatorWidget::textFilterChanged);
layout->addWidget(m_searchWidget);
layout->addWidget(m_treeView); layout->addWidget(m_treeView);
setLayout(layout); setLayout(layout);
@@ -161,10 +165,6 @@ QToolBar *NavigatorWidget::createToolBar()
for (auto toolButton : buttons) for (auto toolButton : buttons)
toolBar->addWidget(toolButton); toolBar->addWidget(toolButton);
m_searchWidget = new NavigatorSearchWidget();
connect(m_searchWidget, &NavigatorSearchWidget::textChanged, this, &NavigatorWidget::textFilterChanged);
toolBar->addWidget(m_searchWidget);
return toolBar; return toolBar;
} }

View File

@@ -68,7 +68,8 @@ void PreviewImageTooltip::setImage(const QImage &image, bool scale)
if (scale) { if (scale) {
m_ui->imageLabel->setPixmap(pm.scaled(m_ui->imageLabel->width(), m_ui->imageLabel->setPixmap(pm.scaled(m_ui->imageLabel->width(),
m_ui->imageLabel->height(), m_ui->imageLabel->height(),
Qt::KeepAspectRatio)); Qt::KeepAspectRatio,
Qt::SmoothTransformation));
} else { } else {
m_ui->imageLabel->setPixmap(pm); m_ui->imageLabel->setPixmap(pm);
} }

View File

@@ -61,8 +61,12 @@ QString fileToString(const QString &filename)
} // namespace } // namespace
ImageCacheCollector::ImageCacheCollector(ImageCacheConnectionManager &connectionManager, ImageCacheCollector::ImageCacheCollector(ImageCacheConnectionManager &connectionManager,
QSize captureImageMinimumSize,
QSize captureImageMaximumSize,
ImageCacheCollectorNullImageHandling nullImageHandling) ImageCacheCollectorNullImageHandling nullImageHandling)
: m_connectionManager{connectionManager} : m_connectionManager{connectionManager}
, captureImageMinimumSize{captureImageMinimumSize}
, captureImageMaximumSize{captureImageMaximumSize}
, nullImageHandling{nullImageHandling} , nullImageHandling{nullImageHandling}
{} {}
@@ -76,6 +80,8 @@ void ImageCacheCollector::start(Utils::SmallStringView name,
{ {
RewriterView rewriterView{RewriterView::Amend, nullptr}; RewriterView rewriterView{RewriterView::Amend, nullptr};
NodeInstanceView nodeInstanceView{m_connectionManager}; NodeInstanceView nodeInstanceView{m_connectionManager};
nodeInstanceView.setCaptureImageMinimumAndMaximumSize(captureImageMinimumSize,
captureImageMaximumSize);
const QString filePath{name}; const QString filePath{name};
std::unique_ptr<Model> model{QmlDesigner::Model::create("QtQuick/Item", 2, 1)}; std::unique_ptr<Model> model{QmlDesigner::Model::create("QtQuick/Item", 2, 1)};
@@ -107,7 +113,10 @@ void ImageCacheCollector::start(Utils::SmallStringView name,
|| !image.isNull()) { || !image.isNull()) {
QSize smallImageSize = image.size().scaled(QSize{96, 96}.boundedTo(image.size()), QSize smallImageSize = image.size().scaled(QSize{96, 96}.boundedTo(image.size()),
Qt::KeepAspectRatio); Qt::KeepAspectRatio);
QImage smallImage = image.isNull() ? QImage{} : image.scaled(smallImageSize); QImage smallImage = image.isNull() ? QImage{}
: image.scaled(smallImageSize,
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation);
captureCallback(image, smallImage); captureCallback(image, smallImage);
} }
}; };

View File

@@ -51,6 +51,8 @@ class ImageCacheCollector final : public ImageCacheCollectorInterface
{ {
public: public:
ImageCacheCollector(ImageCacheConnectionManager &connectionManager, ImageCacheCollector(ImageCacheConnectionManager &connectionManager,
QSize captureImageMinimumSize,
QSize captureImageMaximumSize,
ImageCacheCollectorNullImageHandling nullImageHandling = {}); ImageCacheCollectorNullImageHandling nullImageHandling = {});
~ImageCacheCollector(); ~ImageCacheCollector();
@@ -75,6 +77,8 @@ public:
private: private:
ImageCacheConnectionManager &m_connectionManager; ImageCacheConnectionManager &m_connectionManager;
QPointer<ProjectExplorer::Target> m_target; QPointer<ProjectExplorer::Target> m_target;
QSize captureImageMinimumSize;
QSize captureImageMaximumSize;
ImageCacheCollectorNullImageHandling nullImageHandling{}; ImageCacheCollectorNullImageHandling nullImageHandling{};
}; };

View File

@@ -161,6 +161,13 @@ public:
m_crashCallback = std::move(crashCallback); m_crashCallback = std::move(crashCallback);
} }
void setCaptureImageMinimumAndMaximumSize(QSize captureImageMinimumSize,
QSize captureImageMaximumSize)
{
m_captureImageMinimumSize = captureImageMinimumSize;
m_captureImageMaximumSize = captureImageMaximumSize;
}
void startNanotrace(); void startNanotrace();
void endNanotrace(); void endNanotrace();
@@ -237,6 +244,7 @@ private: // functions
void updateWatcher(const QString &path); void updateWatcher(const QString &path);
void handleShaderChanges(); void handleShaderChanges();
void handleQsbProcessExit(Utils::QtcProcess *qsbProcess, const QString &shader); void handleQsbProcessExit(Utils::QtcProcess *qsbProcess, const QString &shader);
void updateQsbPathToFilterMap();
void updateRotationBlocks(); void updateRotationBlocks();
void maybeResetOnPropertyChange(const PropertyName &name, const ModelNode &node, void maybeResetOnPropertyChange(const PropertyName &name, const ModelNode &node,
PropertyChangeFlags flags); PropertyChangeFlags flags);
@@ -288,9 +296,12 @@ private:
QTimer m_generateQsbFilesTimer; QTimer m_generateQsbFilesTimer;
Utils::FilePath m_qsbPath; Utils::FilePath m_qsbPath;
QSet<QString> m_pendingUpdateDirs; QSet<QString> m_pendingUpdateDirs;
QSet<QString> m_pendingQsbTargets; QHash<QString, bool> m_qsbTargets; // Value indicates if target is pending qsb generation
int m_remainingQsbTargets; QHash<QString, QStringList> m_qsbPathToFilterMap;
int m_remainingQsbTargets = 0;
QTimer m_rotBlockTimer; QTimer m_rotBlockTimer;
QSize m_captureImageMinimumSize{150, 150};
QSize m_captureImageMaximumSize{1000, 1000};
}; };
} // namespace ProxyNodeInstanceView } // namespace ProxyNodeInstanceView

View File

@@ -98,6 +98,7 @@
#include <projectexplorer/target.h> #include <projectexplorer/target.h>
#include <qmlprojectmanager/qmlmultilanguageaspect.h> #include <qmlprojectmanager/qmlmultilanguageaspect.h>
#include <qmlprojectmanager/qmlproject.h>
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
@@ -113,6 +114,8 @@
#include <QDirIterator> #include <QDirIterator>
#include <QFileSystemWatcher> #include <QFileSystemWatcher>
#include <QScopedPointer> #include <QScopedPointer>
#include <QThread>
#include <QApplication>
enum { enum {
debug = false debug = false
@@ -174,9 +177,6 @@ NodeInstanceView::NodeInstanceView(ConnectionManagerInterface &connectionManager
m_generateQsbFilesTimer.setInterval(100); m_generateQsbFilesTimer.setInterval(100);
QObject::connect(&m_generateQsbFilesTimer, &QTimer::timeout, [this] { QObject::connect(&m_generateQsbFilesTimer, &QTimer::timeout, [this] {
handleShaderChanges(); handleShaderChanges();
if (m_qsbPath.isEmpty() || m_remainingQsbTargets <= 0)
m_resetTimer.start();
}); });
connect(m_fileSystemWatcher, &QFileSystemWatcher::directoryChanged, connect(m_fileSystemWatcher, &QFileSystemWatcher::directoryChanged,
@@ -196,8 +196,12 @@ NodeInstanceView::NodeInstanceView(ConnectionManagerInterface &connectionManager
}); });
connect(m_fileSystemWatcher, &QFileSystemWatcher::fileChanged, [this](const QString &path) { connect(m_fileSystemWatcher, &QFileSystemWatcher::fileChanged, [this](const QString &path) {
m_pendingQsbTargets.insert(path); if (m_qsbTargets.contains(path)) {
m_generateQsbFilesTimer.start(); m_qsbTargets.insert(path, true);
m_generateQsbFilesTimer.start();
} else if (m_remainingQsbTargets <= 0) {
m_resetTimer.start();
}
}); });
m_rotBlockTimer.setSingleShot(true); m_rotBlockTimer.setSingleShot(true);
@@ -277,7 +281,15 @@ void NodeInstanceView::modelAttached(Model *model)
activateState(newStateInstance); activateState(newStateInstance);
} }
updateWatcher({}); // If model gets attached on non-main thread of the application, do not attempt to monitor
// file changes. Such models are typically short lived for specific purpose, and timers
// will not work at all, if the thread is not based on QThread.
if (QThread::currentThread() == qApp->thread()) {
m_generateQsbFilesTimer.stop();
m_qsbTargets.clear();
updateQsbPathToFilterMap();
updateWatcher({});
}
} }
void NodeInstanceView::modelAboutToBeDetached(Model * model) void NodeInstanceView::modelAboutToBeDetached(Model * model)
@@ -303,6 +315,9 @@ void NodeInstanceView::modelAboutToBeDetached(Model * model)
m_pendingUpdateDirs.clear(); m_pendingUpdateDirs.clear();
m_fileSystemWatcher->removePaths(m_fileSystemWatcher->directories()); m_fileSystemWatcher->removePaths(m_fileSystemWatcher->directories());
m_fileSystemWatcher->removePaths(m_fileSystemWatcher->files()); m_fileSystemWatcher->removePaths(m_fileSystemWatcher->files());
m_generateQsbFilesTimer.stop();
m_qsbTargets.clear();
} }
void NodeInstanceView::handleCrash() void NodeInstanceView::handleCrash()
@@ -1123,24 +1138,27 @@ CreateSceneCommand NodeInstanceView::createCreateSceneCommand()
if (stateNode.isValid() && stateNode.metaInfo().isSubclassOf("QtQuick.State", 1, 0)) if (stateNode.isValid() && stateNode.metaInfo().isSubclassOf("QtQuick.State", 1, 0))
stateInstanceId = stateNode.internalId(); stateInstanceId = stateNode.internalId();
return CreateSceneCommand(instanceContainerList, return CreateSceneCommand(
reparentContainerList, instanceContainerList,
idContainerList, reparentContainerList,
valueContainerList, idContainerList,
bindingContainerList, valueContainerList,
auxiliaryContainerVector, bindingContainerList,
importVector, auxiliaryContainerVector,
mockupTypesVector, importVector,
model()->fileUrl(), mockupTypesVector,
model()->fileUrl(),
#ifndef QMLDESIGNER_TEST #ifndef QMLDESIGNER_TEST
QUrl::fromLocalFile(QmlDesigner::DocumentManager::currentResourcePath() QUrl::fromLocalFile(
.toFileInfo().absoluteFilePath()), QmlDesigner::DocumentManager::currentResourcePath().toFileInfo().absoluteFilePath()),
#else #else
QUrl::fromLocalFile(QFileInfo(model()->fileUrl().toLocalFile()).absolutePath()), QUrl::fromLocalFile(QFileInfo(model()->fileUrl().toLocalFile()).absolutePath()),
#endif #endif
m_edit3DToolStates[model()->fileUrl()], m_edit3DToolStates[model()->fileUrl()],
lastUsedLanguage, lastUsedLanguage,
stateInstanceId); m_captureImageMinimumSize,
m_captureImageMaximumSize,
stateInstanceId);
} }
ClearSceneCommand NodeInstanceView::createClearSceneCommand() const ClearSceneCommand NodeInstanceView::createClearSceneCommand() const
@@ -1488,9 +1506,6 @@ void NodeInstanceView::setTarget(ProjectExplorer::Target *newTarget)
} }
} }
m_generateQsbFilesTimer.stop();
m_pendingQsbTargets.clear();
m_remainingQsbTargets = 0;
restartProcess(); restartProcess();
} }
} }
@@ -1885,12 +1900,18 @@ void NodeInstanceView::updateWatcher(const QString &path)
QStringList oldDirs; QStringList oldDirs;
QStringList newFiles; QStringList newFiles;
QStringList newDirs; QStringList newDirs;
QStringList qsbFiles;
#ifndef QMLDESIGNER_TEST
const QString projPath = QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath().toString();
#else
const QString projPath = QFileInfo(model()->fileUrl().toLocalFile()).absolutePath();
#endif
const QStringList files = m_fileSystemWatcher->files(); const QStringList files = m_fileSystemWatcher->files();
const QStringList directories = m_fileSystemWatcher->directories(); const QStringList directories = m_fileSystemWatcher->directories();
if (path.isEmpty()) { if (path.isEmpty()) {
// Do full update // Do full update
rootPath = QFileInfo(model()->fileUrl().toLocalFile()).absolutePath(); rootPath = projPath;
if (!directories.isEmpty()) if (!directories.isEmpty())
m_fileSystemWatcher->removePaths(directories); m_fileSystemWatcher->removePaths(directories);
if (!files.isEmpty()) if (!files.isEmpty())
@@ -1916,12 +1937,47 @@ void NodeInstanceView::updateWatcher(const QString &path)
// Common shader suffixes // Common shader suffixes
static const QStringList filterList {"*.frag", "*.vert", static const QStringList filterList {"*.frag", "*.vert",
"*.glsl", "*.glslv", "*.glslf", "*.glsl", "*.glslv", "*.glslf",
"*.vsh","*.fsh"}; "*.vsh", "*.fsh"};
QDirIterator fileIterator(rootPath, filterList, QDir::Files, QDirIterator::Subdirectories); QDirIterator fileIterator(rootPath, filterList, QDir::Files, QDirIterator::Subdirectories);
while (fileIterator.hasNext()) while (fileIterator.hasNext())
newFiles.append(fileIterator.next()); newFiles.append(fileIterator.next());
// Find out which shader files need qsb files generated for them.
// Go through all configured paths and find files that match the specified filter in that path.
bool generateQsb = false;
QHash<QString, QStringList>::const_iterator it = m_qsbPathToFilterMap.constBegin();
while (it != m_qsbPathToFilterMap.constEnd()) {
if (!it.key().isEmpty() && !it.key().startsWith(rootPath)) {
++it;
continue;
}
QDirIterator qsbIterator(it.key().isEmpty() ? rootPath : it.key(),
it.value(), QDir::Files,
it.key().isEmpty() ? QDirIterator::Subdirectories
: QDirIterator::NoIteratorFlags);
while (qsbIterator.hasNext()) {
QString qsbFile = qsbIterator.next();
if (qsbFile.endsWith(".qsb"))
continue; // Skip any generated files that are caught by wildcards
// Filters may specify shader files with non-default suffixes, so add them to newFiles
if (!newFiles.contains(qsbFile))
newFiles.append(qsbFile);
// Only generate qsb files for newly detected files. This avoids immediately regenerating
// qsb file if it's manually deleted, as directory change triggers calling this method.
if (!oldFiles.contains(qsbFile)) {
m_qsbTargets.insert(qsbFile, true);
generateQsb = true;
}
}
++it;
}
if (oldDirs != newDirs) { if (oldDirs != newDirs) {
if (!oldDirs.isEmpty()) if (!oldDirs.isEmpty())
m_fileSystemWatcher->removePaths(oldDirs); m_fileSystemWatcher->removePaths(oldDirs);
@@ -1934,15 +1990,10 @@ void NodeInstanceView::updateWatcher(const QString &path)
m_fileSystemWatcher->removePaths(oldFiles); m_fileSystemWatcher->removePaths(oldFiles);
if (!newFiles.isEmpty()) if (!newFiles.isEmpty())
m_fileSystemWatcher->addPaths(newFiles); m_fileSystemWatcher->addPaths(newFiles);
for (const auto &newFile : qAsConst(newFiles)) {
if (!oldFiles.contains(newFile))
m_pendingQsbTargets.insert(newFile);
}
if (!m_pendingQsbTargets.isEmpty())
m_generateQsbFilesTimer.start();
} }
if (generateQsb)
m_generateQsbFilesTimer.start();
} }
void NodeInstanceView::handleQsbProcessExit(Utils::QtcProcess *qsbProcess, const QString &shader) void NodeInstanceView::handleQsbProcessExit(Utils::QtcProcess *qsbProcess, const QString &shader)
@@ -1969,51 +2020,102 @@ void NodeInstanceView::handleQsbProcessExit(Utils::QtcProcess *qsbProcess, const
qsbProcess->deleteLater(); qsbProcess->deleteLater();
} }
void NodeInstanceView::handleShaderChanges() void NodeInstanceView::updateQsbPathToFilterMap()
{ {
m_remainingQsbTargets += m_pendingQsbTargets.size(); m_qsbPathToFilterMap.clear();
if (m_currentTarget && !m_qsbPath.isEmpty()) {
const auto bs = qobject_cast<QmlProjectManager::QmlBuildSystem *>(m_currentTarget->buildSystem());
const QStringList shaderToolFiles = bs->shaderToolFiles();
for (const auto &shader : qAsConst(m_pendingQsbTargets)) { #ifndef QMLDESIGNER_TEST
// Run qsb for changed shader file const QString projPath = QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath().toString();
if (!m_qsbPath.isEmpty() && !shader.isEmpty()) { #else
const Utils::FilePath sourceFile = Utils::FilePath::fromString(shader); const QString projPath = QFileInfo(model()->fileUrl().toLocalFile()).absolutePath();
const Utils::FilePath srcPath = sourceFile.absolutePath(); #endif
const Utils::FilePath outPath = Utils::FilePath::fromString(shader + ".qsb"); // Parse ShaderTool files from project configuration.
// Separate files to path and file name (called filter here as it can contain wildcards)
if (!sourceFile.exists() || (outPath.exists() && outPath.lastModified() > sourceFile.lastModified())) { // and group filters by paths. Blank path indicates project-wide file wildcard.
--m_remainingQsbTargets; for (const auto &file : shaderToolFiles) {
continue; int idx = file.lastIndexOf('/');
} QString key;
QString filter;
// Run QSB with same parameters as Qt build does if (idx >= 0) {
// TODO: Parameters should be configurable (QDS-6590) key = projPath + "/" + file.left(idx);
const QStringList args = {"-s", "--glsl", "100 es,120,150", "--hlsl", "50", "--msl", "12", filter = file.mid(idx + 1);
"-o", outPath.toString(), shader};
auto qsbProcess = new Utils::QtcProcess;
qsbProcess->setWorkingDirectory(srcPath);
qsbProcess->setCommand({m_qsbPath, args});
qsbProcess->start();
if (!qsbProcess->waitForStarted()) {
handleQsbProcessExit(qsbProcess, shader);
continue;
}
if (qsbProcess->state() == QProcess::Running) {
connect(qsbProcess, &Utils::QtcProcess::finished,
[thisView = QPointer<NodeInstanceView>(this), qsbProcess, shader]() {
if (thisView)
thisView->handleQsbProcessExit(qsbProcess, shader);
else
qsbProcess->deleteLater();
});
} else { } else {
handleQsbProcessExit(qsbProcess, shader); filter = file;
} }
m_qsbPathToFilterMap[key].append(filter);
} }
} }
}
m_pendingQsbTargets.clear(); void NodeInstanceView::handleShaderChanges()
{
if (!m_currentTarget)
return;
const auto bs = qobject_cast<QmlProjectManager::QmlBuildSystem *>(m_currentTarget->buildSystem());
QStringList baseArgs = bs->shaderToolArgs();
if (baseArgs.isEmpty())
return;
QStringList newShaders;
QHash<QString, bool>::iterator it = m_qsbTargets.begin();
while (it != m_qsbTargets.end()) {
if (it.value()) {
newShaders.append(it.key());
it.value() = false;
}
++it;
}
if (newShaders.isEmpty())
return;
m_remainingQsbTargets += newShaders.size();
for (const auto &shader : qAsConst(newShaders)) {
const Utils::FilePath srcFile = Utils::FilePath::fromString(shader);
const Utils::FilePath srcPath = srcFile.absolutePath();
const Utils::FilePath outPath = Utils::FilePath::fromString(shader + ".qsb");
if (!srcFile.exists()) {
m_qsbTargets.remove(shader);
--m_remainingQsbTargets;
continue;
}
if ((outPath.exists() && outPath.lastModified() > srcFile.lastModified())) {
--m_remainingQsbTargets;
continue;
}
QStringList args = baseArgs;
args.append(outPath.toString());
args.append(shader);
auto qsbProcess = new Utils::QtcProcess;
qsbProcess->setWorkingDirectory(srcPath);
qsbProcess->setCommand({m_qsbPath, args});
qsbProcess->start();
if (!qsbProcess->waitForStarted()) {
handleQsbProcessExit(qsbProcess, shader);
continue;
}
if (qsbProcess->state() == QProcess::Running) {
connect(qsbProcess, &Utils::QtcProcess::finished,
[thisView = QPointer<NodeInstanceView>(this), qsbProcess, shader]() {
if (thisView)
thisView->handleQsbProcessExit(qsbProcess, shader);
else
qsbProcess->deleteLater();
});
} else {
handleQsbProcessExit(qsbProcess, shader);
}
}
} }
void NodeInstanceView::updateRotationBlocks() void NodeInstanceView::updateRotationBlocks()

View File

@@ -353,7 +353,7 @@ void GenerateResource::generateMenuEntry()
// ToDo: move this to QtCreator and add tr to the string then // ToDo: move this to QtCreator and add tr to the string then
auto rccAction = new QAction(QCoreApplication::translate("QmlDesigner::GenerateResource", auto rccAction = new QAction(QCoreApplication::translate("QmlDesigner::GenerateResource",
"Generate RCC Resource File")); "Generate Deployable Package"));
rccAction->setEnabled(ProjectExplorer::SessionManager::startupProject() != nullptr); rccAction->setEnabled(ProjectExplorer::SessionManager::startupProject() != nullptr);
QObject::connect(ProjectExplorer::SessionManager::instance(), QObject::connect(ProjectExplorer::SessionManager::instance(),
&ProjectExplorer::SessionManager::startupProjectChanged, [rccAction]() { &ProjectExplorer::SessionManager::startupProjectChanged, [rccAction]() {

View File

@@ -90,23 +90,23 @@ const char M_VIEW_WORKSPACES[] = "QmlDesigner.Menu.View.Workspaces";
const int MODELNODE_PREVIEW_IMAGE_DIMENSIONS = 150; const int MODELNODE_PREVIEW_IMAGE_DIMENSIONS = 150;
const char EVENT_TIMELINE_ADDED[] = "Timeline Added"; const char EVENT_TIMELINE_ADDED[] = "timelineAdded";
const char EVENT_TRANSITION_ADDED[] = "Transition Added"; const char EVENT_TRANSITION_ADDED[] = "transitionAdded";
const char EVENT_STATE_ADDED[] = "State Added"; const char EVENT_STATE_ADDED[] = "stateAdded";
const char EVENT_CONNECTION_ADDED[] = "Connection Added"; const char EVENT_CONNECTION_ADDED[] = "connectionAdded";
const char EVENT_PROPERTY_ADDED[] = "Property Added"; const char EVENT_PROPERTY_ADDED[] = "propertyAdded";
const char EVENT_ANNOTATION_ADDED[] = "Annotation Added"; const char EVENT_ANNOTATION_ADDED[] = "annotationAdded";
const char EVENT_RESOURCE_IMPORTED[] = "Resource Imported "; const char EVENT_RESOURCE_IMPORTED[] = "resourceImported";
const char EVENT_ACTION_EXECUTED[] = "Action Executed "; const char EVENT_ACTION_EXECUTED[] = "actionExecuted";
const char EVENT_HELP_REQUESTED[] = "Help Requested "; const char EVENT_HELP_REQUESTED[] = "helpRequested";
const char EVENT_IMPORT_ADDED[] = "Import Added "; const char EVENT_IMPORT_ADDED[] = "importAdded:";
const char EVENT_BINDINGEDITOR_OPENED[] = "Binding Editor Opened"; const char EVENT_BINDINGEDITOR_OPENED[] = "bindingEditorOpened";
const char EVENT_RICHTEXT_OPENED[] = "Richtext Editor Opened"; const char EVENT_RICHTEXT_OPENED[] = "richtextEditorOpened";
const char EVENT_FORMEDITOR_TIME[] = "Form Editor"; const char EVENT_FORMEDITOR_TIME[] = "formEditor";
const char EVENT_3DEDITOR_TIME[] = "3D Editor"; const char EVENT_3DEDITOR_TIME[] = "3DEditor";
const char EVENT_TIMELINE_TIME[] = "Timeline"; const char EVENT_TIMELINE_TIME[] = "timeline";
const char EVENT_TRANSITIONEDITOR_TIME[] = "Transition Editor"; const char EVENT_TRANSITIONEDITOR_TIME[] = "transitionEditor";
const char EVENT_CURVEDITOR_TIME[] = "Curve Editor"; const char EVENT_CURVEDITOR_TIME[] = "curveEditor";
const char PROPERTY_EDITOR_CLASSNAME_PROPERTY[] = "__classNamePrivateInternal"; const char PROPERTY_EDITOR_CLASSNAME_PROPERTY[] = "__classNamePrivateInternal";

View File

@@ -100,6 +100,16 @@ namespace QmlDesigner {
namespace Internal { namespace Internal {
QString normalizeIdentifier(const QString &string)
{
if (string.isEmpty())
return {};
QString ret = string;
ret.remove(' ');
ret[0] = ret.at(0).toLower();
return ret;
}
class QtQuickDesignerFactory : public QmlJSEditor::QmlJSEditorFactory class QtQuickDesignerFactory : public QmlJSEditor::QmlJSEditorFactory
{ {
public: public:
@@ -318,7 +328,7 @@ bool QmlDesignerPlugin::delayedInitialize()
emitUsageStatistics("StandaloneMode"); emitUsageStatistics("StandaloneMode");
if (QmlProjectManager::QmlProject::isQtDesignStudioStartedFromQtC()) if (QmlProjectManager::QmlProject::isQtDesignStudioStartedFromQtC())
emitUsageStatistics("QDSlaunchedFromQtC"); emitUsageStatistics("QDSlaunchedFromQtC");
emitUsageStatistics("QDSstartupCount"); emitUsageStatistics("qdsStartupCount");
FoundLicense license = checkLicense(); FoundLicense license = checkLicense();
if (license == FoundLicense::enterprise) if (license == FoundLicense::enterprise)
@@ -353,7 +363,7 @@ void QmlDesignerPlugin::extensionsInitialized()
ExtensionSystem::IPlugin::ShutdownFlag QmlDesignerPlugin::aboutToShutdown() ExtensionSystem::IPlugin::ShutdownFlag QmlDesignerPlugin::aboutToShutdown()
{ {
if (QmlProjectManager::QmlProject::isQtDesignStudio()) if (QmlProjectManager::QmlProject::isQtDesignStudio())
emitUsageStatistics("QDSstartupCount"); emitUsageStatistics("qdsShutdownCount");
return SynchronousShutdown; return SynchronousShutdown;
} }
@@ -642,7 +652,8 @@ double QmlDesignerPlugin::formEditorDevicePixelRatio()
void QmlDesignerPlugin::emitUsageStatistics(const QString &identifier) void QmlDesignerPlugin::emitUsageStatistics(const QString &identifier)
{ {
QTC_ASSERT(instance(), return); QTC_ASSERT(instance(), return);
emit instance()->usageStatisticsNotifier(identifier); emit instance()->usageStatisticsNotifier(normalizeIdentifier(identifier));
qDebug() << normalizeIdentifier(identifier);
} }
void QmlDesignerPlugin::emitUsageStatisticsContextAction(const QString &identifier) void QmlDesignerPlugin::emitUsageStatisticsContextAction(const QString &identifier)
@@ -667,7 +678,9 @@ void QmlDesignerPlugin::registerPreviewImageProvider(QQmlEngine *engine)
void QmlDesignerPlugin::emitUsageStatisticsTime(const QString &identifier, int elapsed) void QmlDesignerPlugin::emitUsageStatisticsTime(const QString &identifier, int elapsed)
{ {
emit instance()->usageStatisticsUsageTimer(identifier, elapsed); QTC_ASSERT(instance(), return);
emit instance()->usageStatisticsUsageTimer(normalizeIdentifier(identifier), elapsed);
qDebug() << normalizeIdentifier(identifier);
} }
QmlDesignerPlugin *QmlDesignerPlugin::instance() QmlDesignerPlugin *QmlDesignerPlugin::instance()

View File

@@ -108,6 +108,8 @@ public:
{} {}
ImageCacheConnectionManager connectionManager; ImageCacheConnectionManager connectionManager;
ImageCacheCollector collector{connectionManager, ImageCacheCollector collector{connectionManager,
QSize{300, 300},
QSize{1000, 1000},
ImageCacheCollectorNullImageHandling::DontCaptureNullImage}; ImageCacheCollectorNullImageHandling::DontCaptureNullImage};
TimeStampProvider timeStampProvider; TimeStampProvider timeStampProvider;
AsynchronousImageFactory factory; AsynchronousImageFactory factory;

View File

@@ -161,6 +161,26 @@ QmlProjectItem *QmlProjectFileFormat::parseProjectFile(const Utils::FilePath &fi
projectItem->addToEnviroment(i.key(), i.value().value.toString()); projectItem->addToEnviroment(i.key(), i.value().value.toString());
++i; ++i;
} }
} else if (childNode->name() == "ShaderTool") {
QmlJS::SimpleReaderNode::Property commandLine = childNode->property("args");
if (commandLine.isValid()) {
const QStringList quotedArgs = commandLine.value.toString().split('\"');
QStringList args;
for (int i = 0; i < quotedArgs.size(); ++i) {
// Each odd arg in this list is a single quoted argument, which we should
// not be split further
if (i % 2 == 0)
args.append(quotedArgs[i].trimmed().split(' '));
else
args.append(quotedArgs[i]);
}
args.removeAll({});
args.append("-o"); // Prepare for adding output file as next arg
projectItem->setShaderToolArgs(args);
}
QmlJS::SimpleReaderNode::Property files = childNode->property("files");
if (files.isValid())
projectItem->setShaderToolFiles(files.value.toStringList());
} else { } else {
qWarning() << "Unknown type:" << childNode->name(); qWarning() << "Unknown type:" << childNode->name();
} }

View File

@@ -84,6 +84,12 @@ public:
bool widgetApp() const { return m_widgetApp; } bool widgetApp() const { return m_widgetApp; }
void setWidgetApp(bool widgetApp) { m_widgetApp = widgetApp; } void setWidgetApp(bool widgetApp) { m_widgetApp = widgetApp; }
QStringList shaderToolArgs() const { return m_shaderToolArgs; }
void setShaderToolArgs(const QStringList &args) {m_shaderToolArgs = args; }
QStringList shaderToolFiles() const { return m_shaderToolFiles; }
void setShaderToolFiles(const QStringList &files) {m_shaderToolFiles = files; }
void appendContent(QmlProjectContentItem *item) { m_content.append(item); } void appendContent(QmlProjectContentItem *item) { m_content.append(item); }
Utils::EnvironmentItems environment() const; Utils::EnvironmentItems environment() const;
@@ -107,6 +113,8 @@ protected:
bool m_qtForMCUs = false; bool m_qtForMCUs = false;
bool m_qt6Project = false; bool m_qt6Project = false;
bool m_widgetApp = false; bool m_widgetApp = false;
QStringList m_shaderToolArgs;
QStringList m_shaderToolFiles;
}; };
} // namespace QmlProjectManager } // namespace QmlProjectManager

View File

@@ -626,6 +626,20 @@ bool QmlBuildSystem::widgetApp() const
return false; return false;
} }
QStringList QmlBuildSystem::shaderToolArgs() const
{
if (m_projectItem)
return m_projectItem->shaderToolArgs();
return {};
}
QStringList QmlBuildSystem::shaderToolFiles() const
{
if (m_projectItem)
return m_projectItem->shaderToolFiles();
return {};
}
bool QmlBuildSystem::addFiles(Node *context, const FilePaths &filePaths, FilePaths *) bool QmlBuildSystem::addFiles(Node *context, const FilePaths &filePaths, FilePaths *)
{ {
if (!dynamic_cast<QmlProjectNode *>(context)) if (!dynamic_cast<QmlProjectNode *>(context))

View File

@@ -95,6 +95,8 @@ public:
void setPrimaryLanguage(QString language); void setPrimaryLanguage(QString language);
bool forceFreeType() const; bool forceFreeType() const;
bool widgetApp() const; bool widgetApp() const;
QStringList shaderToolArgs() const;
QStringList shaderToolFiles() const;
bool addFiles(const QStringList &filePaths); bool addFiles(const QStringList &filePaths);

View File

@@ -42,6 +42,7 @@
#include <qtsupport/qtversionmanager.h> #include <qtsupport/qtversionmanager.h>
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/filepath.h>
#include <utils/fileutils.h> #include <utils/fileutils.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <utils/stringutils.h> #include <utils/stringutils.h>
@@ -50,6 +51,8 @@
#include <algorithm> #include <algorithm>
#include <memory> #include <memory>
using namespace Utils;
namespace QtSupport { namespace QtSupport {
namespace Internal { namespace Internal {
@@ -101,12 +104,21 @@ ExampleSetModel::ExampleSetModel()
qWarning() << "Manifest path " << set.manifestPath << "is not a readable directory, ignoring"; qWarning() << "Manifest path " << set.manifestPath << "is not a readable directory, ignoring";
continue; continue;
} }
m_extraExampleSets.append(set);
if (debugExamples()) { if (debugExamples()) {
qWarning() << "Adding examples set displayName=" << set.displayName qWarning() << "Adding examples set displayName=" << set.displayName
<< ", manifestPath=" << set.manifestPath << ", manifestPath=" << set.manifestPath
<< ", examplesPath=" << set.examplesPath; << ", examplesPath=" << set.examplesPath;
} }
if (!Utils::anyOf(m_extraExampleSets, [&set](const ExtraExampleSet &s) {
return FilePath::fromString(s.examplesPath).cleanPath()
== FilePath::fromString(set.examplesPath).cleanPath()
&& FilePath::fromString(s.manifestPath).cleanPath()
== FilePath::fromString(set.manifestPath).cleanPath();
})) {
m_extraExampleSets.append(set);
} else if (debugExamples()) {
qWarning() << "Not adding, because example set with same directories exists";
}
} }
m_extraExampleSets += pluginRegisteredExampleSets(); m_extraExampleSets += pluginRegisteredExampleSets();

View File

@@ -91,10 +91,6 @@
<tags>qtformcus,mcus,qt,video,2021</tags> <tags>qtformcus,mcus,qt,video,2021</tags>
</tutorial> </tutorial>
<tutorial imageUrl=":qtsupport/images/icons/qteventicon.png" difficulty="" projectPath="" name="Talk: Introduction to Qt Creator IDE" isVideo="true" videoUrl="https://www.youtube.com/watch?v=nGFmjOiT22Y" videoLength="1:06:32">
<description><![CDATA[Getting started with using Qt Creator for cross-platform development.]]></description>
<tags>qt creator,talk,2020</tags>
</tutorial>
<tutorial imageUrl=":qtsupport/images/icons/qteventicon.png" difficulty="" projectPath="" name="Talk: Custom Qt Creator Wizards" isVideo="true" videoUrl="https://www.youtube.com/watch?v=Ko3DuCgFamo" videoLength="27:21"> <tutorial imageUrl=":qtsupport/images/icons/qteventicon.png" difficulty="" projectPath="" name="Talk: Custom Qt Creator Wizards" isVideo="true" videoUrl="https://www.youtube.com/watch?v=Ko3DuCgFamo" videoLength="27:21">
<description><![CDATA[Adding custom file and project creation wizards to Qt Creator.]]></description> <description><![CDATA[Adding custom file and project creation wizards to Qt Creator.]]></description>
<tags>qt creator,wizard,talk,2015</tags> <tags>qt creator,wizard,talk,2015</tags>

View File

@@ -41,6 +41,8 @@
#include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorer.h>
#include <qmldesigner/qmldesignerplugin.h>
#include <QDialog> #include <QDialog>
#include <QFileDialog> #include <QFileDialog>
#include <QFileInfo> #include <QFileInfo>
@@ -83,6 +85,7 @@ FileDownloader::~FileDownloader()
void FileDownloader::start() void FileDownloader::start()
{ {
QmlDesigner::QmlDesignerPlugin::emitUsageStatistics("exampleDownload:" + name());
m_tempFile.setFileName(QDir::tempPath() + "/" + name() + ".XXXXXX" + ".zip"); m_tempFile.setFileName(QDir::tempPath() + "/" + name() + ".XXXXXX" + ".zip");
m_tempFile.open(QIODevice::WriteOnly); m_tempFile.open(QIODevice::WriteOnly);

View File

@@ -0,0 +1,108 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
/*
This is a UI file (.ui.qml) that is intended to be edited in Qt Design Studio only.
It is supposed to be strictly declarative and only uses a subset of QML. If you edit
this file manually, you might introduce QML code that is not supported by Qt Design Studio.
Check out https://doc.qt.io/qtcreator/creator-quick-ui-forms.html for details on .ui.qml files.
*/
import QtQuick 2.15
import QtQuick.Templates 2.15
import StudioFonts 1.0
Button {
id: control
implicitWidth: Math.max(
buttonBackground ? buttonBackground.implicitWidth : 0,
textItem.implicitWidth + leftPadding + rightPadding)
implicitHeight: Math.max(
buttonBackground ? buttonBackground.implicitHeight : 0,
textItem.implicitHeight + topPadding + bottomPadding)
leftPadding: 16
rightPadding: 16
text: "My Button"
font.family: StudioFonts.titilliumWeb_light
//property color accentColor: "#047eff"
property color accentColor: "#126491"
background: buttonBackground
Rectangle {
id: buttonBackground
color: "#00000000"
implicitWidth: 100
implicitHeight: 40
opacity: enabled ? 1 : 0.3
radius: 2
border.color: control.accentColor
}
contentItem: textItem
Text {
id: textItem
text: control.text
opacity: enabled ? 1.0 : 0.3
color: "#ffffff"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
states: [
State {
name: "normal"
when: !control.down
PropertyChanges {
target: buttonBackground
color: "#00000000"
border.color: control.accentColor
}
PropertyChanges {
target: textItem
color: "#ffffff"
}
},
State {
name: "down"
when: control.down
PropertyChanges {
target: textItem
color: "#ffffff"
}
PropertyChanges {
target: buttonBackground
color: control.accentColor
border.color: "#00000000"
}
}
]
}

View File

@@ -29,7 +29,7 @@ Rectangle {
id: splashBackground id: splashBackground
width: 460 width: 460
height: 480 height: 480
color: "#11102d" color: "transparent"
layer.enabled: true layer.enabled: true
layer.textureSize: Qt.size(width * 2, height * 2) layer.textureSize: Qt.size(width * 2, height * 2)
@@ -37,6 +37,7 @@ Rectangle {
Item { Item {
id: composition id: composition
anchors.centerIn: parent
width: 460 width: 460
height: 480 height: 480
@@ -56,25 +57,4 @@ Rectangle {
} }
} }
} }
Image {
id: highlight
x: -56
y: -19
width: 520
height: 506
fillMode: Image.PreserveAspectFit
source: "welcome_windows/highlight.png"
}
Image {
id: hand
x: 245
y: 227
width: 224
height: 264
visible: true
fillMode: Image.PreserveAspectFit
source: "welcome_windows/hand.png"
}
} }

View File

@@ -32,8 +32,7 @@ import usagestatistics 1.0
Rectangle { Rectangle {
id: welcome_splash id: welcome_splash
width: 800 anchors.fill: parent
height: 480
gradient: Gradient { gradient: Gradient {
orientation: Gradient.Horizontal orientation: Gradient.Horizontal
@@ -46,43 +45,27 @@ Rectangle {
signal closeClicked signal closeClicked
signal configureClicked signal configureClicked
property alias doNotShowAgain: doNotShowCheckBox.checked property bool doNotShowAgain: true
property bool loadingPlugins: true property bool loadingPlugins: true
color: "#1d212a"
// called from C++
function onPluginInitialized(crashReportingEnabled: bool, crashReportingOn: bool)
{
loadingPlugins = false
if (crashReportingEnabled) {
var configureButton = "<a href='#' style='text-decoration:none;color:#ffff00'>"
+ qsTr("[Configure]") + "</a>";
var settingPath = Qt.platform.os === "osx"
? qsTr("Qt Creator > Preferences > Environment > System")
: qsTr("Tools > Options > Environment > System")
var strConfigure = qsTr("Qt Design Studio collects usage statistics and crash reports for the sole purpose of fixing bugs and improving the tool. "
+ "You can configure the crash reporter under %1. %2").arg(settingPath).arg(configureButton)
crash_reporting_text.text = strConfigure
crashReportCheckBox.visible = true
}
}
Image { Image {
id: logo id: logo
x: 15 anchors.top: parent.top
y: 11 anchors.left: parent.left
width: 66 anchors.margins: 10
height: 50 width: 66 * 2
height: 50 * 2
smooth: true
source: "welcome_windows/logo.png" source: "welcome_windows/logo.png"
} }
Text { Text {
id: qt_design_studio id: qt_design_studio_text
x: 13 anchors.top: logo.top
y: 81 anchors.left: logo.right
width: 336 anchors.leftMargin: 10
height: 46
color: "#25709a" color: "#25709a"
text: qsTr("Qt Design Studio") text: qsTr("Qt Design Studio")
font.pixelSize: 36 font.pixelSize: 36
@@ -90,156 +73,89 @@ Rectangle {
} }
Text { Text {
id: software_for_ui id: qt_design_studio_version_text
x: 15 anchors.left: qt_design_studio_text.right
y: 126 anchors.baseline: qt_design_studio_text.baseline
width: 300 anchors.leftMargin: 10
height: 30 color: "#25709a"
color: "#ffffff" text: usageStatisticModel.version
text: qsTr("Software for UI and UX Designers")
renderType: Text.QtRendering
font.pixelSize: 15
font.family: StudioFonts.titilliumWeb_light font.family: StudioFonts.titilliumWeb_light
font.pixelSize: 36
} }
Text { Text {
id: copyright id: license_variant_text
x: 15 anchors.left: qt_design_studio_text.left
y: 155 anchors.top: qt_design_studio_text.bottom
width: 270 anchors.leftMargin: 5
height: 24
color: "#ffffff" color: "#ffffff"
text: qsTr("Copyright 2008 - 2022 The Qt Company")
font.pixelSize: 14
font.family: StudioFonts.titilliumWeb_light
}
Text {
id: all_rights_reserved
x: 15
y: 174
width: 250
height: 24
color: "#ffffff"
text: qsTr("All Rights Reserved")
font.pixelSize: 14
font.family: StudioFonts.titilliumWeb_light font.family: StudioFonts.titilliumWeb_light
} font.pixelSize: 20
Text { text: {
id: marketing_1 if (projectModel.communityVersion)
x: 15 return qsTr("Community Edition")
y: 206 if (projectModel.enterpriseVersion)
width: 406 return qsTr("Enterprise Edition")
height: 31 return qsTr("Professional Edition")
color: "#ffffff" }
text: qsTr("Multi-paradigm language for creating highly dynamic applications.")
wrapMode: Text.WordWrap
font.family: StudioFonts.titilliumWeb_light
font.pixelSize: 12
font.wordSpacing: 0
}
Text { ProjectModel {
id: marketing_2 id: projectModel
x: 15 }
y: 229
width: 341
height: 31
color: "#ffffff"
text: qsTr("Run your concepts and prototypes on your final hardware.")
wrapMode: Text.WordWrap
font.family: StudioFonts.titilliumWeb_light
font.pixelSize: 12
font.wordSpacing: 0
}
Text { UsageStatisticModel {
id: marketing_3 id: usageStatisticModel
x: 15
y: 252
width: 336
height: 31
color: "#ffffff"
text: qsTr("Seamless integration between designer and developer.")
wrapMode: Text.WordWrap
font.family: StudioFonts.titilliumWeb_light
font.pixelSize: 12
font.wordSpacing: 0
}
Text {
id: crash_reporting_text
color: "#ffffff"
anchors.bottom: columnLayout.top
textFormat: Text.RichText
x: 15
y: 280
width: 311
wrapMode: Text.WordWrap
anchors.bottomMargin: 8
font.family: StudioFonts.titilliumWeb_light
font.pixelSize: 12
font.wordSpacing: 0
onLinkActivated: welcome_splash.configureClicked()
MouseArea { // show hand cursor on link hover
anchors.fill: parent
acceptedButtons: Qt.NoButton // don't eat clicks on the Text
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
} }
} }
Dof_Effect { Dof_Effect {
id: dof_Effect id: dof_effect
x: 358 anchors.top: qt_design_studio_text.bottom
anchors.horizontalCenter: welcome_splash.horizontalCenter
width: 442 width: 442
height: 480 height: 480
visible: true
maskBlurSamples: 64 maskBlurSamples: 64
maskBlurRadius: 32 maskBlurRadius: 32
Splash_Image25d { Splash_Image25d {
id: animated_artwork id: animated_artwork
x: 358 width: dof_effect.width
y: 0 height: dof_effect.height
width: 442
height: 480
clip: true clip: true
} }
} }
Image { Text {
id: close_window id: help_us_text
anchors.top: parent.top anchors.left: welcome_splash.left
anchors.right: parent.right anchors.right: parent.right
anchors.margins: 8 anchors.leftMargin: 10
width: 13 anchors.top: dof_effect.bottom
height: 13 anchors.topMargin: 10
fillMode: Image.PreserveAspectFit color: "#FFFFFF"
source: "welcome_windows/close.png" text: qsTr("Before we let you move on to your wonderful designs, help us make Qt Design Studio even better by letting us know how you're using it.")
opacity: area.containsMouse ? 1 : 0.8
MouseArea { font.family: StudioFonts.titilliumWeb_light
id: area font.pixelSize: 18
hoverEnabled: true wrapMode: Text.WordWrap
anchors.fill: parent anchors.rightMargin: 10
anchors.margins: -10
onClicked: welcome_splash.closeClicked()
}
} }
ColumnLayout { ColumnLayout {
id: columnLayout id: columnLayout
anchors.left: parent.left anchors.left: parent.left
anchors.bottom: parent.bottom anchors.top: help_us_text.bottom
anchors.leftMargin: 16 anchors.leftMargin: 10
anchors.bottomMargin: 10 anchors.topMargin: 20
spacing: 3 spacing: 3
CheckBox { CheckBox {
visible: false
id: usageStatisticCheckBox id: usageStatisticCheckBox
text: qsTr("Enable Usage Statistics") text: qsTr("Send Usage Statistics")
checked: usageStatisticModel.usageStatisticEnabled checked: usageStatisticModel.usageStatisticEnabled
padding: 0 padding: 0
spacing: 12 spacing: 12
@@ -255,11 +171,11 @@ Rectangle {
} }
CheckBox { CheckBox {
visible: false
id: crashReportCheckBox id: crashReportCheckBox
text: qsTr("Enable Crash Reports") text: qsTr("Send Crash Reports")
spacing: 12 spacing: 12
checked: usageStatisticModel.crashReporterEnabled checked: usageStatisticModel.crashReporterEnabled
visible: false
onCheckedChanged: { onCheckedChanged: {
usageStatisticModel.setCrashReporterEnabled(crashReportCheckBox.checked) usageStatisticModel.setCrashReporterEnabled(crashReportCheckBox.checked)
@@ -274,90 +190,41 @@ Rectangle {
} }
padding: 0 padding: 0
} }
CheckBox {
id: doNotShowCheckBox
text: qsTr("Do not show this again")
padding: 0
spacing: 12
contentItem: Text {
text: doNotShowCheckBox.text
color: "#ffffff"
leftPadding: doNotShowCheckBox.indicator.width + doNotShowCheckBox.spacing
font.pixelSize: 12
}
}
} }
RowLayout { RowLayout {
x: 16 anchors.right: parent.right
y: 277 anchors.bottom: welcome_splash.bottom
visible: welcome_splash.loadingPlugins anchors.rightMargin: 10
anchors.bottomMargin: 10
spacing: 20
Text { CustomButton {
id: text1 text: qsTr("Don't send")
color: "#ffffff" onClicked: {
text: qsTr("%") usageStatisticModel.setTelemetryEnabled(false)
font.pixelSize: 12 usageStatisticModel.setCrashReporterEnabled(false)
welcome_splash.closeClicked()
RotationAnimator {
target: text1
from: 0
to: 360
duration: 1800
running: true
loops: -1
} }
} }
Text { CustomButton {
id: loading_progress text: qsTr("Send analytics data")
color: "#ffffff" onClicked: {
text: qsTr("Loading Plugins") usageStatisticModel.setTelemetryEnabled(true)
font.family: StudioFonts.titilliumWeb_light usageStatisticModel.setCrashReporterEnabled(true)
font.pixelSize: 16 welcome_splash.closeClicked()
}
Text {
id: text2
color: "#ffffff"
text: qsTr("%")
font.pixelSize: 12
RotationAnimator {
target: text2
from: 0
to: 360
duration: 2000
running: true
loops: -1
} }
} }
} }
Text { CustomButton {
id: all_rights_reserved1 y: 430
x: 15 text: qsTr("Learn More")
y: 65 anchors.left: parent.left
color: "#ffffff" anchors.bottom: parent.bottom
anchors.bottomMargin: 10
font.pixelSize: 13 anchors.leftMargin: 10
font.family: StudioFonts.titilliumWeb_light onClicked: Qt.openUrlExternally("https://www.qt.io/terms-conditions/telemetry-privacy")
text: {
if (projectModel.communityVersion)
return qsTr("Community Edition")
if (projectModel.enterpriseVersion)
return qsTr("Enterprise Edition")
return qsTr("Professional Edition")
}
ProjectModel {
id: projectModel
}
UsageStatisticModel {
id: usageStatisticModel
}
} }
} }

View File

@@ -27,8 +27,8 @@ import QtQuick 2.0
Item { Item {
id: root id: root
width: 800 width: 720
height: 480 height: 720
signal closeClicked signal closeClicked
signal checkBoxToggled signal checkBoxToggled

View File

@@ -116,6 +116,7 @@ Item {
id: image id: image
width: 240 width: 240
height: 125 height: 125
mipmap: true
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
} }
} }

View File

@@ -27,4 +27,5 @@ import QtQuick 2.0
QtObject { QtObject {
property bool usageStatisticEnabled: false property bool usageStatisticEnabled: false
property string version: "3.3.0"
} }

View File

@@ -133,11 +133,13 @@ class UsageStatisticPluginModel : public QObject
Q_PROPERTY(bool usageStatisticEnabled MEMBER m_usageStatisticEnabled NOTIFY usageStatisticChanged) Q_PROPERTY(bool usageStatisticEnabled MEMBER m_usageStatisticEnabled NOTIFY usageStatisticChanged)
Q_PROPERTY(bool crashReporterEnabled MEMBER m_crashReporterEnabled NOTIFY crashReporterEnabledChanged) Q_PROPERTY(bool crashReporterEnabled MEMBER m_crashReporterEnabled NOTIFY crashReporterEnabledChanged)
Q_PROPERTY(QString version MEMBER m_versionString CONSTANT)
public: public:
explicit UsageStatisticPluginModel(QObject *parent = nullptr) explicit UsageStatisticPluginModel(QObject *parent = nullptr)
: QObject(parent) : QObject(parent)
{ {
m_versionString = Core::Constants::IDE_VERSION_DISPLAY;
setupModel(); setupModel();
} }
@@ -160,13 +162,10 @@ public:
Core::ICore::settings()->setValue(CRASH_REPORTER_SETTING, b); Core::ICore::settings()->setValue(CRASH_REPORTER_SETTING, b);
s_pluginInstance->pauseRemoveSplashTimer();
const QString restartText = tr("The change will take effect after restart."); const QString restartText = tr("The change will take effect after restart.");
Core::RestartDialog restartDialog(Core::ICore::dialogParent(), restartText); Core::RestartDialog restartDialog(Core::ICore::dialogParent(), restartText);
restartDialog.exec(); restartDialog.exec();
s_pluginInstance->resumeRemoveSplashTimer();
setupModel(); setupModel();
} }
@@ -179,14 +178,10 @@ public:
settings->setValue(STATISTICS_COLLECTION_MODE, b ? DETAILED_USAGE_STATISTICS : NO_TELEMETRY); settings->setValue(STATISTICS_COLLECTION_MODE, b ? DETAILED_USAGE_STATISTICS : NO_TELEMETRY);
// pause remove splash timer while dialog is open otherwise splash crashes upon removal
s_pluginInstance->pauseRemoveSplashTimer();
const QString restartText = tr("The change will take effect after restart."); const QString restartText = tr("The change will take effect after restart.");
Core::RestartDialog restartDialog(Core::ICore::dialogParent(), restartText); Core::RestartDialog restartDialog(Core::ICore::dialogParent(), restartText);
restartDialog.exec(); restartDialog.exec();
s_pluginInstance->resumeRemoveSplashTimer();
setupModel(); setupModel();
} }
@@ -197,6 +192,7 @@ signals:
private: private:
bool m_usageStatisticEnabled = false; bool m_usageStatisticEnabled = false;
bool m_crashReporterEnabled = false; bool m_crashReporterEnabled = false;
QString m_versionString;
}; };
class ProjectModel : public QAbstractListModel class ProjectModel : public QAbstractListModel
@@ -249,6 +245,9 @@ public:
const QString &formFile, const QString &formFile,
const QString &explicitQmlproject) const QString &explicitQmlproject)
{ {
QmlDesigner::QmlDesignerPlugin::emitUsageStatistics("exampleOpened:"
+ exampleName);
const QString exampleFolder = examplePath + "/" + exampleName + "/"; const QString exampleFolder = examplePath + "/" + exampleName + "/";
QString projectFile = exampleFolder + exampleName + ".qmlproject"; QString projectFile = exampleFolder + exampleName + ".qmlproject";
@@ -517,10 +516,7 @@ void StudioWelcomePlugin::showSystemSettings()
Core::ICore::infoBar()->removeInfo("WarnCrashReporting"); Core::ICore::infoBar()->removeInfo("WarnCrashReporting");
Core::ICore::infoBar()->globallySuppressInfo("WarnCrashReporting"); Core::ICore::infoBar()->globallySuppressInfo("WarnCrashReporting");
// pause remove splash timer while settings dialog is open otherwise splash crashes upon removal
pauseRemoveSplashTimer();
Core::ICore::showOptionsDialog(Core::Constants::SETTINGS_ID_SYSTEM); Core::ICore::showOptionsDialog(Core::Constants::SETTINGS_ID_SYSTEM);
resumeRemoveSplashTimer();
} }
StudioWelcomePlugin::StudioWelcomePlugin() StudioWelcomePlugin::StudioWelcomePlugin()
@@ -543,11 +539,6 @@ bool StudioWelcomePlugin::initialize(const QStringList &arguments, QString *erro
m_welcomeMode = new WelcomeMode; m_welcomeMode = new WelcomeMode;
m_removeSplashTimer.setSingleShot(true);
const QString splashScreenTimeoutEntry = "QML/Designer/splashScreenTimeout";
m_removeSplashTimer.setInterval(
Core::ICore::settings()->value(splashScreenTimeoutEntry, 15000).toInt());
connect(&m_removeSplashTimer, &QTimer::timeout, this, [this] { closeSplashScreen(); });
return true; return true;
} }
@@ -620,8 +611,6 @@ void StudioWelcomePlugin::extensionsInitialized()
s_view->show(); s_view->show();
s_view->raise(); s_view->raise();
m_removeSplashTimer.start();
}); });
} }
} }
@@ -633,34 +622,9 @@ bool StudioWelcomePlugin::delayedInitialize()
QTC_ASSERT(s_view->rootObject(), return true); QTC_ASSERT(s_view->rootObject(), return true);
#ifdef ENABLE_CRASHPAD
const bool crashReportingEnabled = true;
const bool crashReportingOn = Core::ICore::settings()->value(CRASH_REPORTER_SETTING, false).toBool();
#else
const bool crashReportingEnabled = false;
const bool crashReportingOn = false;
#endif
QMetaObject::invokeMethod(s_view->rootObject(), "onPluginInitialized",
Q_ARG(bool, crashReportingEnabled), Q_ARG(bool, crashReportingOn));
return false; return false;
} }
void StudioWelcomePlugin::pauseRemoveSplashTimer()
{
if (m_removeSplashTimer.isActive()) {
m_removeSplashRemainingTime = m_removeSplashTimer.remainingTime(); // milliseconds
m_removeSplashTimer.stop();
}
}
void StudioWelcomePlugin::resumeRemoveSplashTimer()
{
if (!m_removeSplashTimer.isActive())
m_removeSplashTimer.start(m_removeSplashRemainingTime);
}
Utils::FilePath StudioWelcomePlugin::defaultExamplesPath() Utils::FilePath StudioWelcomePlugin::defaultExamplesPath()
{ {
QStandardPaths::StandardLocation location = Utils::HostOsInfo::isMacHost() QStandardPaths::StandardLocation location = Utils::HostOsInfo::isMacHost()

View File

@@ -77,9 +77,6 @@ public:
void extensionsInitialized() override; void extensionsInitialized() override;
bool delayedInitialize() override; bool delayedInitialize() override;
void pauseRemoveSplashTimer();
void resumeRemoveSplashTimer();
static Utils::FilePath defaultExamplesPath(); static Utils::FilePath defaultExamplesPath();
static QString examplesPathSetting(); static QString examplesPathSetting();
@@ -88,9 +85,7 @@ signals:
private: private:
class WelcomeMode *m_welcomeMode = nullptr; class WelcomeMode *m_welcomeMode = nullptr;
QTimer m_removeSplashTimer;
StudioWelcomeSettingsPage m_settingsPage; StudioWelcomeSettingsPage m_settingsPage;
int m_removeSplashRemainingTime = 0;
}; };
} // namespace Internal } // namespace Internal

View File

@@ -27,6 +27,7 @@
#include "texteditor_global.h" #include "texteditor_global.h"
#include <QList>
#include <QString> #include <QString>
#include <QMetaType> #include <QMetaType>
#include <QSharedPointer> #include <QSharedPointer>

View File

@@ -164,11 +164,10 @@ MultiTextCursor TextDocumentPrivate::indentOrUnindent(const MultiTextCursor &cur
} }
// make sure that selection that begins in first column stays at first column // make sure that selection that begins in first column stays at first column
// even if we insert text at first column // even if we insert text at first column
cursor = textCursor;
if (cursorAtBlockStart) { if (cursorAtBlockStart) {
cursor = textCursor;
cursor.setPosition(startBlock.position(), QTextCursor::KeepAnchor); cursor.setPosition(startBlock.position(), QTextCursor::KeepAnchor);
} else if (anchorAtBlockStart) { } else if (anchorAtBlockStart) {
cursor = textCursor;
cursor.setPosition(startBlock.position(), QTextCursor::MoveAnchor); cursor.setPosition(startBlock.position(), QTextCursor::MoveAnchor);
cursor.setPosition(textCursor.position(), QTextCursor::KeepAnchor); cursor.setPosition(textCursor.position(), QTextCursor::KeepAnchor);
} }

View File

@@ -128,6 +128,7 @@ public:
runControl->buildKey(), runControl->buildKey(),
browserId, browserId,
QString::number(portsGatherer->findEndPoint().port())); QString::number(portsGatherer->findEndPoint().port()));
r.environment = runControl->buildEnvironment();
SimpleTargetRunner::doStart(r); SimpleTargetRunner::doStart(r);
}); });
} }

View File

@@ -226,11 +226,11 @@ def logApplicationOutput():
# get the output from a given cmdline call # get the output from a given cmdline call
def getOutputFromCmdline(cmdline, environment=None, acceptedError=0): def getOutputFromCmdline(cmdline, environment=None, acceptedError=0):
try: try:
return subprocess.check_output(cmdline, env=environment) return stringify(subprocess.check_output(cmdline, env=environment))
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
if e.returncode != acceptedError: if e.returncode != acceptedError:
test.warning("Command '%s' returned %d" % (e.cmd, e.returncode)) test.warning("Command '%s' returned %d" % (e.cmd, e.returncode))
return e.output return stringify(e.output)
def selectFromFileDialog(fileName, waitForFile=False, ignoreFinalSnooze=False): def selectFromFileDialog(fileName, waitForFile=False, ignoreFinalSnooze=False):
def __closePopupIfNecessary__(): def __closePopupIfNecessary__():
@@ -238,41 +238,29 @@ def selectFromFileDialog(fileName, waitForFile=False, ignoreFinalSnooze=False):
test.log("Closing active popup widget") test.log("Closing active popup widget")
QApplication.activePopupWidget().close() QApplication.activePopupWidget().close()
if platform.system() == "Darwin": fName = os.path.basename(os.path.abspath(fileName))
pName = os.path.dirname(os.path.abspath(fileName)) + os.sep
try:
waitForObject("{name='QFileDialog' type='QFileDialog' visible='1'}", 5000)
pathLine = waitForObject("{name='fileNameEdit' type='QLineEdit' visible='1'}")
replaceEditorContent(pathLine, pName)
snooze(1) snooze(1)
nativeType("<Command+Shift+g>") clickButton(waitForObject("{text='Open' type='QPushButton'}"))
waitFor("str(pathLine.text)==''")
replaceEditorContent(pathLine, fName)
snooze(1) snooze(1)
nativeType(fileName) __closePopupIfNecessary__()
snooze(2) clickButton(waitForObject("{text='Open' type='QPushButton'}"))
nativeType("<Return>") except:
snooze(3) nativeType("<Ctrl+a>")
nativeType("<Delete>")
nativeType(pName + fName)
seconds = len(pName + fName) / 20
test.log("Using snooze(%d) [problems with event processing of nativeType()]" % seconds)
snooze(seconds)
nativeType("<Return>") nativeType("<Return>")
if not ignoreFinalSnooze: if not ignoreFinalSnooze:
snooze(1) snooze(3)
else:
fName = os.path.basename(os.path.abspath(fileName))
pName = os.path.dirname(os.path.abspath(fileName)) + os.sep
try:
waitForObject("{name='QFileDialog' type='QFileDialog' visible='1'}", 5000)
pathLine = waitForObject("{name='fileNameEdit' type='QLineEdit' visible='1'}")
replaceEditorContent(pathLine, pName)
snooze(1)
clickButton(waitForObject("{text='Open' type='QPushButton'}"))
waitFor("str(pathLine.text)==''")
replaceEditorContent(pathLine, fName)
snooze(1)
__closePopupIfNecessary__()
clickButton(waitForObject("{text='Open' type='QPushButton'}"))
except:
nativeType("<Ctrl+a>")
nativeType("<Delete>")
nativeType(pName + fName)
seconds = len(pName + fName) / 20
test.log("Using snooze(%d) [problems with event processing of nativeType()]" % seconds)
snooze(seconds)
nativeType("<Return>")
if not ignoreFinalSnooze:
snooze(3)
if waitForFile: if waitForFile:
fileCombo = waitForObject(":Qt Creator_FilenameQComboBox") fileCombo = waitForObject(":Qt Creator_FilenameQComboBox")
if not waitFor("str(fileCombo.currentText) in fileName", 5000): if not waitFor("str(fileCombo.currentText) in fileName", 5000):
@@ -447,6 +435,7 @@ def iterateQtVersions(keepOptionsOpen=False, alreadyOnOptionsDialog=False,
rootChildText = str(rootIndex.data()).replace(".", "\\.").replace("_", "\\_") rootChildText = str(rootIndex.data()).replace(".", "\\.").replace("_", "\\_")
for subIndex in dumpIndices(model, rootIndex): for subIndex in dumpIndices(model, rootIndex):
subChildText = str(subIndex.data()).replace(".", "\\.").replace("_", "\\_") subChildText = str(subIndex.data()).replace(".", "\\.").replace("_", "\\_")
treeView.scrollTo(subIndex)
mouseClick(waitForObjectItem(treeView, ".".join([rootChildText,subChildText]))) mouseClick(waitForObjectItem(treeView, ".".join([rootChildText,subChildText])))
currentText = str(waitForObject(":QtSupport__Internal__QtVersionManager.QLabel").text) currentText = str(waitForObject(":QtSupport__Internal__QtVersionManager.QLabel").text)
matches = pattern.match(currentText) matches = pattern.match(currentText)
@@ -684,3 +673,12 @@ def isString(sth):
return isinstance(sth, str) return isinstance(sth, str)
else: else:
return isinstance(sth, (str, unicode)) return isinstance(sth, (str, unicode))
# helper function to ensure we get str, converts bytes if necessary
def stringify(obj):
stringTypes = (str, unicode) if sys.version_info.major == 2 else (str)
if isinstance(obj, stringTypes):
return obj
if isinstance(obj, bytes):
tmp = obj.decode('cp1252') if platform.system() in ('Microsoft','Windows') else obj.decode()
return tmp

View File

@@ -32,8 +32,9 @@ def cmakeSupported():
versionLines = filter(lambda line: "cmake version " in line, versionLines = filter(lambda line: "cmake version " in line,
getOutputFromCmdline(["cmake", "--version"]).splitlines()) getOutputFromCmdline(["cmake", "--version"]).splitlines())
try: try:
test.log("Using " + versionLines[0]) versionLine = list(versionLines)[0]
matcher = re.match("cmake version (\d+)\.(\d+)\.\d+", versionLines[0]) test.log("Using " + versionLine)
matcher = re.match("cmake version (\d+)\.(\d+)\.\d+", versionLine)
major = __builtin__.int(matcher.group(1)) major = __builtin__.int(matcher.group(1))
minor = __builtin__.int(matcher.group(2)) minor = __builtin__.int(matcher.group(2))
except: except:

View File

@@ -1,6 +1,6 @@
############################################################################ ############################################################################
# #
# Copyright (C) 2016 The Qt Company Ltd. # Copyright (C) 2022 The Qt Company Ltd.
# Contact: https://www.qt.io/licensing/ # Contact: https://www.qt.io/licensing/
# #
# This file is part of Qt Creator. # This file is part of Qt Creator.
@@ -66,8 +66,8 @@ def main():
availableProjectTypes.append({category:template}) availableProjectTypes.append({category:template})
safeClickButton("Cancel") safeClickButton("Cancel")
for current in availableProjectTypes: for current in availableProjectTypes:
category = current.keys()[0] category = list(current.keys())[0]
template = current.values()[0] template = list(current.values())[0]
with TestSection("Testing project template %s -> %s" % (category, template)): with TestSection("Testing project template %s -> %s" % (category, template)):
displayedPlatforms = __createProject__(category, template) displayedPlatforms = __createProject__(category, template)
if template == "Qt Quick Application": if template == "Qt Quick Application":