diff --git a/dist/changes-6.0.2.md b/dist/changes-6.0.2.md new file mode 100644 index 00000000000..2b754672661 --- /dev/null +++ b/dist/changes-6.0.2.md @@ -0,0 +1,82 @@ +Qt Creator 6.0.2 +================ + +Qt Creator version 6.0.2 contains bug fixes. + +The most important changes are listed in this document. For a complete list of +changes, see the Git log for the Qt Creator sources that you can check out from +the public Git repository. For example: + + git clone git://code.qt.io/qt-creator/qt-creator.git + git log --cherry-pick --pretty=oneline origin/v6.0.1..v6.0.2 + +General +------- + +* Fixed crash in process launcher (QTCREATORBUG-26726) + +Editing +------- + +* Fixed that `Select All` scrolled to bottom (QTCREATORBUG-26736) +* Fixed copying with block selection (QTCREATORBUG-26761) + +### C++ + +* ClangCodeModel + * Fixed performance regression of code completion on Windows and macOS + (QTCREATORBUG-26754) + +### Python + +* Fixed working directory for `REPL` + +### Modeling + +* Fixed missing options in property editor (QTCREATORBUG-26760) + +Projects +-------- + +* Fixed that closing application in `Application Output` pane killed process + even if `Keep Running` was selected +* Fixed filtering in target setup page (QTCREATORBUG-26779) + +### CMake + +* Fixed that GUI project wizards did not create GUI applications on Windows and + app bundles on macOS + +Platforms +--------- + +### Linux + +* Fixed that commercial plugins linked to libGLX and libOpenGL + (QTCREATORBUG-26744) + +### macOS + +* Fixed crash when switching screen configuration (QTCREATORBUG-26019) + +Credits for these changes go to: +-------------------------------- +Allan Sandfeld Jensen +André Pönitz +Antti Määttä +Christiaan Janssen +Christian Kandeler +Christian Stenger +Cristian Adam +David Schulz +Eike Ziller +Henning Gruendl +Jaroslaw Kobus +Knud Dollereder +Leena Miettinen +Marco Bubke +Mats Honkamaa +Samuel Ghinet +Tapani Mattila +Thomas Hartmann +Tuomo Pelkonen diff --git a/doc/qtcreator/images/qtcreator-embedded-linux-deployment-overview.png b/doc/qtcreator/images/qtcreator-embedded-linux-deployment-overview.png deleted file mode 100644 index 25cdf5c7bf5..00000000000 Binary files a/doc/qtcreator/images/qtcreator-embedded-linux-deployment-overview.png and /dev/null differ diff --git a/doc/qtcreator/src/cmake/creator-projects-cmake-deploying.qdocinc b/doc/qtcreator/src/cmake/creator-projects-cmake-deploying.qdocinc deleted file mode 100644 index abef5908c8c..00000000000 --- a/doc/qtcreator/src/cmake/creator-projects-cmake-deploying.qdocinc +++ /dev/null @@ -1,106 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt Creator 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. -** -****************************************************************************/ - -// ********************************************************************** -// NOTE: the sections are not ordered by their logical order to avoid -// reshuffling the file each time the index order changes (i.e., often). -// Run the fixnavi.pl script to adjust the links to the index order. -// ********************************************************************** - -/*! -//! [cmake deploying embedded] - - \section1 Deploying CMake Projects to Generic Remote Linux Devices - - \QC cannot directly extract files to be installed from a CMake project. - Therefore, a special deploy step is created that installs the project into - a local directory. The files in that directory are then deployed to the - remote device. - Alternatively, you can provide a \c {QtCreatorDeployment.txt} file in which - you must specify all files to be deployed which are not executables or - libraries. You place this file in either the root directory of the CMake - project or the build directory of the active build configuration. - Currently, \QC first checks the root directory and only if no - \c {QtCreatorDeployment.txt} exists it checks the active build directory. - - Use the following syntax in the file: - - \code - - : - ... - : - \endcode - - Where: - - \list - - \li \c {} is the (absolute) path prefix to where - files are copied on the remote machine. - - \li \c {} is the file path relative to the CMake - project root. No directories or wildcards are allowed in this - value. - - \li \c {} is the destination directory path - relative to \c {deployment/prefix}. - - \endlist - - To automate the creation of \c {QtCreatorDeployment.txt} file: - - \list 1 - - \li Define the following macros in the top level \c {CMakeLists.txt} - file: - - \code - file(WRITE "${CMAKE_SOURCE_DIR}/QtCreatorDeployment.txt" "\n") - - macro(add_deployment_file SRC DEST) - file(RELATIVE_PATH path ${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) - file(APPEND "${CMAKE_SOURCE_DIR}/QtCreatorDeployment.txt" "${path}/${SRC}:${DEST}\n") - endmacro() - - macro(add_deployment_directory SRC DEST) - file(GLOB_RECURSE files RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${SRC}/*") - foreach(filename ${files}) - get_filename_component(path ${filename} PATH) - add_deployment_file("${filename}" "${DEST}/${path}") - endforeach(filename) - endmacro() - \endcode - - \li Use \c {add_deployment_file()} to add files and - \c {add_deployment_directory()} to add directories - (including subdirectories) to the \c QtCreatorDeployment.txt file. - - \li Re-run \c cmake after you add or remove files using the macros. - - \endlist - -//! [cmake deploying embedded] -*/ diff --git a/doc/qtcreator/src/cmake/creator-projects-cmake.qdoc b/doc/qtcreator/src/cmake/creator-projects-cmake.qdoc index 80f0e326ea8..0258564a0b0 100644 --- a/doc/qtcreator/src/cmake/creator-projects-cmake.qdoc +++ b/doc/qtcreator/src/cmake/creator-projects-cmake.qdoc @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2022 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Creator documentation. @@ -42,8 +42,9 @@ native build configurations and workspaces that you can use in the compiler environment of your choice. - You can use CMake from \QC to build applications for the desktop and - Android devices. You can also build single files to test your changes. + You can use CMake from \QC to build applications for the desktop, as well + as mobile and embedded devices. You can also build single files to test + your changes. \QC automatically detects the CMake executable specified in the \c PATH. You can add paths to other CMake executables and use them in different @@ -168,6 +169,6 @@ \li \l {Opening Projects} \li \l {CMake Build Configuration} \li \l {Specifying Run Settings} - \li \l {Deploying CMake Projects to Generic Remote Linux Devices} + \li \l {Deploying Applications to Generic Remote Linux Devices} \endlist */ diff --git a/doc/qtcreator/src/linux-mobile/creator-deployment-b2qt.qdoc b/doc/qtcreator/src/linux-mobile/creator-deployment-b2qt.qdoc index 40af933a85a..b2b9b128059 100644 --- a/doc/qtcreator/src/linux-mobile/creator-deployment-b2qt.qdoc +++ b/doc/qtcreator/src/linux-mobile/creator-deployment-b2qt.qdoc @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2022 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Creator documentation. @@ -30,13 +30,18 @@ \title Deploying Applications to Boot2Qt Devices - You can specify the generic deployment steps for remote Linux devices also - for \l{Boot2Qt} devices. + You can specify settings for deploying applications to \l{Boot2Qt} devices + in the project configuration file and in \uicontrol Projects > + \uicontrol {Run Settings} > \uicontrol Deployment. \image qtcreator-boot2qt-deployment-steps.png "Boot2Qt deployment steps" - For more information, see \l{Generic Deployment Steps}. + The deployment process is described in more detail in + \l{Deploying Applications to Generic Remote Linux Devices}. + + \section1 Launching Applications on Boot In addition, to have your application launch on boot, select - \uicontrol {Add Deploy Step} > \uicontrol {Change Default Application}. + \uicontrol {Add Deploy Step} > \uicontrol {Change default application} + > \uicontrol {Set this application to start by default}. */ diff --git a/doc/qtcreator/src/linux-mobile/creator-deployment-embedded-linux.qdoc b/doc/qtcreator/src/linux-mobile/creator-deployment-embedded-linux.qdoc index 11d7ac33518..f149f741db5 100644 --- a/doc/qtcreator/src/linux-mobile/creator-deployment-embedded-linux.qdoc +++ b/doc/qtcreator/src/linux-mobile/creator-deployment-embedded-linux.qdoc @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2020 The Qt Company Ltd. +** Copyright (C) 2022 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Creator documentation. @@ -41,58 +41,70 @@ \title Deploying Applications to Generic Remote Linux Devices You can specify settings for deploying applications to generic remote - Linux devices in the project .pro file. You can view the settings in - the \uicontrol Projects mode, in \uicontrol {Run Settings}. + Linux devices in the project configuration file and in the + \uicontrol Projects mode, in \uicontrol {Run Settings}. - \image qtcreator-embedded-linux-deployment-overview.png "Deploy to device" + \image qtcreator-embedded-linux-deployment-details.png "Deploy to embedded Linux" The files to be installed are listed in the \uicontrol {Deployment} step, the \uicontrol {Files to deploy} field. The \uicontrol {Local File Path} field displays the location of the file on the development PC. The - \uicontrol {Remote Directory} field displays the folder where the file is + \uicontrol {Remote Directory} field displays the directory where the file is installed on the device. Text in red color indicates that the information is - missing. Edit the qmake \l{Variables#installs} {INSTALLS variable} in the - project \c .pro file to add the missing files. + missing. + + \section1 Adding Missing Files + + The process to add files to deploy depends on the build system you use. + + \section2 CMake + + When using CMake as the build system, use the \l{CMake: install command} + {install} command in the CMakeLists.txt file to add the missing files. + + For example, add the following lines to the CMakeLists.txt file to install + the binary of your project to the \c /opt directory on the remote device: + + \badcode + set(INSTALL_DESTDIR "/opt") + + install(TARGETS + RUNTIME DESTINATION "${INSTALL_DESTDIR}" + BUNDLE DESTINATION "${INSTALL_DESTDIR}" + LIBRARY DESTINATION "${INSTALL_DESTDIR}" + ) + \endcode + + \section2 qmake + + When using qmake, edit the \l{Variables#installs}{INSTALLS variable} in + the project \c .pro file. When you run the application, \QC copies the necessary files to the device and starts the application on it. - For example, adding + For example, add the following lines to the \c .pro file to copy the binary + of your project to the \c /opt directory on the remote device: \code - target.path = /root + target.path = /opt INSTALLS += target \endcode - to the project .pro file will copy the binary of your project to \c /root - on the remote device. Additional files can be deployed by adding them to - further targets and adding those to \c INSTALLS as well. + To deploy additional files, add them to further targets that you also add + to \c INSTALLS. - \section1 Generic Deployment Steps + \section1 Deploy Steps - \image qtcreator-embedded-linux-deployment-details.png "Deploy to embedded Linux" + When you run the application on the device, \QC first uploads the + necessary files to it, as specified by the deploy steps. - When you run the application on the device, \QC - deploys the application as specified by the deploy steps. By default, \QC - copies the application files to the device by using the SSH file transfer - protocol (SFTP), as specified by the \uicontrol {Upload files via SFTP} - step. + \section2 Finding Configured Devices - If you have a lot of data to copy, select \uicontrol Details in the - \uicontrol {Upload Files via SFTP} step, and then select the - \uicontrol {Incremental deployment} check box. \QC takes note of the - deployment time and only copies files that have changed since the last - deployment. However, when you make major changes on the device, such as - removing files from the device manually or flashing a new disk image, or - when you use another device with the same IP address, deselect the check box - once, to have \QC deploy all files again. + The \uicontrol {Check for a configured device} step looks for a device that + is ready for deployment. - To only create a tarball and not copy the files to the device, select - \uicontrol {Add Deploy Step} > \uicontrol {Create tarball}. Then remove all - other deploy steps. - - The \uicontrol {Deploy tarball via SFTP upload} step specifies that \QC - uploads the tarball to the device and extracts it. + \section2 Checking for Free Disk Space The \uicontrol {Check for free disk space} step is by default the first deploy step. Use it to find out whether the remote file system has enough @@ -104,7 +116,27 @@ support will crash when an SFTP upload is being attempted. This is not a bug in \QC. - \if defined(qtcreator) - \include creator-projects-cmake-deploying.qdocinc cmake deploying embedded - \endif + \section2 Uploading Files + + By default, \QC copies the application files to the device by + using the SSH file transfer protocol (SFTP), as specified by + the \uicontrol {Upload files via SFTP} step. + + If you have a lot of data to copy, select \uicontrol Details in the + \uicontrol {Upload Files via SFTP} step, and then select the + \uicontrol {Incremental deployment} check box. \QC takes note of the + deployment time and only copies files that have changed since the last + deployment. However, when you make major changes on the device, such as + removing files from the device manually or flashing a new disk image, or + when you use another device with the same IP address, deselect the check box + once, to have \QC deploy all files again. + + \section2 Creating a Tarball + + To only create a tarball and not copy the files to the device, select + \uicontrol {Add Deploy Step} > \uicontrol {Create tarball}. Then remove all + other deploy steps. + + The \uicontrol {Deploy tarball via SFTP upload} step specifies that \QC + uploads the tarball to the device and extracts it. */ diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-generic.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-generic.qdoc index 1661c8bdc32..2f72728562d 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-generic.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-generic.qdoc @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2019 The Qt Company Ltd. +** Copyright (C) 2022 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Creator documentation. @@ -144,8 +144,8 @@ If you want to run your application on a generic remote Linux device, you first need to deploy your executable and possibly other files. \QC does that for you automatically if you provide the necessary - information. This works the same way as explained for CMake - \l {Deploying CMake Projects to Generic Remote Linux Devices}{here}, + information. This works the same way as explained for CMake in + \l {Deploying Applications to Generic Remote Linux Devices}, except that you also need to include your application binary in the list. \section1 Creating a Run Configuration diff --git a/doc/qtcreator/src/qnx/creator-deployment-qnx.qdoc b/doc/qtcreator/src/qnx/creator-deployment-qnx.qdoc index 232a371e26c..5ec84643126 100644 --- a/doc/qtcreator/src/qnx/creator-deployment-qnx.qdoc +++ b/doc/qtcreator/src/qnx/creator-deployment-qnx.qdoc @@ -1,13 +1,13 @@ /**************************************************************************** ** -** This file is part of Qt Creator -** ** Copyright (C) 2018 Blackberry -** ** Contact: Blackberry (qt@blackberry.com) ** Contact: KDAB (info@kdab.com) ** -** This file is part of the documentation of the Qt Toolkit. +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Creator documentation. ** ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in @@ -40,19 +40,12 @@ \title Deploying Applications to QNX Neutrino Devices - You can deploy applications to QNX Neutrino devices in the way that is - described in \l{Deploying Applications to Generic Remote Linux Devices}. + You can specify settings for deploying applications to QNX Neutrino + devices in the project configuration file and in \uicontrol Projects + > \uicontrol {Run Settings} > \uicontrol Deployment. \image qtcreator-qnx-deployment.png "Deploy to device" - The files to be installed are listed in the \uicontrol {Deployment} step, - the \uicontrol {Files to deploy} field. The - \uicontrol {Local File Path} field displays the location of the file on the - development PC. The \uicontrol {Remote Directory} field displays the folder - where the file is installed on the device. Text in red color indicates that - the information is missing. Edit the qmake \l{Variables#installs} - {INSTALLS variable} in the project \c .pro file to add the missing files. - - When you run the application, \QC copies the necessary files to the device - and starts the application on it. + The deployment process is described in more detail in + \l{Deploying Applications to Generic Remote Linux Devices}. */ diff --git a/doc/qtdesignstudio/examples/doc/images/rain-snow-tutorial-default-state.png b/doc/qtdesignstudio/examples/doc/images/rain-snow-tutorial-default-state.png new file mode 100644 index 00000000000..0bf866bcbf6 Binary files /dev/null and b/doc/qtdesignstudio/examples/doc/images/rain-snow-tutorial-default-state.png differ diff --git a/doc/qtdesignstudio/examples/doc/images/rain-snow-tutorial-navigator.png b/doc/qtdesignstudio/examples/doc/images/rain-snow-tutorial-navigator.png new file mode 100644 index 00000000000..afdeb7183e0 Binary files /dev/null and b/doc/qtdesignstudio/examples/doc/images/rain-snow-tutorial-navigator.png differ diff --git a/doc/qtdesignstudio/examples/doc/images/rain-snow-tutorial-particle-system.png b/doc/qtdesignstudio/examples/doc/images/rain-snow-tutorial-particle-system.png new file mode 100644 index 00000000000..45fc09789e1 Binary files /dev/null and b/doc/qtdesignstudio/examples/doc/images/rain-snow-tutorial-particle-system.png differ diff --git a/doc/qtdesignstudio/examples/doc/images/rain-snow-tutorial-start.png b/doc/qtdesignstudio/examples/doc/images/rain-snow-tutorial-start.png new file mode 100644 index 00000000000..b3f1de854e8 Binary files /dev/null and b/doc/qtdesignstudio/examples/doc/images/rain-snow-tutorial-start.png differ diff --git a/doc/qtdesignstudio/examples/doc/images/rain-snow-tutorial-states.png b/doc/qtdesignstudio/examples/doc/images/rain-snow-tutorial-states.png new file mode 100644 index 00000000000..051e4661353 Binary files /dev/null and b/doc/qtdesignstudio/examples/doc/images/rain-snow-tutorial-states.png differ diff --git a/doc/qtdesignstudio/examples/doc/images/snow-particles.png b/doc/qtdesignstudio/examples/doc/images/snow-particles.png new file mode 100644 index 00000000000..2b31ca59c8d Binary files /dev/null and b/doc/qtdesignstudio/examples/doc/images/snow-particles.png differ diff --git a/doc/qtdesignstudio/examples/doc/rainSnowParticles.qdoc b/doc/qtdesignstudio/examples/doc/rainSnowParticles.qdoc new file mode 100644 index 00000000000..49c42a84603 --- /dev/null +++ b/doc/qtdesignstudio/examples/doc/rainSnowParticles.qdoc @@ -0,0 +1,198 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ + +/*! + \page rain-snow-particle-effect.html + \ingroup gstutorials + + \title Rain and Snow Effect + \brief Illustrates how to create a rain and snow effect with the \QDS + particle system. + + \image snow-particles.png + + The \e{Rain and Snow Effect} tutorial illustrates how you can add a rain and + a snow effect to your + scene using the \QDS particle system. + + You need to download the starting project for this tutorial from + \l{https://git.qt.io/public-demos/qtdesign-studio/-/tree/master/ + tutorial%20projects/rain-snow-particles/Start}{here} before you start. + + Download the project and open the \e faceparticles.qmlproject file in \QDS + to get started. + + \image rain-snow-tutorial-start.png + \section1 Creating a Rain Effect + + \section2 Adding a Particle System to Your Scene + + To add a particle system, you first need to import the QtQuick3D.Particles3D + module to your project: + + \list 1 + \li In the \uicontrol Library view, select + \inlineimage icons/plus.png + next to \uicontrol Components. + \li Find QtQuick3D.Particles3D and select it to add it to your project. + \li From \uicontrol Library > \uicontrol Components, drag a + \uicontrol{Particle System} to \uicontrol scene in \uicontrol Navigator. + \endlist + + Now you have added a particle system to your scene. + + \image rain-snow-tutorial-particle-system + + \section2 Adjusting the Behavior and Apperance of the Particle System + Next, you adjust the position, behavior, and apperance of the particle + system to create a simple rain effect: + + \list 1 + \li Adjust the position of the particle system to align with the sphere. + In \uicontrol Navigator, select \e particleSystem and in + \uicontrol Properties, set \uicontrol Translation > \uicontrol Y to 193. + \li Set the \e rain-drop-white-square.png as texture for the particles. + From \uicontrol Library > \uicontrol Components, drag a \uicontrol Texture + to \e spriteParticle. + \li In \uicontrol Navigator, select \uicontrol texture1 and in \uicontrol + Properties, set \uicontrol Source to \e rain-drop-white-square.png. + \li In \uicontrol Navigator, select \uicontrol spriteParticle and in the + \uicontrol Properties, set \uicontrol Sprite to texture. + \li Adjust the apperance and behavior of the sprite further. In \uicontrol + Properties, set: + \list + \li \uicontrol{Particle Scale} to 10. + \li \uicontrol{Max Amount} to 1000. + \li \uicontrol Color to #91ffffff. + \li \uicontrol{Fade In Effect} to FadeNone. + \li \uicontrol{Fade Out Effect} to FadeNone. + \endlist + \li Now you have set the apperance of the particles. Next, adjust + the particle emitter. In \uicontrol Navigator, select \uicontrol + particleEmitter, and in \uicontrol Properties set: + \list + \li \uicontrol System to particleSystem. + \li \uicontrol{Emit Rate} to 1500. + \li \uicontrol{Life Span} to 100. + \li \uicontrol{Life Span Variation} to 0. + \li \uicontrol{Particle End Scale} to 1. + \li \uicontrol{Particle Scale Variation} to 0,5. + \li \uicontrol{Particle End Scale Variation} to 0,5. + \li \uicontrol{Particle Rotation} > \uicontrol Variation > + \uicontrol X, \uicontrol Y, + and \uicontrol Z to 0. + \li \uicontrol{Particle Rotation} > \uicontrol{Velocity Variation} > + \uicontrol X, + \uicontrol Y, and \uicontrol Z to 0. + \li \uicontrol Transform > \uicontrol Translation \uicontrol Y to -69. + \endlist + \li Finally, you set the direction of the particles. In \uicontrol + Navigator, select \uicontrol dir3d and in \uicontrol + Properties set: + \list + \li \uicontrol Direction > \uicontrol Y to -500. + \li \uicontrol Direction > \uicontrol Z to 0. + \li \uicontrol{Direction Variation} > \uicontrol X, \uicontrol Y, and + \uicontrol Z to 0. + \endlist + \endlist + + \section2 Adjusting the Size of the Emitting Area + + By default, the \uicontrol {Particle Emitter} emits particles from one + point in the scene. In this scene you want to emit particles from a bigger + area matching the size of the sphere. To do this, you need to add + a \uicontrol{Particle Shape} component: + + \list 1 + \li From \uicontrol Components, drag a \uicontrol{Particle Shape} + component to \uicontrol{particleSystem} in \uicontrol Navigator. + \li In \uicontrol Navigator, select \uicontrol particleShape, and in + \uicontrol Properties set: + \list + \li \uicontrol Type to \uicontrol Sphere. + \li \uicontrol Extends \uicontrol X to 85. + \li \uicontrol Extends \uicontrol Y to 85. + \li \uicontrol Extends \uicontrol Z to 85. + \endlist + \li In \uicontrol Navigator, select \uicontrol particleEmitter, and in + \uicontrol Particle set + \uicontrol Shape to \uicontrol particleShape. + \endlist + + \image rain-snow-tutorial-navigator.png + + Now, the rain effect is ready. Press \key Alt+P to see it in the live + preview. + + \section1 Creating a Snow Effect + + To make it easy, you can duplicate the particle system you created for the + rain effect and adjust the properties to create a snow effect. To do this, + first create a new state for the snow effect: + + \list + \li In \uicontrol{States}, select \uicontrol{Create New State}. + \endlist + + \image rain-snow-tutorial-states.png + + \section2 Turning the Rain into Snow + + \list 1 + \li With the new state that you just created selected in + \uicontrol{States}, in \uicontrol{Navigator}, select \uicontrol + spriteParticle and set \uicontrol Color to #ffffff. + \li In \uicontrol{Navigator}, select \uicontrol texture1 and set + \uicontrol Source to \e{snowflake.png}. + \li In \uicontrol{Navigator}, select \uicontrol particleEmitter and set: + \list + \li \uicontrol{Emit Rate} to 250. + \li \uicontrol{Life Span} to 450. + \li \uicontrol{Particle Rotation} > \uicontrol Variation > + \uicontrol{X}, \uicontrol{Y}, and \uicontrol Z to 180. + \li \uicontrol{Particle Rotation} > \uicontrol{Velocity Variation} > + \uicontrol{X}, \uicontrol{Y}, and \uicontrol Z to 200. + \endlist + \li In \uicontrol{Navigator}, select \uicontrol particleEmitter + > \uicontrol dir3d and set: + \list + \li \uicontrol Direction > \uicontrol Y to -100. + \li \uicontrol{Direction Variation} \uicontrol{X}, \uicontrol{Y}, + and \uicontrol Z + to 10. + \endlist + \endlist + + Now you can run the snow effect in the live preview: + \list 1 + \li In \uicontrol{States} next to \uicontrol State1 select + \inlineimage icons/action-icon.png + and select \uicontrol{Set as Default}. + \li Press \key{Alt+P}. + \endlist + + \image rain-snow-tutorial-default-state.png +*/ diff --git a/doc/qtdesignstudio/images/studio-project-custom-screen-size.png b/doc/qtdesignstudio/images/studio-project-custom-screen-size.png deleted file mode 100644 index 2c4bfdee92a..00000000000 Binary files a/doc/qtdesignstudio/images/studio-project-custom-screen-size.png and /dev/null differ diff --git a/doc/qtdesignstudio/images/studio-project-location.png b/doc/qtdesignstudio/images/studio-project-location.png deleted file mode 100644 index 5b921bade0f..00000000000 Binary files a/doc/qtdesignstudio/images/studio-project-location.png and /dev/null differ diff --git a/doc/qtdesignstudio/images/studio-project-wizards.png b/doc/qtdesignstudio/images/studio-project-wizards.png index 66bb516af0b..81fcc3af535 100644 Binary files a/doc/qtdesignstudio/images/studio-project-wizards.png and b/doc/qtdesignstudio/images/studio-project-wizards.png differ diff --git a/doc/qtdesignstudio/src/qtdesignstudio-projects.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-projects.qdoc index 07af4c6decb..de7f7150d35 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-projects.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-projects.qdoc @@ -48,95 +48,83 @@ prompt you to enter the settings needed for a particular type of project and create the necessary files for you. - \QDS projects are useful for creating UIs. You cannot use them for - application development, because they do not contain: - - \list - \li C++ code - \li Resource files (.qrc) - \li Code needed for deploying applications to devices. - \endlist - - Because the projects do not contain any C++ code, you do not need - to build them. To test how well your designs work, you can preview the - UIs on the desktop or embedded Linux devices. For more + To test how well your designs work, you can preview the + UIs on the desktop, embedded Linux devices, or Android devices. For more information, see \l{Validating with Target Hardware}. \image studio-project-wizards.png "A list of project wizards" You can export designs from other design tools and import them to projects - or create them from scratch using the following wizard templates: + or create them from scratch using the following wizard presets: \table \header \li Category - \li Wizard Template + \li Wizard Preset \li Purpose \row \li {1,2} General - \li Qt Quick Application - Empty - \li Creates a project that uses default components and preset UI - controls and can be run on all target platforms. + \li Empty + \li Creates a project that uses default components such as rectangles, + images, and text. You can run the application on all target + platforms. \row - \li Qt Quick 3D Application - \li Creates a project that uses default components, UI controls, and - 3D components. + \li 3D + \li Creates a project that uses default and 3D components such as + cameras, lights, 3D models, and materials. \row \li Qt for MCUs - \li Qt for MCUs Application - \li Creates an application that uses a subset of preset components + \li MCU + \li Creates an application that uses a subset of default components (as supported by Qt for MCUs) that you can deploy, run, and debug on MCU boards. \row - \li {1,2} Mobile - \li Qt Quick Application - Scroll - \li Creates an application that uses UI controls to implement a + \li {1,3} Mobile + \li Scroll + \li Creates an application that uses Qt Quick controls to implement a scrollable list. \row - \li Qt Quick Application - Stack - \li Creates an application that uses UI controls to implement a + \li Stack + \li Creates an application that uses Qt Quick controls to implement a set of pages with a stack-based navigation model. + \row + \li Swipe + \li Creates an application that uses Qt Quick controls to implement a + swipable screen. \row \li Desktop - \li Qt Quick Application - Launcher - \li Creates a project that uses default components and UI controls and - defines a launcher application. + \li Launcher + \li Creates a project that uses default components such as rectangles, + images, and text, and defines a launcher application. \endtable - For an example of creating a \uicontrol {Qt Quick 3D Application} project, - watch the following video: - - \youtube 9ihYeC0YJ0M - \section1 Using Project Wizards + To create a new project: + \list 1 \li Select \uicontrol File > \uicontrol {New Project}. - \li Select a wizard template, and then select \uicontrol Choose. - \li In the \uicontrol Name field, enter a name for the project. - Keep in mind that projects cannot be easily renamed later. - \image studio-project-location.png "Project Location dialog" - \li In the \uicontrol {Create in} field, enter the path for the project - files. You can move project folders later without problems. - \li Select \uicontrol Next (or \uicontrol Continue on \macos). - \li In the \uicontrol {Screen resolution} field, select the screen - resolution for previewing the UI on the desktop or on a device. - This determines the screen size. - \image studio-project-custom-screen-size.png "Define Project Details dialog" - \li To use a custom screen size, specify the width and height of the - screen in the \uicontrol {Custom screen width} and - \uicontrol {Custom screen height} fields. - You can easily change the screen size later in \l Properties. - \li In the \uicontrol {Qt Quick Controls Style} field, select one of + \li In the \uicontrol Presets tab, select a wizard preset. + \li In the \uicontrol Details tab: + \list + \li Enter a name for the project. Keep in mind that projects + cannot be easily renamed later. + \li Select the path for the project files. You can move project + folders later. + \li Set the screen resolution for previewing the UI on the + desktop or on a device. This determines the screen size. You can + change the screen size later in \l Properties. + \li Select \uicontrol {Use Qt Virtual Keyboard} to + enable users to enter text using a virtual keyboard. + \li In \uicontrol {Target Qt Version}, select the Qt + version to use for developing the application. While you can + change the Qt version later in the \uicontrol {Run Settings} + of the project, keep in mind that the two versions are not fully + compatible. + \endlist + \li In the \uicontrol {Style} tab, select one of the predefined \l{Styling Qt Quick Controls}{UI styles} to use. - \li Select the \uicontrol {Use Qt Virtual Keyboard} check box to enable - users to enter text using a virtual keyboard. - \li In the \uicontrol {Target Qt Version} field, select the Qt version - to use for developing the application. While you can change the - Qt version later in the \uicontrol {Run Settings} of the project, - keep in mind that the two versions are not fully compatible. - \li Select \uicontrol Finish (or \uicontrol Done on \macos) to create - the project. + \li Select \uicontrol Create to create the project. \endlist \QDS creates the following files and folders: @@ -155,6 +143,9 @@ Specifically, if you export and import designs using \QB, your main file is most likely called something else. For more information, see \l {Exporting from Design Tools}. + \li \e CMakeLists.txt project configuration file allowing you to + share your project as a fully working C++ application with + developers. \li qtquickcontrols2.conf file specifies the preferred style and some style-specific arguments. \li \e fonts folder contains font files that you have added in diff --git a/share/qtcreator/templates/wizards/autotest/wizard.json b/share/qtcreator/templates/wizards/autotest/wizard.json index b3222b29182..ae8fdb9d121 100644 --- a/share/qtcreator/templates/wizards/autotest/wizard.json +++ b/share/qtcreator/templates/wizards/autotest/wizard.json @@ -237,7 +237,7 @@ "enabled": "%{IsTopLevelProject}", "data": { "projectFilePath": "%{ProjectFilePath}", - "requiredFeatures": [ "%{JS: (value('TestFrameWork') === 'QtQuickTest' ? 'QtSupport.Wizards.FeatureQt.5' : ((value('BuildSystem') === 'qmake' || value('TestFrameWork') === 'QtTest') ? 'QtSupport.Wizards.FeatureQt' : 'DeviceType.Desktop' )) }" ] + "requiredFeatures": [ "%{JS: (value('TestFrameWork') === 'QtQuickTest' ? 'QtSupport.Wizards.FeatureQtQuick.2' : ((value('BuildSystem') === 'qmake' || value('TestFrameWork') === 'QtTest') ? 'QtSupport.Wizards.FeatureQt' : 'DeviceType.Desktop' )) }" ] } }, { diff --git a/src/libs/sqlite/sqlitealgorithms.h b/src/libs/sqlite/sqlitealgorithms.h index df1ea44d483..4f4a6ed6b5d 100644 --- a/src/libs/sqlite/sqlitealgorithms.h +++ b/src/libs/sqlite/sqlitealgorithms.h @@ -25,6 +25,7 @@ #pragma once +#include #include #include @@ -62,7 +63,7 @@ void insertUpdateDelete(SqliteRange &&sqliteRange, auto endSqliteIterator = sqliteRange.end(); auto currentValueIterator = values.begin(); auto endValueIterator = values.end(); - std::optional> lastValue; + Utils::optional> lastValue; while (true) { bool hasMoreValues = currentValueIterator != endValueIterator; diff --git a/src/libs/sqlite/sqlitetimestamp.h b/src/libs/sqlite/sqlitetimestamp.h index c7c100ec454..eb98d8852cc 100644 --- a/src/libs/sqlite/sqlitetimestamp.h +++ b/src/libs/sqlite/sqlitetimestamp.h @@ -41,6 +41,17 @@ public: } friend bool operator!=(TimeStamp first, TimeStamp second) { return !(first == second); } + friend bool operator<(TimeStamp first, TimeStamp second) { return first.value < second.value; } + + friend TimeStamp operator+(TimeStamp first, TimeStamp second) + { + return first.value + second.value; + } + + friend TimeStamp operator-(TimeStamp first, TimeStamp second) + { + return first.value - second.value; + } bool isValid() const { return value >= 0; } diff --git a/src/libs/utils/runextensions.h b/src/libs/utils/runextensions.h index e7b9dd6c939..9339f1a52ee 100644 --- a/src/libs/utils/runextensions.h +++ b/src/libs/utils/runextensions.h @@ -408,7 +408,10 @@ QFuture runAsync_internal(QThreadPool *pool, QFuture future = job->future(); if (pool) { job->setThreadPool(pool); - pool->start(job); + if (QThread::currentThread() == pool->thread()) + pool->start(job); + else + QMetaObject::invokeMethod(pool, [pool, job]() { pool->start(job); }, Qt::QueuedConnection); } else { auto thread = new Internal::RunnableThread(job); if (stackSize) diff --git a/src/plugins/cpaster/dpastedotcomprotocol.cpp b/src/plugins/cpaster/dpastedotcomprotocol.cpp index fe333a5630b..26c38dd0d34 100644 --- a/src/plugins/cpaster/dpastedotcomprotocol.cpp +++ b/src/plugins/cpaster/dpastedotcomprotocol.cpp @@ -138,11 +138,10 @@ void DPasteDotComProtocol::paste( }); } -bool DPasteDotComProtocol::checkConfiguration(QString *errorMessage) +bool DPasteDotComProtocol::checkConfiguration(QString * /*errorMessage*/) { - if (!m_hostKnownOk) - m_hostKnownOk = httpStatus(baseUrl(), errorMessage); - return m_hostKnownOk; + // we need a 1s gap between requests, so skip status check to avoid failing + return true; } void DPasteDotComProtocol::reportError(const QString &message) diff --git a/src/plugins/cpaster/dpastedotcomprotocol.h b/src/plugins/cpaster/dpastedotcomprotocol.h index b7c7452b445..93b1a6e3383 100644 --- a/src/plugins/cpaster/dpastedotcomprotocol.h +++ b/src/plugins/cpaster/dpastedotcomprotocol.h @@ -50,8 +50,6 @@ private: bool checkConfiguration(QString *errorMessage) override; static void reportError(const QString &message); - - bool m_hostKnownOk = false; }; } // namespace CodePaster diff --git a/src/plugins/projectexplorer/msvctoolchain.cpp b/src/plugins/projectexplorer/msvctoolchain.cpp index fc8af7b6507..94994e84766 100644 --- a/src/plugins/projectexplorer/msvctoolchain.cpp +++ b/src/plugins/projectexplorer/msvctoolchain.cpp @@ -1572,7 +1572,9 @@ static const MsvcToolChain *selectMsvcToolChain(const QString &displayedVarsBat, QTC_CHECK(displayedVarsBat.isEmpty()); const QVersionNumber version = clangClVersion(clangClPath); if (version.majorVersion() >= 6) { - toolChain = findMsvcToolChain(wordWidth, Abi::WindowsMsvc2019Flavor); + toolChain = findMsvcToolChain(wordWidth, Abi::WindowsMsvc2022Flavor); + if (!toolChain) + toolChain = findMsvcToolChain(wordWidth, Abi::WindowsMsvc2019Flavor); if (!toolChain) toolChain = findMsvcToolChain(wordWidth, Abi::WindowsMsvc2017Flavor); } diff --git a/src/plugins/qmldesigner/cmakegeneratordialogtreemodel.cpp b/src/plugins/qmldesigner/cmakegeneratordialogtreemodel.cpp index bb0ba02c8f7..28a318dcf05 100644 --- a/src/plugins/qmldesigner/cmakegeneratordialogtreemodel.cpp +++ b/src/plugins/qmldesigner/cmakegeneratordialogtreemodel.cpp @@ -185,7 +185,7 @@ void CMakeGeneratorDialogTreeModel::createNodes(const FilePaths &candidates, QSt const CheckableFileTreeItem* CMakeGeneratorDialogTreeModel::constNodeForIndex(const QModelIndex &index) const { - const QStandardItem *parent = static_cast(index.constInternalPointer()); + const QStandardItem *parent = static_cast(index.internalPointer()); const QStandardItem *item = parent->child(index.row(), index.column()); return static_cast(item); } diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index 7459f51c184..5327098cf3a 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -1050,15 +1050,16 @@ static QString getAssetDefaultDirectory(const QString &assetDir, const QString & { QString adjustedDefaultDirectory = defaultDirectory; - Utils::FilePath assetPath = projectFilePath(); - if (assetPath.pathAppended("content").exists()) - assetPath= assetPath.pathAppended("content"); + Utils::FilePath contentPath = projectFilePath(); - assetPath = assetPath.pathAppended(assetDir); + if (contentPath.pathAppended("content").exists()) + contentPath = contentPath.pathAppended("content"); + + Utils::FilePath assetPath = contentPath.pathAppended(assetDir); if (!assetPath.exists()) { // Create the default asset type directory if it doesn't exist - QDir dir(projectFilePath().toString()); + QDir dir(contentPath.toString()); dir.mkpath(assetDir); } diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp index 22c5a09c06c..83cc9372302 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp @@ -25,18 +25,19 @@ #include "asynchronousimagefactory.h" +#include "imagecachecollector.h" #include "imagecachegenerator.h" #include "imagecachestorage.h" -#include "timestampprovider.h" +#include "timestampproviderinterface.h" namespace QmlDesigner { AsynchronousImageFactory::AsynchronousImageFactory(ImageCacheStorageInterface &storage, - ImageCacheGeneratorInterface &generator, - TimeStampProviderInterface &timeStampProvider) + TimeStampProviderInterface &timeStampProvider, + ImageCacheCollectorInterface &collector) : m_storage(storage) - , m_generator(generator) , m_timeStampProvider(timeStampProvider) + , m_collector(collector) { m_backgroundThread = std::thread{[this] { while (isRunning()) { @@ -45,8 +46,8 @@ AsynchronousImageFactory::AsynchronousImageFactory(ImageCacheStorageInterface &s entry->extraId, std::move(entry->auxiliaryData), m_storage, - m_generator, - m_timeStampProvider); + m_timeStampProvider, + m_collector); } waitForEntries(); @@ -107,30 +108,33 @@ void AsynchronousImageFactory::request(Utils::SmallStringView name, Utils::SmallStringView extraId, ImageCache::AuxiliaryData auxiliaryData, ImageCacheStorageInterface &storage, - ImageCacheGeneratorInterface &generator, - TimeStampProviderInterface &timeStampProvider) + TimeStampProviderInterface &timeStampProvider, + ImageCacheCollectorInterface &collector) { const auto id = extraId.empty() ? Utils::PathString{name} : Utils::PathString::join({name, "+", extraId}); const auto currentModifiedTime = timeStampProvider.timeStamp(name); const auto storageModifiedTime = storage.fetchModifiedImageTime(id); + const auto pause = timeStampProvider.pause(); - if (currentModifiedTime == storageModifiedTime && storage.fetchHasImage(id)) + if (currentModifiedTime < (storageModifiedTime + pause)) return; - generator.generateImage(name, - extraId, - currentModifiedTime, - ImageCache::CaptureImageWithSmallImageCallback{}, - ImageCache::AbortCallback{}, - std::move(auxiliaryData)); + auto capture = [=](const QImage &image, const QImage &smallImage) { + m_storage.storeImage(id, currentModifiedTime, image, smallImage); + }; + + collector.start(name, + extraId, + std::move(auxiliaryData), + std::move(capture), + ImageCache::AbortCallback{}); } void AsynchronousImageFactory::clean() { clearEntries(); - m_generator.clean(); } void AsynchronousImageFactory::wait() diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.h b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.h index d9da32d3a5b..1331e1af415 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.h +++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.h @@ -39,15 +39,14 @@ namespace QmlDesigner { class TimeStampProviderInterface; class ImageCacheStorageInterface; -class ImageCacheGeneratorInterface; class ImageCacheCollectorInterface; class AsynchronousImageFactory { public: AsynchronousImageFactory(ImageCacheStorageInterface &storage, - ImageCacheGeneratorInterface &generator, - TimeStampProviderInterface &timeStampProvider); + TimeStampProviderInterface &timeStampProvider, + ImageCacheCollectorInterface &collector); ~AsynchronousImageFactory(); @@ -83,8 +82,8 @@ private: Utils::SmallStringView extraId, ImageCache::AuxiliaryData auxiliaryData, ImageCacheStorageInterface &storage, - ImageCacheGeneratorInterface &generator, - TimeStampProviderInterface &timeStampProvider); + TimeStampProviderInterface &timeStampProvider, + ImageCacheCollectorInterface &collector); void wait(); void clearEntries(); void stopThread(); @@ -95,8 +94,8 @@ private: std::condition_variable m_condition; std::thread m_backgroundThread; ImageCacheStorageInterface &m_storage; - ImageCacheGeneratorInterface &m_generator; TimeStampProviderInterface &m_timeStampProvider; + ImageCacheCollectorInterface &m_collector; bool m_finishing{false}; }; diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp index 6f5aa0cdd80..b4f50848b9a 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp @@ -60,8 +60,10 @@ QString fileToString(const QString &filename) } // namespace -ImageCacheCollector::ImageCacheCollector(ImageCacheConnectionManager &connectionManager) +ImageCacheCollector::ImageCacheCollector(ImageCacheConnectionManager &connectionManager, + ImageCacheCollectorNullImageHandling nullImageHandling) : m_connectionManager{connectionManager} + , nullImageHandling{nullImageHandling} {} ImageCacheCollector::~ImageCacheCollector() = default; @@ -89,7 +91,8 @@ void ImageCacheCollector::start(Utils::SmallStringView name, model->setRewriterView(&rewriterView); if (rewriterView.inErrorState() || !rewriterView.rootModelNode().metaInfo().isGraphicalItem()) { - abortCallback(ImageCache::AbortReason::Failed); + if (abortCallback) + abortCallback(ImageCache::AbortReason::Failed); return; } @@ -98,12 +101,14 @@ void ImageCacheCollector::start(Utils::SmallStringView name, if (stateNode.isValid()) rewriterView.setCurrentStateNode(stateNode); - auto callback = [captureCallback = std::move(captureCallback)](const QImage &image) { - QSize smallImageSize = image.size().scaled(QSize{96, 96}.boundedTo(image.size()), - Qt::KeepAspectRatio); - QImage smallImage = image.isNull() ? QImage{} : image.scaled(smallImageSize); - - captureCallback(image, smallImage); + auto callback = [=, captureCallback = std::move(captureCallback)](const QImage &image) { + if (nullImageHandling == ImageCacheCollectorNullImageHandling::CaptureNullImage + || !image.isNull()) { + QSize smallImageSize = image.size().scaled(QSize{96, 96}.boundedTo(image.size()), + Qt::KeepAspectRatio); + QImage smallImage = image.isNull() ? QImage{} : image.scaled(smallImageSize); + captureCallback(image, smallImage); + } }; if (!m_target) @@ -122,29 +127,21 @@ void ImageCacheCollector::start(Utils::SmallStringView name, model->setNodeInstanceView({}); model->setRewriterView({}); - if (!capturedDataArrived) + if (!capturedDataArrived && abortCallback) abortCallback(ImageCache::AbortReason::Failed); } -std::pair ImageCacheCollector::createImage(Utils::SmallStringView filePath, - Utils::SmallStringView state, - const ImageCache::AuxiliaryData &auxiliaryData) +std::pair ImageCacheCollector::createImage(Utils::SmallStringView, + Utils::SmallStringView, + const ImageCache::AuxiliaryData &) { - Q_UNUSED(filePath) - Q_UNUSED(state) - Q_UNUSED(auxiliaryData) - return {}; } -QIcon ImageCacheCollector::createIcon(Utils::SmallStringView filePath, - Utils::SmallStringView state, - const ImageCache::AuxiliaryData &auxiliaryData) +QIcon ImageCacheCollector::createIcon(Utils::SmallStringView, + Utils::SmallStringView, + const ImageCache::AuxiliaryData &) { - Q_UNUSED(filePath) - Q_UNUSED(state) - Q_UNUSED(auxiliaryData) - return {}; } diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h index a9deaf9bf7d..2e2803114ac 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h @@ -45,10 +45,13 @@ class ImageCacheConnectionManager; class RewriterView; class NodeInstanceView; +enum class ImageCacheCollectorNullImageHandling { CaptureNullImage, DontCaptureNullImage }; + class ImageCacheCollector final : public ImageCacheCollectorInterface { public: - ImageCacheCollector(ImageCacheConnectionManager &connectionManager); + ImageCacheCollector(ImageCacheConnectionManager &connectionManager, + ImageCacheCollectorNullImageHandling nullImageHandling = {}); ~ImageCacheCollector(); @@ -72,6 +75,7 @@ public: private: ImageCacheConnectionManager &m_connectionManager; QPointer m_target; + ImageCacheCollectorNullImageHandling nullImageHandling{}; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/imagecache/timestampprovider.h b/src/plugins/qmldesigner/designercore/imagecache/timestampprovider.h index 8acc5fcb588..e9d2b60550d 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/timestampprovider.h +++ b/src/plugins/qmldesigner/designercore/imagecache/timestampprovider.h @@ -33,6 +33,7 @@ class TimeStampProvider : public TimeStampProviderInterface { public: Sqlite::TimeStamp timeStamp(Utils::SmallStringView name) const override; + Sqlite::TimeStamp pause() const override { return 0; } }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/imagecache/timestampproviderinterface.h b/src/plugins/qmldesigner/designercore/imagecache/timestampproviderinterface.h index 33cffc9b49c..4babac27965 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/timestampproviderinterface.h +++ b/src/plugins/qmldesigner/designercore/imagecache/timestampproviderinterface.h @@ -36,6 +36,7 @@ class TimeStampProviderInterface { public: virtual Sqlite::TimeStamp timeStamp(Utils::SmallStringView name) const = 0; + virtual Sqlite::TimeStamp pause() const = 0; protected: ~TimeStampProviderInterface() = default; diff --git a/src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.cpp b/src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.cpp index 39590beadf7..f1e1e933b65 100644 --- a/src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.cpp +++ b/src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.cpp @@ -137,7 +137,8 @@ void BaseConnectionManager::callCrashCallback() { std::lock_guard lock{m_callbackMutex}; - m_crashCallback(); + if (m_crashCallback) + m_crashCallback(); } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp index 0d75c6a97af..51d62a2bbe5 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp @@ -50,7 +50,7 @@ #include #include #include -#include +#include #include @@ -70,6 +70,23 @@ QString defaultImagePath() return qobject_cast<::QmlProjectManager::QmlBuildSystem *>(target->buildSystem()); } +class TimeStampProvider : public TimeStampProviderInterface +{ +public: + Sqlite::TimeStamp timeStamp(Utils::SmallStringView) const override + { + auto now = std::chrono::steady_clock::now(); + + return std::chrono::duration_cast(now.time_since_epoch()).count(); + } + + Sqlite::TimeStamp pause() const override + { + using namespace std::chrono_literals; + return std::chrono::duration_cast(1h).count(); + } +}; + } // namespace class PreviewImageCacheData @@ -81,11 +98,11 @@ public: Sqlite::LockingMode::Normal}; ImageCacheStorage storage{database}; ImageCacheConnectionManager connectionManager; - ImageCacheCollector collector{connectionManager}; - ImageCacheGenerator generator{collector, storage}; + ImageCacheCollector collector{connectionManager, + ImageCacheCollectorNullImageHandling::DontCaptureNullImage}; TimeStampProvider timeStampProvider; AsynchronousExplicitImageCache cache{storage}; - AsynchronousImageFactory factory{storage, generator, timeStampProvider}; + AsynchronousImageFactory factory{storage, timeStampProvider, collector}; }; class QmlDesignerProjectManagerProjectData @@ -155,8 +172,10 @@ void QmlDesignerProjectManager::projectAdded(::ProjectExplorer::Project *project void QmlDesignerProjectManager::aboutToRemoveProject(::ProjectExplorer::Project *) { - m_imageCacheData->collector.setTarget(m_projectData->activeTarget); - m_projectData.reset(); + if (m_projectData) { + m_imageCacheData->collector.setTarget(m_projectData->activeTarget); + m_projectData.reset(); + } } void QmlDesignerProjectManager::projectRemoved(::ProjectExplorer::Project *) {} diff --git a/src/plugins/studiowelcome/studiowelcomeplugin.cpp b/src/plugins/studiowelcome/studiowelcomeplugin.cpp index 2bcd6b675fe..1195cf8af81 100644 --- a/src/plugins/studiowelcome/studiowelcomeplugin.cpp +++ b/src/plugins/studiowelcome/studiowelcomeplugin.cpp @@ -28,6 +28,8 @@ #include "qdsnewdialog.h" +#include + #include #include #include @@ -268,6 +270,24 @@ int ProjectModel::rowCount(const QModelIndex &) const return ProjectExplorer::ProjectExplorerPlugin::recentProjects().count(); } +QString getQDSVersion(const QString &projectFilePath) +{ + const QString defaultReturn = ""; + Utils::FileReader reader; + if (!reader.fetch(Utils::FilePath::fromString(projectFilePath))) + return defaultReturn; + + const QByteArray data = reader.data(); + + QRegularExpression regexp(R"x(qdsVersion: "(.*)")x"); + QRegularExpressionMatch match = regexp.match(QString::fromUtf8(data)); + + if (!match.hasMatch()) + return defaultReturn; + + return ProjectModel::tr("Created with Qt Design Studio version: %1").arg(match.captured(1)); +} + QString getMainQmlFile(const QString &projectFilePath) { const QString defaultReturn = "content/App.qml"; @@ -293,8 +313,8 @@ QString appQmlFile(const QString &projectFilePath) static QString fromCamelCase(const QString &s) { - static QRegularExpression regExp1 {"(.)([A-Z][a-z]+)"}; - static QRegularExpression regExp2 {"([a-z0-9])([A-Z])"}; + const QRegularExpression regExp1 {"(.)([A-Z][a-z]+)"}; + const QRegularExpression regExp2 {"([a-z0-9])([A-Z])"}; QString result = s; result.replace(regExp1, "\\1 \\2"); result.replace(regExp2, "\\1 \\2"); @@ -302,15 +322,49 @@ static QString fromCamelCase(const QString &s) { return result; } +static QString resolutionFromConstants(const QString &projectFilePath) +{ + const QFileInfo fileInfo(projectFilePath); + const QString fileName = fileInfo.dir().absolutePath() + + "/" + "imports" + "/" + fileInfo.baseName() + "/Constants.qml"; + + Utils::FileReader reader; + if (!reader.fetch(Utils::FilePath::fromString(fileName))) + return {}; + + const QByteArray data = reader.data(); + + const QRegularExpression regexpWidth(R"x(readonly\s+property\s+int\s+width:\s+(\d*))x"); + const QRegularExpression regexpHeight(R"x(readonly\s+property\s+int\s+height:\s+(\d*))x"); + + int width = -1; + int height = -1; + + QRegularExpressionMatch match = regexpHeight.match(QString::fromUtf8(data)); + if (match.hasMatch()) + height = match.captured(1).toInt(); + + match = regexpWidth.match(QString::fromUtf8(data)); + if (match.hasMatch()) + width = match.captured(1).toInt(); + + if (width > 0 && height > 0) + return ProjectModel::tr("Resolution: %1x%2").arg(width).arg(height); + + return {}; +} + static QString description(const QString &projectFilePath) { - const QString created = "Created: " + - QFileInfo(projectFilePath).fileTime(QFileDevice::FileBirthTime).toString(); - const QString lastEdited = "Last Edited: " + - QFileInfo(projectFilePath).fileTime(QFileDevice::FileModificationTime).toString(); + const QString created = ProjectModel::tr("Created: %1").arg( + QFileInfo(projectFilePath).fileTime(QFileDevice::FileBirthTime).toString()); + const QString lastEdited = ProjectModel::tr("Last Edited: %1").arg( + QFileInfo(projectFilePath).fileTime(QFileDevice::FileModificationTime).toString()); - return fromCamelCase(QFileInfo(projectFilePath).baseName()) + "\n" + created + "\n" + lastEdited; + return fromCamelCase(QFileInfo(projectFilePath).baseName()) + "\n\n" + created + "\n" + lastEdited + + "\n" + resolutionFromConstants(projectFilePath) + + "\n" + getQDSVersion(projectFilePath); } static QString tags(const QString &projectFilePath) @@ -438,6 +492,26 @@ bool StudioWelcomePlugin::initialize(const QStringList &arguments, QString *erro return true; } +static bool showSplashScreen() +{ + const QString lastQDSVersionEntry = "QML/Designer/lastQDSVersion"; + + QSettings *settings = Core::ICore::settings(); + + const QString lastQDSVersion = settings->value(lastQDSVersionEntry).toString(); + + + const QString currentVersion = Core::Constants::IDE_VERSION_DISPLAY; + + if (currentVersion != lastQDSVersion) { + settings->setValue(lastQDSVersionEntry, currentVersion); + return true; + } + + return Utils::CheckableMessageBox::shouldAskAgain(Core::ICore::settings(), + DO_NOT_SHOW_SPLASHSCREEN_AGAIN_KEY); +} + void StudioWelcomePlugin::extensionsInitialized() { Core::ModeManager::activateMode(m_welcomeMode->id()); @@ -445,8 +519,7 @@ void StudioWelcomePlugin::extensionsInitialized() // Enable QDS new project dialog Core::ICore::setNewDialogFactory([](QWidget *parent) { return new QdsNewDialog(parent); }); - if (Utils::CheckableMessageBox::shouldAskAgain(Core::ICore::settings(), - DO_NOT_SHOW_SPLASHSCREEN_AGAIN_KEY)) { + if (showSplashScreen()) { connect(Core::ICore::instance(), &Core::ICore::coreOpened, this, [this] { s_view = new QQuickWidget(Core::ICore::dialogParent()); s_view->setResizeMode(QQuickWidget::SizeRootObjectToView); @@ -532,6 +605,8 @@ WelcomeMode::WelcomeMode() setContextHelp("Qt Design Studio Manual"); setContext(Core::Context(Core::Constants::C_WELCOME_MODE)); + QFontDatabase::addApplicationFont(":/studiofonts/TitilliumWeb-Regular.ttf"); + m_modeWidget = new QQuickWidget; m_modeWidget->setMinimumSize(1024, 768); m_modeWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); diff --git a/tests/unit/unittest/asynchronousimagefactory-test.cpp b/tests/unit/unittest/asynchronousimagefactory-test.cpp index b0245251441..cc23374a575 100644 --- a/tests/unit/unittest/asynchronousimagefactory-test.cpp +++ b/tests/unit/unittest/asynchronousimagefactory-test.cpp @@ -25,6 +25,7 @@ #include "googletest.h" +#include "imagecachecollectormock.h" #include "mockimagecachegenerator.h" #include "mockimagecachestorage.h" #include "mocktimestampprovider.h" @@ -41,67 +42,64 @@ class AsynchronousImageFactory : public testing::Test protected: AsynchronousImageFactory() { - ON_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml"))) + ON_CALL(timeStampProviderMock, timeStamp(Eq("/path/to/Component.qml"))) .WillByDefault(Return(Sqlite::TimeStamp{123})); } protected: Notification notification; Notification waitInThread; - NiceMock mockStorage; - NiceMock mockGenerator; - NiceMock mockTimeStampProvider; - QmlDesigner::AsynchronousImageFactory factory{mockStorage, mockGenerator, mockTimeStampProvider}; + NiceMock storageMock; + NiceMock collectorMock; + NiceMock timeStampProviderMock; + QmlDesigner::AsynchronousImageFactory factory{storageMock, timeStampProviderMock, collectorMock}; QImage image1{10, 10, QImage::Format_ARGB32}; QImage smallImage1{1, 1, QImage::Format_ARGB32}; }; -TEST_F(AsynchronousImageFactory, RequestImageRequestImageFromGenerator) +TEST_F(AsynchronousImageFactory, RequestImageRequestImageFromCollector) { - EXPECT_CALL(mockGenerator, - generateImage(Eq("/path/to/Component.qml"), - IsEmpty(), - Eq(Sqlite::TimeStamp{123}), - _, - _, - VariantWith(Utils::monostate{}))) - .WillRepeatedly([&](auto, auto, auto, auto, auto, auto) { notification.notify(); }); + EXPECT_CALL(collectorMock, + start(Eq("/path/to/Component.qml"), + IsEmpty(), + VariantWith(Utils::monostate{}), + _, + _)) + .WillRepeatedly([&](auto, auto, auto, auto, auto) { notification.notify(); }); factory.generate("/path/to/Component.qml"); notification.wait(); } -TEST_F(AsynchronousImageFactory, RequestImageWithExtraIdRequestImageFromGenerator) +TEST_F(AsynchronousImageFactory, RequestImageWithExtraIdRequestImageFromCollector) { - EXPECT_CALL(mockGenerator, - generateImage(Eq("/path/to/Component.qml"), - Eq("foo"), - Eq(Sqlite::TimeStamp{123}), - _, - _, - VariantWith(Utils::monostate{}))) - .WillRepeatedly([&](auto, auto, auto, auto, auto, auto) { notification.notify(); }); + EXPECT_CALL(collectorMock, + start(Eq("/path/to/Component.qml"), + Eq("foo"), + VariantWith(Utils::monostate{}), + _, + _)) + .WillRepeatedly([&](auto, auto, auto, auto, auto) { notification.notify(); }); factory.generate("/path/to/Component.qml", "foo"); notification.wait(); } -TEST_F(AsynchronousImageFactory, RequestImageWithAuxiliaryDataRequestImageFromGenerator) +TEST_F(AsynchronousImageFactory, RequestImageWithAuxiliaryDataRequestImageFromCollector) { std::vector sizes{{20, 11}}; - EXPECT_CALL(mockGenerator, - generateImage(Eq("/path/to/Component.qml"), - Eq("foo"), - Eq(Sqlite::TimeStamp{123}), - _, - _, - VariantWith(AllOf( - Field(&FontCollectorSizesAuxiliaryData::sizes, - ElementsAre(QSize{20, 11})), - Field(&FontCollectorSizesAuxiliaryData::colorName, Eq(u"color")), - Field(&FontCollectorSizesAuxiliaryData::text, Eq(u"some text")))))) - .WillRepeatedly([&](auto, auto, auto, auto, auto, auto) { notification.notify(); }); + EXPECT_CALL(collectorMock, + start(Eq("/path/to/Component.qml"), + Eq("foo"), + VariantWith( + AllOf(Field(&FontCollectorSizesAuxiliaryData::sizes, + ElementsAre(QSize{20, 11})), + Field(&FontCollectorSizesAuxiliaryData::colorName, Eq(u"color")), + Field(&FontCollectorSizesAuxiliaryData::text, Eq(u"some text")))), + _, + _)) + .WillRepeatedly([&](auto, auto, auto, auto, auto) { notification.notify(); }); factory.generate("/path/to/Component.qml", "foo", @@ -111,40 +109,34 @@ TEST_F(AsynchronousImageFactory, RequestImageWithAuxiliaryDataRequestImageFromGe notification.wait(); } -TEST_F(AsynchronousImageFactory, DontRequestImageRequestImageFromGeneratorIfFileWasNotUpdated) +TEST_F(AsynchronousImageFactory, DontRequestImageRequestImageFromCollectorIfFileWasUpdatedRecently) { - ON_CALL(mockStorage, fetchHasImage(Eq("/path/to/Component.qml"))).WillByDefault([&](auto) { + ON_CALL(storageMock, fetchModifiedImageTime(Eq("/path/to/Component.qml"))).WillByDefault([&](auto) { notification.notify(); - return true; + return Sqlite::TimeStamp{124}; }); - ON_CALL(mockStorage, fetchModifiedImageTime(Eq("/path/to/Component.qml"))) - .WillByDefault(Return(Sqlite::TimeStamp{124})); - ON_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml"))) - .WillByDefault(Return(Sqlite::TimeStamp{124})); + ON_CALL(timeStampProviderMock, timeStamp(Eq("/path/to/Component.qml"))) + .WillByDefault(Return(Sqlite::TimeStamp{125})); + ON_CALL(timeStampProviderMock, timeStamp(Eq("/path/to/Component.qml"))) + .WillByDefault(Return(Sqlite::TimeStamp{1})); - EXPECT_CALL(mockGenerator, generateImage(_, _, _, _, _, _)).Times(0); + EXPECT_CALL(collectorMock, start(_, _, _, _, _)).Times(0); factory.generate("/path/to/Component.qml"); notification.wait(); } -TEST_F(AsynchronousImageFactory, - RequestImageRequestImageFromGeneratorIfFileWasNotUpdatedButTheImageIsNull) +TEST_F(AsynchronousImageFactory, RequestImageRequestImageFromCollectorIfFileWasNotUpdatedRecently) { - ON_CALL(mockStorage, fetchHasImage(Eq("/path/to/Component.qml"))).WillByDefault(Return(false)); - ON_CALL(mockStorage, fetchModifiedImageTime(Eq("/path/to/Component.qml"))) - .WillByDefault(Return(Sqlite::TimeStamp{124})); - ON_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml"))) - .WillByDefault(Return(Sqlite::TimeStamp{124})); + ON_CALL(storageMock, fetchModifiedImageTime(Eq("/path/to/Component.qml"))) + .WillByDefault(Return(Sqlite::TimeStamp{123})); + ON_CALL(timeStampProviderMock, timeStamp(Eq("/path/to/Component.qml"))) + .WillByDefault(Return(Sqlite::TimeStamp{125})); + ON_CALL(timeStampProviderMock, pause()).WillByDefault(Return(Sqlite::TimeStamp{1})); - EXPECT_CALL(mockGenerator, - generateImage(Eq("/path/to/Component.qml"), - IsEmpty(), - Eq(Sqlite::TimeStamp{124}), - _, - _, - VariantWith(Utils::monostate{}))) - .WillRepeatedly([&](auto, auto, auto, auto, auto, auto) { notification.notify(); }); + EXPECT_CALL(collectorMock, start(_, _, _, _, _)).WillOnce([&](auto, auto, auto, auto, auto) { + notification.notify(); + }); factory.generate("/path/to/Component.qml"); notification.wait(); @@ -152,39 +144,57 @@ TEST_F(AsynchronousImageFactory, TEST_F(AsynchronousImageFactory, CleanRemovesEntries) { - EXPECT_CALL(mockGenerator, generateImage(Eq("/path/to/Component1.qml"), _, _, _, _, _)) - .WillRepeatedly([&](auto, auto, auto, auto, auto, auto) { waitInThread.wait(); }); + EXPECT_CALL(collectorMock, start(Eq("/path/to/Component1.qml"), _, _, _, _)) + .WillRepeatedly([&](auto, auto, auto, auto, auto) { waitInThread.wait(); }); factory.generate("/path/to/Component1.qml"); - EXPECT_CALL(mockGenerator, generateImage(Eq("/path/to/Component3.qml"), _, _, _, _, _)).Times(0); + EXPECT_CALL(collectorMock, start(Eq("/path/to/Component3.qml"), _, _, _, _)).Times(0); factory.generate("/path/to/Component3.qml"); factory.clean(); waitInThread.notify(); } -TEST_F(AsynchronousImageFactory, CleanCallsGeneratorClean) -{ - EXPECT_CALL(mockGenerator, clean()).Times(AtLeast(1)); - - factory.clean(); -} - TEST_F(AsynchronousImageFactory, AfterCleanNewJobsWorks) { factory.clean(); - EXPECT_CALL(mockGenerator, - generateImage(Eq("/path/to/Component.qml"), - IsEmpty(), - Eq(Sqlite::TimeStamp{123}), - _, - _, - VariantWith(Utils::monostate{}))) - .WillRepeatedly([&](auto, auto, auto, auto, auto, auto) { notification.notify(); }); + EXPECT_CALL(collectorMock, + start(Eq("/path/to/Component.qml"), + IsEmpty(), + VariantWith(Utils::monostate{}), + _, + _)) + .WillRepeatedly([&](auto, auto, auto, auto, auto) { notification.notify(); }); factory.generate("/path/to/Component.qml"); notification.wait(); } +TEST_F(AsynchronousImageFactory, CaptureImageCallbackStoresImage) +{ + ON_CALL(storageMock, fetchModifiedImageTime(Eq("/path/to/Component.qml"))) + .WillByDefault(Return(Sqlite::TimeStamp{123})); + ON_CALL(timeStampProviderMock, timeStamp(Eq("/path/to/Component.qml"))) + .WillByDefault(Return(Sqlite::TimeStamp{125})); + ON_CALL(timeStampProviderMock, pause()).WillByDefault(Return(Sqlite::TimeStamp{1})); + ON_CALL(collectorMock, + start(Eq("/path/to/Component.qml"), + Eq("id"), + VariantWith(Utils::monostate{}), + _, + _)) + .WillByDefault([&](auto, auto, auto, auto capture, auto) { capture(image1, smallImage1); }); + + EXPECT_CALL(storageMock, + storeImage(Eq("/path/to/Component.qml+id"), + Sqlite::TimeStamp{125}, + Eq(image1), + Eq(smallImage1))) + .WillOnce([&](auto, auto, auto, auto) { notification.notify(); }); + + factory.generate("/path/to/Component.qml", "id"); + notification.wait(); +} + } // namespace diff --git a/tests/unit/unittest/mocktimestampprovider.h b/tests/unit/unittest/mocktimestampprovider.h index 0adad4c0305..39d58790f22 100644 --- a/tests/unit/unittest/mocktimestampprovider.h +++ b/tests/unit/unittest/mocktimestampprovider.h @@ -33,4 +33,5 @@ class MockTimeStampProvider : public QmlDesigner::TimeStampProviderInterface { public: MOCK_METHOD(Sqlite::TimeStamp, timeStamp, (Utils::SmallStringView name), (const, override)); + MOCK_METHOD(Sqlite::TimeStamp, pause, (), (const, override)); };