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
userMessageOnFailure: "Failed to extract LLVM package, check logs."
- 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
maxTimeBetweenOutput: 3600
userMessageOnFailure: "Failed to run build.py, check logs."
@@ -50,7 +50,7 @@ instructions:
maxTimeBetweenOutput: 360
userMessageOnFailure: "Failed to extract LLVM package, check logs."
- 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
maxTimeBetweenOutput: 3600
userMessageOnFailure: "Failed to run build.py, check logs."

View File

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

View File

@@ -32,7 +32,7 @@ instructions:
property: host.os
in_values: [MacOS, Linux, Windows]
- 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
maxTimeInSeconds: 3600
maxTimeBetweenOutput: 360
@@ -42,7 +42,7 @@ instructions:
property: host.os
equals_value: Linux
- 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
maxTimeInSeconds: 3600
maxTimeBetweenOutput: 360
@@ -52,7 +52,7 @@ instructions:
property: host.os
equals_value: MacOS
- type: ExecuteCommand
command: "C:\\Python27\\Scripts\\pip.exe install pywin32"
command: "pip.exe install pywin32"
maxTimeInSeconds: 1200
maxTimeBetweenOutput: 120
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
pointer or dwarf mode. However, to generate the files correctly, numerous
preconditions have to be met. All system images for the
\l{http://doc.qt.io/QtForDeviceCreation/qtee-supported-platforms.html}
{Qt for Device Creation reference devices}, except for Freescale iMX53 Quick
Start Board and SILICA Architect Tibidabo, are correctly set up for
profiling in the dwarf mode. For other devices, check whether Perf can read
back its own data in a sensible way by checking the output of
\l{https://doc.qt.io/Boot2Qt/qtdc-supported-platforms.html}
{Boot2Qt:Supported Target Devices and Development Hosts} are correctly set
up for profiling in the dwarf mode. For other devices, check whether Perf
can read 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.
\section1 Loading and Saving Trace Files

View File

@@ -157,3 +157,15 @@
\externalpage https://microsoft.github.io/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
plugged in devices. To allow the development user access to the device
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}
{Setting Up USB Access to Embedded Devices}.
\l{https://doc.qt.io/Boot2Qt/b2qt-requirements-x11.html#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 >
\uicontrol Devices > \uicontrol Devices.

View File

@@ -65,24 +65,22 @@
license holders, tooling is provided to customize the contents of the stack
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.
The following topics contain more information about developing applications
for Boot2Qt devices:
\list
\li \l{https://doc.qt.io/QtForDeviceCreation/qtee-supported-platforms.html}
{Reference Target Devices and Development Hosts}
\li \l{https://doc.qt.io/QtForDeviceCreation/b2qt-installation-guides.html}
{Installation Guides}
\li \l{https://doc.qt.io/Boot2Qt/qtdc-supported-platforms.html}
{Boot2Qt: Supported Target Devices and Development Hosts}
\li \l{https://doc.qt.io/Boot2Qt/b2qt-installation-guides.html}
{Boot2Qt: Installation Guides}
\li \l{Connecting Boot2Qt Devices}
\li \l{Specifying Run Settings for Boot2Qt Devices}
\li \l{Deploying Applications to Boot2Qt Devices}
\li \l{https://doc.qt.io/qtcreator/creator-overview-qtasam.html}
{Qt Creator Plugin for Qt Application Manager}
\li \l{https://doc.qt.io/QtForDeviceCreation/index.html}
{Qt for Device Creation}
\endlist
\section1 Generic Remote Linux

View File

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

View File

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

View File

@@ -44,12 +44,10 @@
\l{Connecting Android Devices} and \l{Connecting iOS Devices}.
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
\l{https://doc.qt.io/QtForDeviceCreation/b2qt-installation-guides.html}
{Installation Guides} in the
\l{http://doc.qt.io/QtForDeviceCreation/index.html}{Qt for Device Creation}
documentation.
\l{https://doc.qt.io/Boot2Qt/b2qt-installation-guides.html}
{Boot2Qt: Installation Guides}
If you have \l{Qt Design Studio Manual}{\QDS} installed, you can open
\QDS examples from \QC in \QDS.

View File

@@ -49,7 +49,7 @@
\list
\li \l{Connecting Android Devices}{Android 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)
\li \l{Emulator}{Boot2Qt Emulator Device} (commercial only)
\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
\if defined(qtdesignstudio)
\previouspage creator-live-preview-android.html
\nextpage studio-exporting-and-importing.html
\else
\previouspage creator-live-preview-devices.html
\nextpage creator-building-targets.html
\endif

View File

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

View File

@@ -72,6 +72,14 @@
devices is set up automatically. You only need to connect your
devices to your system.
\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}
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
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
\image qt-figma-bridge.png "Qt Bridge for Figma"

View File

@@ -67,7 +67,7 @@
\section1 Embedding Resources into Applications
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
embedd in the \uicontrol {Add Resources} dialog.

View File

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

View File

@@ -31,7 +31,7 @@
\nextpage studio-3d-particle-system.html
\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
techniques. This includes chaotic systems, natural phenomena, or processes
caused by chemical reactions. For example, you can simulate fire, smoke,
@@ -47,15 +47,16 @@
\li \l {Particle Directions}
\endlist
\section1 Adding a Particle System
Preset particle components are available in
\uicontrol Components > \uicontrol {Qt Quick 3D Particles 3D}
Preset particle \l{Particle Components}{components},
\l{Particle Templates}{templates}, and \l{Particle Effects}{effects} are
available in \uicontrol Components > \uicontrol {QtQuick3D Particles3D}
after you add the \uicontrol {QtQuick3D.Particles3D} module to
your project, as instructed in \l{Adding and Removing Modules}.
\image studio-3d-particles.png "3D Particles"
\section1 Particle Components
When you add an instance of the \uicontrol {Particle System} component to a
scene, \QDS automatically adds instances of the \uicontrol {Sprite Particle},
\uicontrol Emitter, and \uicontrol {Vector Direction} components for you.
@@ -82,53 +83,6 @@
to simulate flying objects that follow wavy curves, or an instance of the
\uicontrol {Point Rotator} to simulate windy weather.
To add a particle system that emits sprite particles:
\list 1
\li Select \uicontrol 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.
\table
@@ -207,6 +161,190 @@
\li Applies random wave curves to particles.
\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
\list

View File

@@ -67,41 +67,6 @@ if [ -d "$assetimporterSrcDir" ]; then
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
imports2Dir="$app_path/Contents/Imports/qtquick2"
if [ -d "$quick2_src" ]; then
@@ -209,3 +174,19 @@ if [ ! -d "$app_path/Contents/Frameworks/QtCore.framework" ]; then
"$clangbackendArgument" || exit 1
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
#include <qmetatype.h>
#include <QSize>
#include <QUrl>
#include <QVector>
#include <qmetatype.h>
#include "instancecontainer.h"
#include "reparentcontainer.h"
@@ -55,6 +56,8 @@ public:
const QUrl &resourceUrl,
const QHash<QString, QVariantMap> &edit3dToolStates,
const QString &language,
QSize captureImageMinimumSize,
QSize captureImageMaximumSize,
qint32 stateInstanceId)
: instances(instanceContainer)
, reparentInstances(reparentContainer)
@@ -68,6 +71,8 @@ public:
, resourceUrl(resourceUrl)
, edit3dToolStates(edit3dToolStates)
, language(language)
, captureImageMinimumSize(captureImageMinimumSize)
, captureImageMaximumSize(captureImageMaximumSize)
, stateInstanceId{stateInstanceId}
{}
@@ -86,6 +91,8 @@ public:
out << command.edit3dToolStates;
out << command.language;
out << command.stateInstanceId;
out << command.captureImageMinimumSize;
out << command.captureImageMaximumSize;
return out;
}
@@ -105,6 +112,8 @@ public:
in >> command.edit3dToolStates;
in >> command.language;
in >> command.stateInstanceId;
in >> command.captureImageMinimumSize;
in >> command.captureImageMaximumSize;
return in;
}
@@ -122,6 +131,8 @@ public:
QUrl resourceUrl;
QHash<QString, QVariantMap> edit3dToolStates;
QString language;
QSize captureImageMinimumSize;
QSize captureImageMaximumSize;
qint32 stateInstanceId = 0;
};

View File

@@ -38,16 +38,22 @@ namespace QmlDesigner {
namespace {
QImage renderImage(ServerNodeInstance rootNodeInstance)
QImage renderImage(ServerNodeInstance rootNodeInstance, QSize minimumSize, QSize maximumSize)
{
rootNodeInstance.updateDirtyNodeRecursive();
QSize previewImageSize = rootNodeInstance.boundingRect().size().toSize();
if (previewImageSize.isEmpty())
previewImageSize = {150, 150};
if (previewImageSize.isEmpty()) {
previewImageSize = minimumSize;
} else if (previewImageSize.width() < minimumSize.width()
|| previewImageSize.height() < minimumSize.height()) {
previewImageSize.scale(minimumSize, Qt::KeepAspectRatio);
}
if (previewImageSize.width() > 150 || previewImageSize.height() > 150)
previewImageSize.scale({150, 150}, Qt::KeepAspectRatio);
if (previewImageSize.width() > maximumSize.width()
|| previewImageSize.height() > maximumSize.height()) {
previewImageSize.scale(maximumSize, Qt::KeepAspectRatio);
}
QImage previewImage = rootNodeInstance.renderPreviewImage(previewImageSize);
@@ -73,7 +79,7 @@ void Qt5CaptureImageNodeInstanceServer::collectItemChangesAndSendChangeCommands(
DesignerSupport::polishItems(quickWindow());
QImage image = renderImage(rooNodeInstance);
QImage image = renderImage(rooNodeInstance, m_minimumSize, m_maximumSize);
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

View File

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

View File

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

View File

@@ -95,6 +95,15 @@ Project {
@if %{IsQt6Project}
/* If any modules the project imports require widgets (e.g. QtCharts), widgetApp must be 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
multilanguageSupport: true

View File

@@ -84,10 +84,13 @@ void NamePrettyPrinter::visit(const TemplateNameId *name)
TemplateArgument templArg = name->templateArgumentAt(index);
QString arg;
if (templArg.type().isValid())
arg = overview()->prettyType(templArg.type());
else if (const NumericLiteral *num = templArg.numericLiteral())
if (templArg.type().isValid()) {
Overview o = *_overview;
o.showReturnTypes = true;
arg = o.prettyType(templArg.type());
} else if (const NumericLiteral *num = templArg.numericLiteral()) {
arg = QString::fromLatin1(num->chars(), num->size());
}
if (arg.isEmpty())
_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)
m_beforeStartAdbCommands.append(QString("shell %1").arg(shellCmd));
}
const auto data = runner->recordedData(Constants::ANDROID_PRESTARTSHELLCMDLIST).toStringList();
for (const QString &shellCmd : data)
const auto preStartCmdList = runner->recordedData(Constants::ANDROID_PRESTARTSHELLCMDLIST);
for (const QString &shellCmd : preStartCmdList.toStringList())
m_beforeStartAdbCommands.append(QString("shell %1").arg(shellCmd));
if (auto aspect = runControl->aspect(Constants::ANDROID_POSTFINISHSHELLCMDLIST)) {
@@ -300,8 +300,8 @@ AndroidRunnerWorker::AndroidRunnerWorker(RunWorker *runner, const QString &packa
for (const QString &shellCmd : commands)
m_afterFinishAdbCommands.append(QString("shell %1").arg(shellCmd));
}
const auto data2 = runner->recordedData(Constants::ANDROID_POSTFINISHSHELLCMDLIST).toStringList();
for (const QString &shellCmd : data)
const auto postFinishCmdList = runner->recordedData(Constants::ANDROID_POSTFINISHSHELLCMDLIST);
for (const QString &shellCmd : postFinishCmdList.toStringList())
m_afterFinishAdbCommands.append(QString("shell %1").arg(shellCmd));
m_debugServerPath = debugServer(m_useLldb, target).toString();

View File

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

View File

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

View File

@@ -1321,6 +1321,14 @@ void ClangdTestHighlighting::test_data()
<< QList<int>{C_FIELD} << 0;
QTest::newRow("member call on dependent (3)") << 999 << 9 << 999 << 12
<< 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()
@@ -1423,12 +1431,12 @@ void ClangdTestHighlighting::test()
void ClangdTestHighlighting::testIfdefedOutBlocks()
{
QCOMPARE(m_ifdefedOutBlocks.size(), 3);
QCOMPARE(m_ifdefedOutBlocks.at(0).first(), 12033);
QCOMPARE(m_ifdefedOutBlocks.at(0).last(), 12050);
QCOMPARE(m_ifdefedOutBlocks.at(1).first(), 13351);
QCOMPARE(m_ifdefedOutBlocks.at(1).last(), 13364);
QCOMPARE(m_ifdefedOutBlocks.at(2).first(), 13390);
QCOMPARE(m_ifdefedOutBlocks.at(2).last(), 13402);
QCOMPARE(m_ifdefedOutBlocks.at(0).first(), 12056);
QCOMPARE(m_ifdefedOutBlocks.at(0).last(), 12073);
QCOMPARE(m_ifdefedOutBlocks.at(1).first(), 13374);
QCOMPARE(m_ifdefedOutBlocks.at(1).last(), 13387);
QCOMPARE(m_ifdefedOutBlocks.at(2).first(), 13413);
QCOMPARE(m_ifdefedOutBlocks.at(2).last(), 13425);
}

View File

@@ -5,7 +5,7 @@ auto *rawVariable = R"(Vari
auto Character = 'c';
namespace std {
template<typename T> class vector {};
template<typename T> class vector { public: void clear(); };
template<typename T, typename U> class pair {};
}
@@ -999,3 +999,44 @@ public:
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->setSortingEnabled(true);
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->setSelectionBehavior(QAbstractItemView::SelectItems);
m_configView->setAlternatingRowColors(true);
@@ -373,18 +373,15 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildConfiguration *bc)
m_configModel->setConfiguration(m_buildConfiguration->configurationFromCMake());
m_configModel->setInitialParametersConfiguration(
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->setInitialParametersConfiguration(
m_buildConfiguration->initialCMakeConfiguration());
m_buildConfiguration->filterConfigArgumentsFromAdditionalCMakeArguments();
updateFromKit();
m_configView->expandAll();
m_configView->setEnabled(true);
stretcher->stretch();
updateButtonState();
m_showProgressTimer.stop();
m_progressIndicator->hide();
@@ -402,10 +399,6 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildConfiguration *bc)
m_progressIndicator->hide();
updateConfigurationStateSelection();
});
connect(m_configTextFilterModel, &QAbstractItemModel::modelReset, this, [this, stretcher]() {
m_configView->expandAll();
stretcher->stretch();
});
connect(m_configModel, &QAbstractItemModel::dataChanged,
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 textHighlight = 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 searchTermLengthPixels = painter->fontMetrics().horizontalAdvance(textHighlight);
int searchTermStartPixels = option.fontMetrics.horizontalAdvance(textBefore);
int searchTermLengthPixels = option.fontMetrics.horizontalAdvance(textHighlight);
// rects
QRect beforeHighlightRect(rect);

View File

@@ -3273,6 +3273,58 @@ void QuickfixTest::testGenerateGetterSetterOnlySetter()
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()
{
QList<TestDocumentPtr> testDocuments;
@@ -3362,6 +3414,43 @@ void QuickfixTest::testGenerateGetterSetterOnlySetterHeaderFileWithIncludeGuard(
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 {
public:
CppCodeStyleSettingsChanger(const CppCodeStyleSettings &settings);

View File

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

View File

@@ -3907,7 +3907,12 @@ void GetterSetterRefactoringHelper::performGeneration(ExistingGetterSetterData d
else
getterInClassDeclaration += QLatin1String(" const");
getterInClassDeclaration.prepend(m_settings->getterAttributes + QLatin1Char(' '));
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) {
getterInClassDeclaration += QLatin1String("\n{\nreturn ") + returnExpression
+ QLatin1String(";\n}\n");
@@ -4026,6 +4031,10 @@ void GetterSetterRefactoringHelper::performGeneration(ExistingGetterSetterData d
body += "}";
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())
setterLocation = CppQuickFixSettings::FunctionLocation::OutsideClass;
@@ -4100,6 +4109,10 @@ void GetterSetterRefactoringHelper::performGeneration(ExistingGetterSetterData d
body.replace(QRegularExpression("\\b" + parameterName + "\\b"), "defaultValue");
// body.count('\n') - 2 : do not count the 2 at start
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())
resetLocation = CppQuickFixSettings::FunctionLocation::OutsideClass;

View File

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

View File

@@ -435,17 +435,22 @@ void GenericBuildSystem::parse(RefreshOptions options)
if (options & Configuration) {
m_rawProjectIncludePaths = readLines(m_includesFileName);
Utils::FilePaths normalPaths;
Utils::FilePaths frameworkPaths;
QStringList normalPaths;
QStringList frameworkPaths;
const auto baseDir = Utils::FilePath::fromString(m_includesFileName).parentDir();
for (const QString &rawPath : qAsConst(m_rawProjectIncludePaths)) {
if (rawPath.startsWith("-F"))
frameworkPaths << baseDir.resolvePath(rawPath.mid(2));
frameworkPaths << rawPath.mid(2);
else
normalPaths << baseDir.resolvePath(rawPath);
normalPaths << rawPath;
}
m_projectIncludePaths = toUserHeaderPaths(normalPaths);
m_projectIncludePaths << toFrameworkHeaderPaths(frameworkPaths);
const auto expandedPaths = [this](const QStringList &paths) {
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_cflags = readFlags(m_cflagsFileName);
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -41,29 +41,6 @@ typedef unsigned char RGBE[4];
#define B 2
#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)
{
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[G] = convertComponent(scanline[i][E], scanline[i][G]);
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;
dst[0] = c.m;
dst[1] = b.m;
dst[2] = a.m;
auto getColor = [](float src) -> quint8 {
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;
}
}

View File

@@ -33,6 +33,8 @@
#include <qmldesignerplugin.h>
#include <qmldesignerconstants.h>
#include <QFile>
namespace QmlDesigner {
static inline bool itemsHaveSameParent(const QList<ModelNode> &siblingList)
@@ -94,10 +96,24 @@ bool selectionHasSameParent(const SelectionContext &selectionState)
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)
{
return selectionState.currentSingleSelectedNode().isValid()
&& selectionState.currentSingleSelectedNode().isComponent();
&& selectionState.currentSingleSelectedNode().isComponent()
&& fileComponentExists(selectionState.currentSingleSelectedNode());
}
bool selectionIsImported3DAsset(const SelectionContext &selectionState)

View File

@@ -55,6 +55,7 @@
#include <QScrollArea>
#include <QMessageBox>
#include <QFileDialog>
#include <QVBoxLayout>
namespace QmlDesigner {
@@ -73,7 +74,11 @@ static void addFormattedMessage(Utils::OutputFormatter *formatter, const QString
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->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;
auto doc = QmlDesignerPlugin::instance()->currentDesignDocument();
if (doc) {
@@ -196,6 +207,13 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog(
++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
QMap<QString, int> tabMap; // QMap used for alphabetical order
for (const auto &file : qAsConst(m_quick3DFiles)) {
@@ -214,22 +232,21 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog(
auto tabIt = tabMap.constBegin();
while (tabIt != tabMap.constEnd()) {
createTab(tabIt.key(), tabIt.value(), groups[tabIt.value()]);
++tabIt;
}
// Pad all tabs to same height
for (int i = 0; i < ui->tabWidget->count(); ++i) {
auto optionsArea = qobject_cast<QScrollArea *>(ui->tabWidget->widget(i));
if (optionsArea && optionsArea->widget()) {
auto grid = qobject_cast<QGridLayout *>(optionsArea->widget()->layout());
auto padGrid = [](QWidget *widget, int optionRows) {
auto grid = qobject_cast<QGridLayout *>(widget->layout());
if (grid) {
int rows = grid->rowCount();
for (int j = rows; j < m_optionsRows; ++j) {
grid->addWidget(new QWidget(optionsArea->widget()), j, 0);
grid->setRowMinimumHeight(j, rowHeight);
for (int i = rows; i <optionRows; ++i) {
grid->addWidget(new QWidget(widget), i, 0);
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);
@@ -257,8 +274,10 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog(
for (const auto &file : qAsConst(m_quick3DFiles))
addInfo(file);
QTimer::singleShot(0, [this]() {
ui->tabWidget->setMaximumHeight(m_optionsHeight + ui->tabWidget->tabBar()->height() + 10);
connect(ui->advancedSettingsButton, &QPushButton::clicked,
this, &ItemLibraryAssetImportDialog::toggleAdvanced);
QTimer::singleShot(0, this, [this]() {
updateUi();
});
}
@@ -390,28 +409,45 @@ void ItemLibraryAssetImportDialog::updateImport(const ModelNode &updateNode,
void ItemLibraryAssetImportDialog::createTab(const QString &tabLabel, int optionsIndex,
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);
optionsArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
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(1, labelMinWidth);
layout->setColumnMinimumWidth(2, controlMinWidth);
@@ -427,14 +463,29 @@ void ItemLibraryAssetImportDialog::createTab(const QString &tabLabel, int option
layout->setColumnStretch(5, 4);
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 *>>());
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 QJsonArray items = group.toObject().value("items").toArray();
for (const auto &item : items)
optionToGroupMap.insert(item.toString(), name);
auto groupLabel = new QLabel(name, optionsAreaContents);
auto groupLabel = new QLabel(name, contentWidget);
QFont labelFont = groupLabel->font();
labelFont.setBold(true);
groupLabel->setFont(labelFont);
@@ -442,8 +493,11 @@ void ItemLibraryAssetImportDialog::createTab(const QString &tabLabel, int option
groupIndexMap.insert(name, widgets.size() - 1);
}
QJsonObject &options = m_importOptions[optionsIndex];
const auto optKeys = options.keys();
for (const auto &optKey : optKeys) {
if (!advanced && !isSimpleOption(optKey))
continue;
QJsonObject optObj = options.value(optKey).toObject();
const QString optName = optObj.value("name").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");
QJsonArray conditions = optObj.value("conditions").toArray();
auto *optLabel = new QLabel(optionsAreaContents);
auto *optLabel = new QLabel(contentWidget);
optLabel->setText(optName);
optLabel->setToolTip(optDesc);
QWidget *optControl = nullptr;
if (optType == "Boolean") {
auto *optCheck = new QCheckBox(optionsAreaContents);
auto *optCheck = new QCheckBox(contentWidget);
optCheck->setChecked(optValue.toBool());
optControl = optCheck;
QObject::connect(optCheck, &QCheckBox::toggled,
[this, optCheck, optKey, optionsIndex]() {
QJsonObject optObj = m_importOptions[optionsIndex].value(optKey).toObject();
QJsonValue value(optCheck->isChecked());
optObj.insert("value", value);
m_importOptions[optionsIndex].insert(optKey, optObj);
});
if (advanced) {
QObject::connect(optCheck, &QCheckBox::toggled, this,
[this, optCheck, optKey, optionsIndex]() {
QJsonObject optObj = m_importOptions[optionsIndex].value(optKey).toObject();
QJsonValue value(optCheck->isChecked());
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") {
auto *optSpin = new QDoubleSpinBox(optionsAreaContents);
auto *optSpin = new QDoubleSpinBox(contentWidget);
double min = -999999999.;
double max = 999999999.;
double step = 1.;
@@ -493,13 +564,31 @@ void ItemLibraryAssetImportDialog::createTab(const QString &tabLabel, int option
optSpin->setSingleStep(step);
optSpin->setMinimumWidth(controlMinWidth);
optControl = optSpin;
QObject::connect(optSpin, QOverload<double>::of(&QDoubleSpinBox::valueChanged),
[this, optSpin, optKey, optionsIndex]() {
QJsonObject optObj = m_importOptions[optionsIndex].value(optKey).toObject();
QJsonValue value(optSpin->value());
optObj.insert("value", value);
m_importOptions[optionsIndex].insert(optKey, optObj);
});
if (advanced) {
QObject::connect(optSpin, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this,
[this, optSpin, optKey, optionsIndex]() {
QJsonObject optObj = m_importOptions[optionsIndex].value(optKey).toObject();
QJsonValue value(optSpin->value());
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 {
qWarning() << __FUNCTION__ << "Unsupported option type:" << optType;
continue;
@@ -515,6 +604,8 @@ void ItemLibraryAssetImportDialog::createTab(const QString &tabLabel, int option
else
widgets[0].append({optLabel, optControl});
optionToWidgetsMap.insert(optKey, {optLabel, optControl});
if (advanced)
m_labelToControlWidgetMaps[optionsIndex].insert(optKey, optControl);
}
// Handle conditions
@@ -562,7 +653,7 @@ void ItemLibraryAssetImportDialog::createTab(const QString &tabLabel, int option
else
conditionalWidgetMap.insert(optCb, conControl);
QObject::connect(
optCb, &QCheckBox::toggled,
optCb, &QCheckBox::toggled, optCb,
[optCb, conLabel, conControl, mode, enableConditionally]() {
enableConditionally(optCb, conLabel, conControl, mode);
});
@@ -586,7 +677,7 @@ void ItemLibraryAssetImportDialog::createTab(const QString &tabLabel, int option
enableConditionally(optSpin, conLabel, conControl, mode);
QObject::connect(
optSpin, QOverload<double>::of(&QDoubleSpinBox::valueChanged),
[optSpin, conLabel, conControl, mode, enableConditionally]() {
optSpin, [optSpin, conLabel, conControl, mode, enableConditionally]() {
enableConditionally(optSpin, conLabel, conControl, mode);
});
}
@@ -646,8 +737,13 @@ void ItemLibraryAssetImportDialog::createTab(const QString &tabLabel, int option
};
if (widgets.size() == 1 && widgets[0].isEmpty()) {
layout->addWidget(new QLabel(tr("No options available for this type."),
optionsAreaContents), 0, 0, 2, 7, Qt::AlignCenter);
if (advanced) {
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);
}
@@ -663,7 +759,7 @@ void ItemLibraryAssetImportDialog::createTab(const QString &tabLabel, int option
for (int j = 1; j < groupWidgets.size(); ++j)
insertOptionToLayout(col, groupWidgets[j]);
// Add a separator line after each group
auto *separator = new QFrame(optionsAreaContents);
auto *separator = new QFrame(contentWidget);
separator->setMaximumHeight(1);
separator->setFrameShape(QFrame::HLine);
separator->setFrameShadow(QFrame::Sunken);
@@ -681,38 +777,56 @@ void ItemLibraryAssetImportDialog::createTab(const QString &tabLabel, int option
}
int optionRows = qMax(rowIndex[0], rowIndex[1]);
m_optionsRows = qMax(m_optionsRows, optionRows);
m_optionsHeight = qMax(rowHeight * optionRows + 16, m_optionsHeight);
layout->setContentsMargins(8, 8, 8, 8);
optionsAreaContents->setContentsMargins(0, 0, 0, 0);
optionsAreaContents->setLayout(layout);
optionsAreaContents->setMinimumWidth(
(checkBoxColWidth + labelMinWidth + controlMinWidth) * 2 + columnSpacing);
optionsAreaContents->setObjectName("optionsAreaContents"); // For stylesheet
int &globalOptionRows = advanced ? m_advancedData.optionsRows : m_simpleData.optionsRows;
int &globalOptionsHeight = advanced ? m_advancedData.optionsHeight : m_simpleData.optionsHeight;
globalOptionRows = qMax(globalOptionRows, optionRows);
globalOptionsHeight = qMax(rowHeight * optionRows + 20, globalOptionsHeight);
layout->setContentsMargins(8, 8, 8, 0);
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));
return layout;
}
void ItemLibraryAssetImportDialog::updateUi()
{
auto optionsArea = qobject_cast<QScrollArea *>(ui->tabWidget->currentWidget());
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();
int scrollBarWidth = optionsArea->verticalScrollBar()->isVisible()
? 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)
{
Q_UNUSED(event)
m_dialogHeight = event->size().height();
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 <QSet>
QT_BEGIN_NAMESPACE
class QGridLayout;
QT_END_NAMESPACE
namespace Utils {
class OutputFormatter;
}
@@ -76,21 +80,37 @@ private:
void onImportNearlyFinished();
void onImportFinished();
void onClose();
void toggleAdvanced();
void createTab(const QString &tabLabel, int optionsIndex, const QJsonObject &groups);
QGridLayout *createOptionsGrid(QWidget *contentWidget, bool advanced, int optionsIndex,
const QJsonObject &groups);
void updateUi();
bool isSimpleGroup(const QString &id);
bool isSimpleOption(const QString &id);
Ui::ItemLibraryAssetImportDialog *ui = nullptr;
Utils::OutputFormatter *m_outputFormatter = nullptr;
struct OptionsData
{
int optionsRows = 0;
int optionsHeight = 0;
QList<QWidget *> contentWidgets; // Tab content widgets
};
QStringList m_quick3DFiles;
QString m_quick3DImportPath;
ItemLibraryAssetImporter m_importer;
QVector<QJsonObject> m_importOptions;
QHash<QString, int> m_extToImportOptionsMap;
int m_optionsHeight = 0;
int m_optionsRows = 0;
QSet<QString> m_preselectedFilesForOverwrite;
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>
<x>0</x>
<y>0</y>
<width>631</width>
<height>750</height>
<width>630</width>
<height>350</height>
</rect>
</property>
<property name="windowTitle">
<string>Asset Import</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>2</verstretch>
</sizepolicy>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<property name="tabBarAutoHide">
<bool>false</bool>
</property>
<widget class="QWidget" name="modelOptionsTab">
<attribute name="title">
<string>Import Options</string>
</attribute>
</widget>
</widget>
</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="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>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0" colspan="2">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>2</verstretch>
</sizepolicy>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<property name="tabBarAutoHide">
<bool>false</bool>
</property>
<widget class="QWidget" name="modelOptionsTab">
<attribute name="title">
<string>Import Options</string>
</attribute>
</widget>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>80</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="advancedSettingsButton">
<property name="text">
<string>Show All Settings</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<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>
</layout>
</widget>

View File

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

View File

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

View File

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

View File

@@ -28,30 +28,87 @@
#include <utils/stylehelper.h>
#include <theme.h>
#include <QAction>
#include <QBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QStyle>
#include <QToolButton>
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)
: QWidget(parent)
{
auto layout = new QBoxLayout(QBoxLayout::LeftToRight);
layout->setSpacing(0);
layout->setContentsMargins(5, 5, 5, 3);
setLayout(layout);
const QString fontName = "qtds_propertyIconFont.ttf";
const int iconSize = 15;
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 = new LineEdit;
m_textField->setPlaceholderText(tr("Search"));
m_textField->setFrame(false);
m_textField->setClearButtonEnabled(true);
m_textField->addAction(searchIcon, QLineEdit::LeadingPosition);
connect(m_textField, &QLineEdit::textChanged, this, &NavigatorSearchWidget::textChanged);

View File

@@ -27,8 +27,27 @@
#include <QLineEdit>
class QToolButton;
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
{
Q_OBJECT
@@ -42,8 +61,7 @@ signals:
void textChanged(const QString &text);
private:
QLineEdit *m_textField;
LineEdit *m_textField;
};
} //QmlDesigner

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -98,6 +98,7 @@
#include <projectexplorer/target.h>
#include <qmlprojectmanager/qmlmultilanguageaspect.h>
#include <qmlprojectmanager/qmlproject.h>
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
@@ -113,6 +114,8 @@
#include <QDirIterator>
#include <QFileSystemWatcher>
#include <QScopedPointer>
#include <QThread>
#include <QApplication>
enum {
debug = false
@@ -174,9 +177,6 @@ NodeInstanceView::NodeInstanceView(ConnectionManagerInterface &connectionManager
m_generateQsbFilesTimer.setInterval(100);
QObject::connect(&m_generateQsbFilesTimer, &QTimer::timeout, [this] {
handleShaderChanges();
if (m_qsbPath.isEmpty() || m_remainingQsbTargets <= 0)
m_resetTimer.start();
});
connect(m_fileSystemWatcher, &QFileSystemWatcher::directoryChanged,
@@ -196,8 +196,12 @@ NodeInstanceView::NodeInstanceView(ConnectionManagerInterface &connectionManager
});
connect(m_fileSystemWatcher, &QFileSystemWatcher::fileChanged, [this](const QString &path) {
m_pendingQsbTargets.insert(path);
m_generateQsbFilesTimer.start();
if (m_qsbTargets.contains(path)) {
m_qsbTargets.insert(path, true);
m_generateQsbFilesTimer.start();
} else if (m_remainingQsbTargets <= 0) {
m_resetTimer.start();
}
});
m_rotBlockTimer.setSingleShot(true);
@@ -277,7 +281,15 @@ void NodeInstanceView::modelAttached(Model *model)
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)
@@ -303,6 +315,9 @@ void NodeInstanceView::modelAboutToBeDetached(Model * model)
m_pendingUpdateDirs.clear();
m_fileSystemWatcher->removePaths(m_fileSystemWatcher->directories());
m_fileSystemWatcher->removePaths(m_fileSystemWatcher->files());
m_generateQsbFilesTimer.stop();
m_qsbTargets.clear();
}
void NodeInstanceView::handleCrash()
@@ -1123,24 +1138,27 @@ CreateSceneCommand NodeInstanceView::createCreateSceneCommand()
if (stateNode.isValid() && stateNode.metaInfo().isSubclassOf("QtQuick.State", 1, 0))
stateInstanceId = stateNode.internalId();
return CreateSceneCommand(instanceContainerList,
reparentContainerList,
idContainerList,
valueContainerList,
bindingContainerList,
auxiliaryContainerVector,
importVector,
mockupTypesVector,
model()->fileUrl(),
return CreateSceneCommand(
instanceContainerList,
reparentContainerList,
idContainerList,
valueContainerList,
bindingContainerList,
auxiliaryContainerVector,
importVector,
mockupTypesVector,
model()->fileUrl(),
#ifndef QMLDESIGNER_TEST
QUrl::fromLocalFile(QmlDesigner::DocumentManager::currentResourcePath()
.toFileInfo().absoluteFilePath()),
QUrl::fromLocalFile(
QmlDesigner::DocumentManager::currentResourcePath().toFileInfo().absoluteFilePath()),
#else
QUrl::fromLocalFile(QFileInfo(model()->fileUrl().toLocalFile()).absolutePath()),
QUrl::fromLocalFile(QFileInfo(model()->fileUrl().toLocalFile()).absolutePath()),
#endif
m_edit3DToolStates[model()->fileUrl()],
lastUsedLanguage,
stateInstanceId);
m_edit3DToolStates[model()->fileUrl()],
lastUsedLanguage,
m_captureImageMinimumSize,
m_captureImageMaximumSize,
stateInstanceId);
}
ClearSceneCommand NodeInstanceView::createClearSceneCommand() const
@@ -1488,9 +1506,6 @@ void NodeInstanceView::setTarget(ProjectExplorer::Target *newTarget)
}
}
m_generateQsbFilesTimer.stop();
m_pendingQsbTargets.clear();
m_remainingQsbTargets = 0;
restartProcess();
}
}
@@ -1885,12 +1900,18 @@ void NodeInstanceView::updateWatcher(const QString &path)
QStringList oldDirs;
QStringList newFiles;
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 directories = m_fileSystemWatcher->directories();
if (path.isEmpty()) {
// Do full update
rootPath = QFileInfo(model()->fileUrl().toLocalFile()).absolutePath();
rootPath = projPath;
if (!directories.isEmpty())
m_fileSystemWatcher->removePaths(directories);
if (!files.isEmpty())
@@ -1916,12 +1937,47 @@ void NodeInstanceView::updateWatcher(const QString &path)
// Common shader suffixes
static const QStringList filterList {"*.frag", "*.vert",
"*.glsl", "*.glslv", "*.glslf",
"*.vsh","*.fsh"};
"*.vsh", "*.fsh"};
QDirIterator fileIterator(rootPath, filterList, QDir::Files, QDirIterator::Subdirectories);
while (fileIterator.hasNext())
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.isEmpty())
m_fileSystemWatcher->removePaths(oldDirs);
@@ -1934,15 +1990,10 @@ void NodeInstanceView::updateWatcher(const QString &path)
m_fileSystemWatcher->removePaths(oldFiles);
if (!newFiles.isEmpty())
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)
@@ -1969,51 +2020,102 @@ void NodeInstanceView::handleQsbProcessExit(Utils::QtcProcess *qsbProcess, const
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)) {
// Run qsb for changed shader file
if (!m_qsbPath.isEmpty() && !shader.isEmpty()) {
const Utils::FilePath sourceFile = Utils::FilePath::fromString(shader);
const Utils::FilePath srcPath = sourceFile.absolutePath();
const Utils::FilePath outPath = Utils::FilePath::fromString(shader + ".qsb");
if (!sourceFile.exists() || (outPath.exists() && outPath.lastModified() > sourceFile.lastModified())) {
--m_remainingQsbTargets;
continue;
}
// Run QSB with same parameters as Qt build does
// TODO: Parameters should be configurable (QDS-6590)
const QStringList args = {"-s", "--glsl", "100 es,120,150", "--hlsl", "50", "--msl", "12",
"-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();
});
#ifndef QMLDESIGNER_TEST
const QString projPath = QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath().toString();
#else
const QString projPath = QFileInfo(model()->fileUrl().toLocalFile()).absolutePath();
#endif
// Parse ShaderTool files from project configuration.
// Separate files to path and file name (called filter here as it can contain wildcards)
// and group filters by paths. Blank path indicates project-wide file wildcard.
for (const auto &file : shaderToolFiles) {
int idx = file.lastIndexOf('/');
QString key;
QString filter;
if (idx >= 0) {
key = projPath + "/" + file.left(idx);
filter = file.mid(idx + 1);
} 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()

View File

@@ -353,7 +353,7 @@ void GenerateResource::generateMenuEntry()
// ToDo: move this to QtCreator and add tr to the string then
auto rccAction = new QAction(QCoreApplication::translate("QmlDesigner::GenerateResource",
"Generate RCC Resource File"));
"Generate Deployable Package"));
rccAction->setEnabled(ProjectExplorer::SessionManager::startupProject() != nullptr);
QObject::connect(ProjectExplorer::SessionManager::instance(),
&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 char EVENT_TIMELINE_ADDED[] = "Timeline Added";
const char EVENT_TRANSITION_ADDED[] = "Transition Added";
const char EVENT_STATE_ADDED[] = "State Added";
const char EVENT_CONNECTION_ADDED[] = "Connection Added";
const char EVENT_PROPERTY_ADDED[] = "Property Added";
const char EVENT_ANNOTATION_ADDED[] = "Annotation Added";
const char EVENT_RESOURCE_IMPORTED[] = "Resource Imported ";
const char EVENT_ACTION_EXECUTED[] = "Action Executed ";
const char EVENT_HELP_REQUESTED[] = "Help Requested ";
const char EVENT_IMPORT_ADDED[] = "Import Added ";
const char EVENT_BINDINGEDITOR_OPENED[] = "Binding Editor Opened";
const char EVENT_RICHTEXT_OPENED[] = "Richtext Editor Opened";
const char EVENT_FORMEDITOR_TIME[] = "Form Editor";
const char EVENT_3DEDITOR_TIME[] = "3D Editor";
const char EVENT_TIMELINE_TIME[] = "Timeline";
const char EVENT_TRANSITIONEDITOR_TIME[] = "Transition Editor";
const char EVENT_CURVEDITOR_TIME[] = "Curve Editor";
const char EVENT_TIMELINE_ADDED[] = "timelineAdded";
const char EVENT_TRANSITION_ADDED[] = "transitionAdded";
const char EVENT_STATE_ADDED[] = "stateAdded";
const char EVENT_CONNECTION_ADDED[] = "connectionAdded";
const char EVENT_PROPERTY_ADDED[] = "propertyAdded";
const char EVENT_ANNOTATION_ADDED[] = "annotationAdded";
const char EVENT_RESOURCE_IMPORTED[] = "resourceImported";
const char EVENT_ACTION_EXECUTED[] = "actionExecuted";
const char EVENT_HELP_REQUESTED[] = "helpRequested";
const char EVENT_IMPORT_ADDED[] = "importAdded:";
const char EVENT_BINDINGEDITOR_OPENED[] = "bindingEditorOpened";
const char EVENT_RICHTEXT_OPENED[] = "richtextEditorOpened";
const char EVENT_FORMEDITOR_TIME[] = "formEditor";
const char EVENT_3DEDITOR_TIME[] = "3DEditor";
const char EVENT_TIMELINE_TIME[] = "timeline";
const char EVENT_TRANSITIONEDITOR_TIME[] = "transitionEditor";
const char EVENT_CURVEDITOR_TIME[] = "curveEditor";
const char PROPERTY_EDITOR_CLASSNAME_PROPERTY[] = "__classNamePrivateInternal";

View File

@@ -100,6 +100,16 @@ namespace QmlDesigner {
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
{
public:
@@ -318,7 +328,7 @@ bool QmlDesignerPlugin::delayedInitialize()
emitUsageStatistics("StandaloneMode");
if (QmlProjectManager::QmlProject::isQtDesignStudioStartedFromQtC())
emitUsageStatistics("QDSlaunchedFromQtC");
emitUsageStatistics("QDSstartupCount");
emitUsageStatistics("qdsStartupCount");
FoundLicense license = checkLicense();
if (license == FoundLicense::enterprise)
@@ -353,7 +363,7 @@ void QmlDesignerPlugin::extensionsInitialized()
ExtensionSystem::IPlugin::ShutdownFlag QmlDesignerPlugin::aboutToShutdown()
{
if (QmlProjectManager::QmlProject::isQtDesignStudio())
emitUsageStatistics("QDSstartupCount");
emitUsageStatistics("qdsShutdownCount");
return SynchronousShutdown;
}
@@ -642,7 +652,8 @@ double QmlDesignerPlugin::formEditorDevicePixelRatio()
void QmlDesignerPlugin::emitUsageStatistics(const QString &identifier)
{
QTC_ASSERT(instance(), return);
emit instance()->usageStatisticsNotifier(identifier);
emit instance()->usageStatisticsNotifier(normalizeIdentifier(identifier));
qDebug() << normalizeIdentifier(identifier);
}
void QmlDesignerPlugin::emitUsageStatisticsContextAction(const QString &identifier)
@@ -667,7 +678,9 @@ void QmlDesignerPlugin::registerPreviewImageProvider(QQmlEngine *engine)
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()

View File

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

View File

@@ -161,6 +161,26 @@ QmlProjectItem *QmlProjectFileFormat::parseProjectFile(const Utils::FilePath &fi
projectItem->addToEnviroment(i.key(), i.value().value.toString());
++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 {
qWarning() << "Unknown type:" << childNode->name();
}

View File

@@ -84,6 +84,12 @@ public:
bool widgetApp() const { return m_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); }
Utils::EnvironmentItems environment() const;
@@ -107,6 +113,8 @@ protected:
bool m_qtForMCUs = false;
bool m_qt6Project = false;
bool m_widgetApp = false;
QStringList m_shaderToolArgs;
QStringList m_shaderToolFiles;
};
} // namespace QmlProjectManager

View File

@@ -626,6 +626,20 @@ bool QmlBuildSystem::widgetApp() const
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 *)
{
if (!dynamic_cast<QmlProjectNode *>(context))

View File

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

View File

@@ -42,6 +42,7 @@
#include <qtsupport/qtversionmanager.h>
#include <utils/algorithm.h>
#include <utils/filepath.h>
#include <utils/fileutils.h>
#include <utils/qtcassert.h>
#include <utils/stringutils.h>
@@ -50,6 +51,8 @@
#include <algorithm>
#include <memory>
using namespace Utils;
namespace QtSupport {
namespace Internal {
@@ -101,12 +104,21 @@ ExampleSetModel::ExampleSetModel()
qWarning() << "Manifest path " << set.manifestPath << "is not a readable directory, ignoring";
continue;
}
m_extraExampleSets.append(set);
if (debugExamples()) {
qWarning() << "Adding examples set displayName=" << set.displayName
<< ", manifestPath=" << set.manifestPath
<< ", 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();

View File

@@ -91,10 +91,6 @@
<tags>qtformcus,mcus,qt,video,2021</tags>
</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">
<description><![CDATA[Adding custom file and project creation wizards to Qt Creator.]]></description>
<tags>qt creator,wizard,talk,2015</tags>

View File

@@ -41,6 +41,8 @@
#include <projectexplorer/projectexplorer.h>
#include <qmldesigner/qmldesignerplugin.h>
#include <QDialog>
#include <QFileDialog>
#include <QFileInfo>
@@ -83,6 +85,7 @@ FileDownloader::~FileDownloader()
void FileDownloader::start()
{
QmlDesigner::QmlDesignerPlugin::emitUsageStatistics("exampleDownload:" + name());
m_tempFile.setFileName(QDir::tempPath() + "/" + name() + ".XXXXXX" + ".zip");
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
width: 460
height: 480
color: "#11102d"
color: "transparent"
layer.enabled: true
layer.textureSize: Qt.size(width * 2, height * 2)
@@ -37,6 +37,7 @@ Rectangle {
Item {
id: composition
anchors.centerIn: parent
width: 460
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 {
id: welcome_splash
width: 800
height: 480
anchors.fill: parent
gradient: Gradient {
orientation: Gradient.Horizontal
@@ -46,43 +45,27 @@ Rectangle {
signal closeClicked
signal configureClicked
property alias doNotShowAgain: doNotShowCheckBox.checked
property bool doNotShowAgain: true
property bool loadingPlugins: true
// 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
}
}
color: "#1d212a"
Image {
id: logo
x: 15
y: 11
width: 66
height: 50
anchors.top: parent.top
anchors.left: parent.left
anchors.margins: 10
width: 66 * 2
height: 50 * 2
smooth: true
source: "welcome_windows/logo.png"
}
Text {
id: qt_design_studio
x: 13
y: 81
width: 336
height: 46
id: qt_design_studio_text
anchors.top: logo.top
anchors.left: logo.right
anchors.leftMargin: 10
color: "#25709a"
text: qsTr("Qt Design Studio")
font.pixelSize: 36
@@ -90,156 +73,89 @@ Rectangle {
}
Text {
id: software_for_ui
x: 15
y: 126
width: 300
height: 30
color: "#ffffff"
text: qsTr("Software for UI and UX Designers")
renderType: Text.QtRendering
font.pixelSize: 15
id: qt_design_studio_version_text
anchors.left: qt_design_studio_text.right
anchors.baseline: qt_design_studio_text.baseline
anchors.leftMargin: 10
color: "#25709a"
text: usageStatisticModel.version
font.family: StudioFonts.titilliumWeb_light
font.pixelSize: 36
}
Text {
id: copyright
x: 15
y: 155
width: 270
height: 24
id: license_variant_text
anchors.left: qt_design_studio_text.left
anchors.top: qt_design_studio_text.bottom
anchors.leftMargin: 5
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.pixelSize: 20
Text {
id: marketing_1
x: 15
y: 206
width: 406
height: 31
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: {
if (projectModel.communityVersion)
return qsTr("Community Edition")
if (projectModel.enterpriseVersion)
return qsTr("Enterprise Edition")
return qsTr("Professional Edition")
}
Text {
id: marketing_2
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
}
ProjectModel {
id: projectModel
}
Text {
id: marketing_3
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
UsageStatisticModel {
id: usageStatisticModel
}
}
Dof_Effect {
id: dof_Effect
x: 358
id: dof_effect
anchors.top: qt_design_studio_text.bottom
anchors.horizontalCenter: welcome_splash.horizontalCenter
width: 442
height: 480
visible: true
maskBlurSamples: 64
maskBlurRadius: 32
Splash_Image25d {
id: animated_artwork
x: 358
y: 0
width: 442
height: 480
width: dof_effect.width
height: dof_effect.height
clip: true
}
}
Image {
id: close_window
anchors.top: parent.top
Text {
id: help_us_text
anchors.left: welcome_splash.left
anchors.right: parent.right
anchors.margins: 8
width: 13
height: 13
fillMode: Image.PreserveAspectFit
source: "welcome_windows/close.png"
opacity: area.containsMouse ? 1 : 0.8
anchors.leftMargin: 10
anchors.top: dof_effect.bottom
anchors.topMargin: 10
color: "#FFFFFF"
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.")
MouseArea {
id: area
hoverEnabled: true
anchors.fill: parent
anchors.margins: -10
onClicked: welcome_splash.closeClicked()
}
font.family: StudioFonts.titilliumWeb_light
font.pixelSize: 18
wrapMode: Text.WordWrap
anchors.rightMargin: 10
}
ColumnLayout {
id: columnLayout
anchors.left: parent.left
anchors.bottom: parent.bottom
anchors.leftMargin: 16
anchors.bottomMargin: 10
anchors.top: help_us_text.bottom
anchors.leftMargin: 10
anchors.topMargin: 20
spacing: 3
CheckBox {
visible: false
id: usageStatisticCheckBox
text: qsTr("Enable Usage Statistics")
text: qsTr("Send Usage Statistics")
checked: usageStatisticModel.usageStatisticEnabled
padding: 0
spacing: 12
@@ -255,11 +171,11 @@ Rectangle {
}
CheckBox {
visible: false
id: crashReportCheckBox
text: qsTr("Enable Crash Reports")
text: qsTr("Send Crash Reports")
spacing: 12
checked: usageStatisticModel.crashReporterEnabled
visible: false
onCheckedChanged: {
usageStatisticModel.setCrashReporterEnabled(crashReportCheckBox.checked)
@@ -274,90 +190,41 @@ Rectangle {
}
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 {
x: 16
y: 277
visible: welcome_splash.loadingPlugins
anchors.right: parent.right
anchors.bottom: welcome_splash.bottom
anchors.rightMargin: 10
anchors.bottomMargin: 10
spacing: 20
Text {
id: text1
color: "#ffffff"
text: qsTr("%")
font.pixelSize: 12
RotationAnimator {
target: text1
from: 0
to: 360
duration: 1800
running: true
loops: -1
CustomButton {
text: qsTr("Don't send")
onClicked: {
usageStatisticModel.setTelemetryEnabled(false)
usageStatisticModel.setCrashReporterEnabled(false)
welcome_splash.closeClicked()
}
}
Text {
id: loading_progress
color: "#ffffff"
text: qsTr("Loading Plugins")
font.family: StudioFonts.titilliumWeb_light
font.pixelSize: 16
}
Text {
id: text2
color: "#ffffff"
text: qsTr("%")
font.pixelSize: 12
RotationAnimator {
target: text2
from: 0
to: 360
duration: 2000
running: true
loops: -1
CustomButton {
text: qsTr("Send analytics data")
onClicked: {
usageStatisticModel.setTelemetryEnabled(true)
usageStatisticModel.setCrashReporterEnabled(true)
welcome_splash.closeClicked()
}
}
}
Text {
id: all_rights_reserved1
x: 15
y: 65
color: "#ffffff"
font.pixelSize: 13
font.family: StudioFonts.titilliumWeb_light
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
}
CustomButton {
y: 430
text: qsTr("Learn More")
anchors.left: parent.left
anchors.bottom: parent.bottom
anchors.bottomMargin: 10
anchors.leftMargin: 10
onClicked: Qt.openUrlExternally("https://www.qt.io/terms-conditions/telemetry-privacy")
}
}

View File

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

View File

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

View File

@@ -27,4 +27,5 @@ import QtQuick 2.0
QtObject {
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 crashReporterEnabled MEMBER m_crashReporterEnabled NOTIFY crashReporterEnabledChanged)
Q_PROPERTY(QString version MEMBER m_versionString CONSTANT)
public:
explicit UsageStatisticPluginModel(QObject *parent = nullptr)
: QObject(parent)
{
m_versionString = Core::Constants::IDE_VERSION_DISPLAY;
setupModel();
}
@@ -160,13 +162,10 @@ public:
Core::ICore::settings()->setValue(CRASH_REPORTER_SETTING, b);
s_pluginInstance->pauseRemoveSplashTimer();
const QString restartText = tr("The change will take effect after restart.");
Core::RestartDialog restartDialog(Core::ICore::dialogParent(), restartText);
restartDialog.exec();
s_pluginInstance->resumeRemoveSplashTimer();
setupModel();
}
@@ -179,14 +178,10 @@ public:
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.");
Core::RestartDialog restartDialog(Core::ICore::dialogParent(), restartText);
restartDialog.exec();
s_pluginInstance->resumeRemoveSplashTimer();
setupModel();
}
@@ -197,6 +192,7 @@ signals:
private:
bool m_usageStatisticEnabled = false;
bool m_crashReporterEnabled = false;
QString m_versionString;
};
class ProjectModel : public QAbstractListModel
@@ -249,6 +245,9 @@ public:
const QString &formFile,
const QString &explicitQmlproject)
{
QmlDesigner::QmlDesignerPlugin::emitUsageStatistics("exampleOpened:"
+ exampleName);
const QString exampleFolder = examplePath + "/" + exampleName + "/";
QString projectFile = exampleFolder + exampleName + ".qmlproject";
@@ -517,10 +516,7 @@ void StudioWelcomePlugin::showSystemSettings()
Core::ICore::infoBar()->removeInfo("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);
resumeRemoveSplashTimer();
}
StudioWelcomePlugin::StudioWelcomePlugin()
@@ -543,11 +539,6 @@ bool StudioWelcomePlugin::initialize(const QStringList &arguments, QString *erro
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;
}
@@ -620,8 +611,6 @@ void StudioWelcomePlugin::extensionsInitialized()
s_view->show();
s_view->raise();
m_removeSplashTimer.start();
});
}
}
@@ -633,34 +622,9 @@ bool StudioWelcomePlugin::delayedInitialize()
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;
}
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()
{
QStandardPaths::StandardLocation location = Utils::HostOsInfo::isMacHost()

View File

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

View File

@@ -27,6 +27,7 @@
#include "texteditor_global.h"
#include <QList>
#include <QString>
#include <QMetaType>
#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
// even if we insert text at first column
cursor = textCursor;
if (cursorAtBlockStart) {
cursor = textCursor;
cursor.setPosition(startBlock.position(), QTextCursor::KeepAnchor);
} else if (anchorAtBlockStart) {
cursor = textCursor;
cursor.setPosition(startBlock.position(), QTextCursor::MoveAnchor);
cursor.setPosition(textCursor.position(), QTextCursor::KeepAnchor);
}

View File

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

View File

@@ -226,11 +226,11 @@ def logApplicationOutput():
# get the output from a given cmdline call
def getOutputFromCmdline(cmdline, environment=None, acceptedError=0):
try:
return subprocess.check_output(cmdline, env=environment)
return stringify(subprocess.check_output(cmdline, env=environment))
except subprocess.CalledProcessError as e:
if e.returncode != acceptedError:
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 __closePopupIfNecessary__():
@@ -238,41 +238,29 @@ def selectFromFileDialog(fileName, waitForFile=False, ignoreFinalSnooze=False):
test.log("Closing active popup widget")
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)
nativeType("<Command+Shift+g>")
clickButton(waitForObject("{text='Open' type='QPushButton'}"))
waitFor("str(pathLine.text)==''")
replaceEditorContent(pathLine, fName)
snooze(1)
nativeType(fileName)
snooze(2)
nativeType("<Return>")
snooze(3)
__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(1)
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)
snooze(3)
if waitForFile:
fileCombo = waitForObject(":Qt Creator_FilenameQComboBox")
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("_", "\\_")
for subIndex in dumpIndices(model, rootIndex):
subChildText = str(subIndex.data()).replace(".", "\\.").replace("_", "\\_")
treeView.scrollTo(subIndex)
mouseClick(waitForObjectItem(treeView, ".".join([rootChildText,subChildText])))
currentText = str(waitForObject(":QtSupport__Internal__QtVersionManager.QLabel").text)
matches = pattern.match(currentText)
@@ -684,3 +673,12 @@ def isString(sth):
return isinstance(sth, str)
else:
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,
getOutputFromCmdline(["cmake", "--version"]).splitlines())
try:
test.log("Using " + versionLines[0])
matcher = re.match("cmake version (\d+)\.(\d+)\.\d+", versionLines[0])
versionLine = list(versionLines)[0]
test.log("Using " + versionLine)
matcher = re.match("cmake version (\d+)\.(\d+)\.\d+", versionLine)
major = __builtin__.int(matcher.group(1))
minor = __builtin__.int(matcher.group(2))
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/
#
# This file is part of Qt Creator.
@@ -66,8 +66,8 @@ def main():
availableProjectTypes.append({category:template})
safeClickButton("Cancel")
for current in availableProjectTypes:
category = current.keys()[0]
template = current.values()[0]
category = list(current.keys())[0]
template = list(current.values())[0]
with TestSection("Testing project template %s -> %s" % (category, template)):
displayedPlatforms = __createProject__(category, template)
if template == "Qt Quick Application":